msvcrt: Pass pthreadlocinfo to update_threadlocinfo_category helper function.
[wine.git] / dlls / winejoystick.drv / joystick_osx.c
blobda45450864689cb23316ec7bde9e41754a00ea94
1 /*
2 * WinMM joystick driver OS X implementation
4 * Copyright 1997 Andreas Mohr
5 * Copyright 1998 Marcus Meissner
6 * Copyright 1998,1999 Lionel Ulmer
7 * Copyright 2000 Wolfgang Schwotzer
8 * Copyright 2000-2001 TransGaming Technologies Inc.
9 * Copyright 2002 David Hagood
10 * Copyright 2009 CodeWeavers, Aric Stewart
11 * Copyright 2015 Ken Thomases for CodeWeavers Inc.
13 * This library is free software; you can redistribute it and/or
14 * modify it under the terms of the GNU Lesser General Public
15 * License as published by the Free Software Foundation; either
16 * version 2.1 of the License, or (at your option) any later version.
18 * This library is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
21 * Lesser General Public License for more details.
23 * You should have received a copy of the GNU Lesser General Public
24 * License along with this library; if not, write to the Free Software
25 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
28 #include "config.h"
30 #if defined(HAVE_IOKIT_HID_IOHIDLIB_H)
32 #define DWORD UInt32
33 #define LPDWORD UInt32*
34 #define LONG SInt32
35 #define LPLONG SInt32*
36 #define E_PENDING __carbon_E_PENDING
37 #define ULONG __carbon_ULONG
38 #define E_INVALIDARG __carbon_E_INVALIDARG
39 #define E_OUTOFMEMORY __carbon_E_OUTOFMEMORY
40 #define E_HANDLE __carbon_E_HANDLE
41 #define E_ACCESSDENIED __carbon_E_ACCESSDENIED
42 #define E_UNEXPECTED __carbon_E_UNEXPECTED
43 #define E_FAIL __carbon_E_FAIL
44 #define E_ABORT __carbon_E_ABORT
45 #define E_POINTER __carbon_E_POINTER
46 #define E_NOINTERFACE __carbon_E_NOINTERFACE
47 #define E_NOTIMPL __carbon_E_NOTIMPL
48 #define S_FALSE __carbon_S_FALSE
49 #define S_OK __carbon_S_OK
50 #define HRESULT_FACILITY __carbon_HRESULT_FACILITY
51 #define IS_ERROR __carbon_IS_ERROR
52 #define FAILED __carbon_FAILED
53 #define SUCCEEDED __carbon_SUCCEEDED
54 #define MAKE_HRESULT __carbon_MAKE_HRESULT
55 #define HRESULT __carbon_HRESULT
56 #define STDMETHODCALLTYPE __carbon_STDMETHODCALLTYPE
57 #include <IOKit/IOKitLib.h>
58 #include <IOKit/hid/IOHIDLib.h>
59 #undef ULONG
60 #undef E_INVALIDARG
61 #undef E_OUTOFMEMORY
62 #undef E_HANDLE
63 #undef E_ACCESSDENIED
64 #undef E_UNEXPECTED
65 #undef E_FAIL
66 #undef E_ABORT
67 #undef E_POINTER
68 #undef E_NOINTERFACE
69 #undef E_NOTIMPL
70 #undef S_FALSE
71 #undef S_OK
72 #undef HRESULT_FACILITY
73 #undef IS_ERROR
74 #undef FAILED
75 #undef SUCCEEDED
76 #undef MAKE_HRESULT
77 #undef HRESULT
78 #undef STDMETHODCALLTYPE
79 #undef DWORD
80 #undef LPDWORD
81 #undef LONG
82 #undef LPLONG
83 #undef E_PENDING
85 #include "joystick.h"
87 #include "wine/debug.h"
90 WINE_DEFAULT_DEBUG_CHANNEL(joystick);
93 #define MAXJOYSTICK (JOYSTICKID2 + 30)
96 enum {
97 AXIS_X,
98 AXIS_Y,
99 AXIS_Z,
100 AXIS_RX,
101 AXIS_RY,
102 AXIS_RZ,
103 NUM_AXES
106 struct axis {
107 IOHIDElementRef element;
108 CFIndex min_value, max_value;
111 typedef struct {
112 BOOL in_use;
113 IOHIDElementRef element;
114 struct axis axes[NUM_AXES];
115 CFMutableArrayRef buttons;
116 IOHIDElementRef hatswitch;
117 } joystick_t;
120 static joystick_t joysticks[MAXJOYSTICK];
121 static CFMutableArrayRef device_main_elements = NULL;
124 static const char* debugstr_cf(CFTypeRef t)
126 CFStringRef s;
127 const char* ret;
129 if (!t) return "(null)";
131 if (CFGetTypeID(t) == CFStringGetTypeID())
132 s = t;
133 else
134 s = CFCopyDescription(t);
135 ret = CFStringGetCStringPtr(s, kCFStringEncodingUTF8);
136 if (ret) ret = debugstr_a(ret);
137 if (!ret)
139 const UniChar* u = CFStringGetCharactersPtr(s);
140 if (u)
141 ret = debugstr_wn((const WCHAR*)u, CFStringGetLength(s));
143 if (!ret)
145 UniChar buf[200];
146 int len = min(CFStringGetLength(s), sizeof(buf)/sizeof(buf[0]));
147 CFStringGetCharacters(s, CFRangeMake(0, len), buf);
148 ret = debugstr_wn(buf, len);
150 if (s != t) CFRelease(s);
151 return ret;
154 static const char* debugstr_device(IOHIDDeviceRef device)
156 return wine_dbg_sprintf("<IOHIDDevice %p product %s>", device,
157 debugstr_cf(IOHIDDeviceGetProperty(device, CFSTR(kIOHIDProductKey))));
160 static const char* debugstr_element(IOHIDElementRef element)
162 return wine_dbg_sprintf("<IOHIDElement %p type %d usage %u/%u device %p>", element,
163 IOHIDElementGetType(element), IOHIDElementGetUsagePage(element),
164 IOHIDElementGetUsage(element), IOHIDElementGetDevice(element));
168 static int axis_for_usage(int usage)
170 switch (usage)
172 case kHIDUsage_GD_X: return AXIS_X;
173 case kHIDUsage_GD_Y: return AXIS_Y;
174 case kHIDUsage_GD_Z: return AXIS_Z;
175 case kHIDUsage_GD_Rx: return AXIS_RX;
176 case kHIDUsage_GD_Ry: return AXIS_RY;
177 case kHIDUsage_GD_Rz: return AXIS_RZ;
180 return -1;
184 /**************************************************************************
185 * joystick_from_id
187 static joystick_t* joystick_from_id(DWORD_PTR device_id)
189 int index;
191 if ((device_id - (DWORD_PTR)joysticks) % sizeof(joysticks[0]) != 0)
192 return NULL;
193 index = (device_id - (DWORD_PTR)joysticks) / sizeof(joysticks[0]);
194 if (index < 0 || index >= MAXJOYSTICK || !((joystick_t*)device_id)->in_use)
195 return NULL;
197 return (joystick_t*)device_id;
200 /**************************************************************************
201 * create_osx_device_match
203 static CFDictionaryRef create_osx_device_match(int usage)
205 CFDictionaryRef result = NULL;
206 int number;
207 CFStringRef keys[] = { CFSTR(kIOHIDDeviceUsagePageKey), CFSTR(kIOHIDDeviceUsageKey) };
208 CFNumberRef values[2];
209 int i;
211 TRACE("usage %d\n", usage);
213 number = kHIDPage_GenericDesktop;
214 values[0] = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &number);
215 values[1] = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &usage);
217 if (values[0] && values[1])
219 result = CFDictionaryCreate(NULL, (const void**)keys, (const void**)values, sizeof(values) / sizeof(values[0]),
220 &kCFCopyStringDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
222 if (!result)
223 ERR("CFDictionaryCreate failed.\n");
225 else
226 ERR("CFNumberCreate failed.\n");
228 for (i = 0; i < sizeof(values) / sizeof(values[0]); i++)
229 if (values[i]) CFRelease(values[i]);
231 return result;
234 /**************************************************************************
235 * find_top_level
237 static CFIndex find_top_level(IOHIDDeviceRef hid_device, CFMutableArrayRef main_elements)
239 CFArrayRef elements;
240 CFIndex total = 0;
242 TRACE("hid_device %s\n", debugstr_device(hid_device));
244 if (!hid_device)
245 return 0;
247 elements = IOHIDDeviceCopyMatchingElements(hid_device, NULL, 0);
249 if (elements)
251 CFIndex i, count = CFArrayGetCount(elements);
252 for (i = 0; i < count; i++)
254 IOHIDElementRef element = (IOHIDElementRef)CFArrayGetValueAtIndex(elements, i);
255 int type = IOHIDElementGetType(element);
257 TRACE("element %s\n", debugstr_element(element));
259 /* Check for top-level gaming device collections */
260 if (type == kIOHIDElementTypeCollection && IOHIDElementGetParent(element) == 0)
262 int usage_page = IOHIDElementGetUsagePage(element);
263 int usage = IOHIDElementGetUsage(element);
265 if (usage_page == kHIDPage_GenericDesktop &&
266 (usage == kHIDUsage_GD_Joystick || usage == kHIDUsage_GD_GamePad))
268 CFArrayAppendValue(main_elements, element);
269 total++;
273 CFRelease(elements);
276 TRACE("-> total %d\n", (int)total);
277 return total;
280 /**************************************************************************
281 * find_osx_devices
283 static int find_osx_devices(void)
285 IOHIDManagerRef hid_manager;
286 int usages[] = { kHIDUsage_GD_Joystick, kHIDUsage_GD_GamePad };
287 int i;
288 CFDictionaryRef matching_dicts[sizeof(usages) / sizeof(usages[0])];
289 CFArrayRef matching;
290 CFSetRef devset;
292 TRACE("()\n");
294 hid_manager = IOHIDManagerCreate(kCFAllocatorDefault, 0L);
295 if (IOHIDManagerOpen(hid_manager, 0) != kIOReturnSuccess)
297 ERR("Couldn't open IOHIDManager.\n");
298 CFRelease(hid_manager);
299 return 0;
302 for (i = 0; i < sizeof(matching_dicts) / sizeof(matching_dicts[0]); i++)
304 matching_dicts[i] = create_osx_device_match(usages[i]);
305 if (!matching_dicts[i])
307 while (i > 0)
308 CFRelease(matching_dicts[--i]);
309 goto fail;
313 matching = CFArrayCreate(NULL, (const void**)matching_dicts, sizeof(matching_dicts) / sizeof(matching_dicts[0]),
314 &kCFTypeArrayCallBacks);
316 for (i = 0; i < sizeof(matching_dicts) / sizeof(matching_dicts[0]); i++)
317 CFRelease(matching_dicts[i]);
319 IOHIDManagerSetDeviceMatchingMultiple(hid_manager, matching);
320 CFRelease(matching);
321 devset = IOHIDManagerCopyDevices(hid_manager);
322 if (devset)
324 CFIndex num_devices, num_main_elements;
325 const void** refs;
326 CFArrayRef devices;
328 num_devices = CFSetGetCount(devset);
329 refs = HeapAlloc(GetProcessHeap(), 0, num_devices * sizeof(*refs));
330 if (!refs)
332 CFRelease(devset);
333 goto fail;
336 CFSetGetValues(devset, refs);
337 devices = CFArrayCreate(NULL, refs, num_devices, &kCFTypeArrayCallBacks);
338 HeapFree(GetProcessHeap(), 0, refs);
339 CFRelease(devset);
340 if (!devices)
341 goto fail;
343 device_main_elements = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
344 if (!device_main_elements)
346 CFRelease(devices);
347 goto fail;
350 num_main_elements = 0;
351 for (i = 0; i < num_devices; i++)
353 IOHIDDeviceRef hid_device = (IOHIDDeviceRef)CFArrayGetValueAtIndex(devices, i);
354 TRACE("hid_device %s\n", debugstr_device(hid_device));
355 num_main_elements += find_top_level(hid_device, device_main_elements);
358 CFRelease(devices);
360 TRACE("found %i device(s), %i collection(s)\n",(int)num_devices,(int)num_main_elements);
361 return (int)num_main_elements;
364 fail:
365 IOHIDManagerClose(hid_manager, 0);
366 CFRelease(hid_manager);
367 return 0;
370 /**************************************************************************
371 * collect_joystick_elements
373 static void collect_joystick_elements(joystick_t* joystick, IOHIDElementRef collection)
375 CFIndex i, count;
376 CFArrayRef children = IOHIDElementGetChildren(collection);
378 TRACE("collection %s\n", debugstr_element(collection));
380 count = CFArrayGetCount(children);
381 for (i = 0; i < count; i++)
383 IOHIDElementRef child;
384 int type;
386 child = (IOHIDElementRef)CFArrayGetValueAtIndex(children, i);
387 TRACE("child %s\n", debugstr_element(child));
388 type = IOHIDElementGetType(child);
389 switch (type)
391 case kIOHIDElementTypeCollection:
392 collect_joystick_elements(joystick, child);
393 break;
394 case kIOHIDElementTypeInput_Button:
396 int usage_page = IOHIDElementGetUsagePage(child);
398 TRACE("kIOHIDElementTypeInput_Button usage_page %d\n", usage_page);
400 /* avoid strange elements found on the 360 controller */
401 if (usage_page == kHIDPage_Button)
402 CFArrayAppendValue(joystick->buttons, child);
403 break;
405 case kIOHIDElementTypeInput_Axis:
407 TRACE("kIOHIDElementTypeInput_Axis; ignoring\n");
408 break;
410 case kIOHIDElementTypeInput_Misc:
412 uint32_t usage = IOHIDElementGetUsage( child );
413 switch(usage)
415 case kHIDUsage_GD_Hatswitch:
417 TRACE("kIOHIDElementTypeInput_Misc / kHIDUsage_GD_Hatswitch\n");
418 if (joystick->hatswitch)
419 TRACE(" ignoring additional hatswitch\n");
420 else
421 joystick->hatswitch = (IOHIDElementRef)CFRetain(child);
422 break;
424 case kHIDUsage_GD_X:
425 case kHIDUsage_GD_Y:
426 case kHIDUsage_GD_Z:
427 case kHIDUsage_GD_Rx:
428 case kHIDUsage_GD_Ry:
429 case kHIDUsage_GD_Rz:
431 int axis = axis_for_usage(usage);
432 TRACE("kIOHIDElementTypeInput_Misc / kHIDUsage_GD_<axis> (%d) axis %d\n", usage, axis);
433 if (axis < 0 || joystick->axes[axis].element)
434 TRACE(" ignoring\n");
435 else
437 joystick->axes[axis].element = (IOHIDElementRef)CFRetain(child);
438 joystick->axes[axis].min_value = IOHIDElementGetLogicalMin(child);
439 joystick->axes[axis].max_value = IOHIDElementGetLogicalMax(child);
441 break;
443 case kHIDUsage_GD_Slider:
444 TRACE("kIOHIDElementTypeInput_Misc / kHIDUsage_GD_Slider; ignoring\n");
445 break;
446 default:
447 FIXME("kIOHIDElementTypeInput_Misc / Unhandled usage %d\n", usage);
448 break;
450 break;
452 default:
453 FIXME("Unhandled type %i\n",type);
454 break;
459 /**************************************************************************
460 * button_usage_comparator
462 static CFComparisonResult button_usage_comparator(const void *val1, const void *val2, void *context)
464 IOHIDElementRef element1 = (IOHIDElementRef)val1, element2 = (IOHIDElementRef)val2;
465 int usage1 = IOHIDElementGetUsage(element1), usage2 = IOHIDElementGetUsage(element2);
467 if (usage1 < usage2)
468 return kCFCompareLessThan;
469 if (usage1 > usage2)
470 return kCFCompareGreaterThan;
471 return kCFCompareEqualTo;
474 /**************************************************************************
475 * driver_open
477 LRESULT driver_open(LPSTR str, DWORD index)
479 if (index >= MAXJOYSTICK || joysticks[index].in_use)
480 return 0;
482 joysticks[index].in_use = TRUE;
483 return (LRESULT)&joysticks[index];
486 /**************************************************************************
487 * driver_close
489 LRESULT driver_close(DWORD_PTR device_id)
491 joystick_t* joystick = joystick_from_id(device_id);
492 int i;
494 if (joystick == NULL)
495 return 0;
497 CFRelease(joystick->element);
498 for (i = 0; i < NUM_AXES; i++)
500 if (joystick->axes[i].element)
501 CFRelease(joystick->axes[i].element);
503 if (joystick->buttons)
504 CFRelease(joystick->buttons);
505 if (joystick->hatswitch)
506 CFRelease(joystick->hatswitch);
508 memset(joystick, 0, sizeof(*joystick));
509 return 1;
512 /**************************************************************************
513 * open_joystick
515 static BOOL open_joystick(joystick_t* joystick)
517 CFIndex index;
518 CFRange range;
520 if (joystick->element)
521 return TRUE;
523 if (!device_main_elements)
525 find_osx_devices();
526 if (!device_main_elements)
527 return FALSE;
530 index = joystick - joysticks;
531 if (index >= CFArrayGetCount(device_main_elements))
532 return FALSE;
534 joystick->element = (IOHIDElementRef)CFArrayGetValueAtIndex(device_main_elements, index);
535 joystick->buttons = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
536 collect_joystick_elements(joystick, joystick->element);
538 /* Sort buttons into correct order */
539 range.location = 0;
540 range.length = CFArrayGetCount(joystick->buttons);
541 CFArraySortValues(joystick->buttons, range, button_usage_comparator, NULL);
542 if (range.length > 32)
544 /* Delete any buttons beyond the first 32 */
545 range.location = 32;
546 range.length -= 32;
547 CFArrayReplaceValues(joystick->buttons, range, NULL, 0);
550 return TRUE;
554 /**************************************************************************
555 * driver_joyGetDevCaps
557 LRESULT driver_joyGetDevCaps(DWORD_PTR device_id, JOYCAPSW* caps, DWORD size)
559 joystick_t* joystick;
560 IOHIDDeviceRef device;
562 if ((joystick = joystick_from_id(device_id)) == NULL)
563 return MMSYSERR_NODRIVER;
565 if (!open_joystick(joystick))
566 return JOYERR_PARMS;
568 caps->szPname[0] = 0;
570 device = IOHIDElementGetDevice(joystick->element);
571 if (device)
573 CFStringRef product_name = IOHIDDeviceGetProperty(device, CFSTR(kIOHIDProductKey));
574 if (product_name)
576 CFRange range;
578 range.location = 0;
579 range.length = min(MAXPNAMELEN - 1, CFStringGetLength(product_name));
580 CFStringGetCharacters(product_name, range, (UniChar*)caps->szPname);
581 caps->szPname[range.length] = 0;
585 caps->wMid = MM_MICROSOFT;
586 caps->wPid = MM_PC_JOYSTICK;
587 caps->wXmin = 0;
588 caps->wXmax = 0xFFFF;
589 caps->wYmin = 0;
590 caps->wYmax = 0xFFFF;
591 caps->wZmin = 0;
592 caps->wZmax = joystick->axes[AXIS_Z].element ? 0xFFFF : 0;
593 caps->wNumButtons = CFArrayGetCount(joystick->buttons);
594 if (size == sizeof(JOYCAPSW))
596 int i;
598 /* complete 95 structure */
599 caps->wRmin = 0;
600 caps->wRmax = 0xFFFF;
601 caps->wUmin = 0;
602 caps->wUmax = 0xFFFF;
603 caps->wVmin = 0;
604 caps->wVmax = 0xFFFF;
605 caps->wMaxAxes = 6; /* same as MS Joystick Driver */
606 caps->wNumAxes = 0;
607 caps->wMaxButtons = 32; /* same as MS Joystick Driver */
608 caps->szRegKey[0] = 0;
609 caps->szOEMVxD[0] = 0;
610 caps->wCaps = 0;
612 for (i = 0; i < NUM_AXES; i++)
614 if (joystick->axes[i].element)
616 caps->wNumAxes++;
617 switch (i)
619 case AXIS_Z: caps->wCaps |= JOYCAPS_HASZ; break;
620 case AXIS_RX: caps->wCaps |= JOYCAPS_HASU; break;
621 case AXIS_RY: caps->wCaps |= JOYCAPS_HASV; break;
622 case AXIS_RZ: caps->wCaps |= JOYCAPS_HASR; break;
627 if (joystick->hatswitch)
628 caps->wCaps |= JOYCAPS_HASPOV | JOYCAPS_POV4DIR;
631 TRACE("name %s buttons %u axes %d caps 0x%08x\n", debugstr_w(caps->szPname), caps->wNumButtons, caps->wNumAxes, caps->wCaps);
633 return JOYERR_NOERROR;
636 /**************************************************************************
637 * driver_joyGetPosEx
639 LRESULT driver_joyGetPosEx(DWORD_PTR device_id, JOYINFOEX* info)
641 static const struct {
642 DWORD flag;
643 off_t offset;
644 } axis_map[NUM_AXES] = {
645 { JOY_RETURNX, FIELD_OFFSET(JOYINFOEX, dwXpos) },
646 { JOY_RETURNY, FIELD_OFFSET(JOYINFOEX, dwYpos) },
647 { JOY_RETURNZ, FIELD_OFFSET(JOYINFOEX, dwZpos) },
648 { JOY_RETURNU, FIELD_OFFSET(JOYINFOEX, dwUpos) },
649 { JOY_RETURNV, FIELD_OFFSET(JOYINFOEX, dwVpos) },
650 { JOY_RETURNR, FIELD_OFFSET(JOYINFOEX, dwRpos) },
653 joystick_t* joystick;
654 IOHIDDeviceRef device;
655 CFIndex i, count;
656 IOHIDValueRef valueRef;
657 long value;
659 if ((joystick = joystick_from_id(device_id)) == NULL)
660 return MMSYSERR_NODRIVER;
662 if (!open_joystick(joystick))
663 return JOYERR_PARMS;
665 device = IOHIDElementGetDevice(joystick->element);
667 if (info->dwFlags & JOY_RETURNBUTTONS)
669 info->dwButtons = 0;
670 info->dwButtonNumber = 0;
672 count = CFArrayGetCount(joystick->buttons);
673 for (i = 0; i < count; i++)
675 IOHIDElementRef button = (IOHIDElementRef)CFArrayGetValueAtIndex(joystick->buttons, i);
676 IOHIDDeviceGetValue(device, button, &valueRef);
677 value = IOHIDValueGetIntegerValue(valueRef);
678 if (value)
680 info->dwButtons |= 1 << i;
681 info->dwButtonNumber++;
686 for (i = 0; i < NUM_AXES; i++)
688 if (info->dwFlags & axis_map[i].flag)
690 DWORD* field = (DWORD*)((char*)info + axis_map[i].offset);
691 if (joystick->axes[i].element)
693 IOHIDDeviceGetValue(device, joystick->axes[i].element, &valueRef);
694 value = IOHIDValueGetIntegerValue(valueRef) - joystick->axes[i].min_value;
695 *field = MulDiv(value, 0xFFFF, joystick->axes[i].max_value - joystick->axes[i].min_value);
697 else
699 *field = 0;
700 info->dwFlags &= ~axis_map[i].flag;
705 if (info->dwFlags & JOY_RETURNPOV)
707 if (joystick->hatswitch)
709 IOHIDDeviceGetValue(device, joystick->hatswitch, &valueRef);
710 value = IOHIDValueGetIntegerValue(valueRef);
711 if (value >= 8)
712 info->dwPOV = JOY_POVCENTERED;
713 else
714 info->dwPOV = value * 4500;
716 else
718 info->dwPOV = 0;
719 info->dwFlags &= ~JOY_RETURNPOV;
723 TRACE("x: %d, y: %d, z: %d, r: %d, u: %d, v: %d, buttons: 0x%04x, pov %d, flags: 0x%04x\n",
724 info->dwXpos, info->dwYpos, info->dwZpos, info->dwRpos, info->dwUpos, info->dwVpos, info->dwButtons, info->dwPOV, info->dwFlags);
726 return JOYERR_NOERROR;
729 /**************************************************************************
730 * driver_joyGetPos
732 LRESULT driver_joyGetPos(DWORD_PTR device_id, JOYINFO* info)
734 JOYINFOEX ji;
735 LONG ret;
737 memset(&ji, 0, sizeof(ji));
739 ji.dwSize = sizeof(ji);
740 ji.dwFlags = JOY_RETURNX | JOY_RETURNY | JOY_RETURNZ | JOY_RETURNBUTTONS;
741 ret = driver_joyGetPosEx(device_id, &ji);
742 if (ret == JOYERR_NOERROR)
744 info->wXpos = ji.dwXpos;
745 info->wYpos = ji.dwYpos;
746 info->wZpos = ji.dwZpos;
747 info->wButtons = ji.dwButtons;
750 return ret;
753 #endif /* HAVE_IOKIT_HID_IOHIDLIB_H */