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.
12 * Copyright 2016 David Lawrie
14 * This library is free software; you can redistribute it and/or
15 * modify it under the terms of the GNU Lesser General Public
16 * License as published by the Free Software Foundation; either
17 * version 2.1 of the License, or (at your option) any later version.
19 * This library is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
22 * Lesser General Public License for more details.
24 * You should have received a copy of the GNU Lesser General Public
25 * License along with this library; if not, write to the Free Software
26 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
31 #if defined(HAVE_IOKIT_HID_IOHIDLIB_H)
34 #define LPDWORD UInt32*
36 #define LPLONG SInt32*
37 #define E_PENDING __carbon_E_PENDING
38 #define ULONG __carbon_ULONG
39 #define E_INVALIDARG __carbon_E_INVALIDARG
40 #define E_OUTOFMEMORY __carbon_E_OUTOFMEMORY
41 #define E_HANDLE __carbon_E_HANDLE
42 #define E_ACCESSDENIED __carbon_E_ACCESSDENIED
43 #define E_UNEXPECTED __carbon_E_UNEXPECTED
44 #define E_FAIL __carbon_E_FAIL
45 #define E_ABORT __carbon_E_ABORT
46 #define E_POINTER __carbon_E_POINTER
47 #define E_NOINTERFACE __carbon_E_NOINTERFACE
48 #define E_NOTIMPL __carbon_E_NOTIMPL
49 #define S_FALSE __carbon_S_FALSE
50 #define S_OK __carbon_S_OK
51 #define HRESULT_FACILITY __carbon_HRESULT_FACILITY
52 #define IS_ERROR __carbon_IS_ERROR
53 #define FAILED __carbon_FAILED
54 #define SUCCEEDED __carbon_SUCCEEDED
55 #define MAKE_HRESULT __carbon_MAKE_HRESULT
56 #define HRESULT __carbon_HRESULT
57 #define STDMETHODCALLTYPE __carbon_STDMETHODCALLTYPE
58 #include <IOKit/IOKitLib.h>
59 #include <IOKit/hid/IOHIDLib.h>
73 #undef HRESULT_FACILITY
79 #undef STDMETHODCALLTYPE
88 #include "wine/debug.h"
91 WINE_DEFAULT_DEBUG_CHANNEL(joystick
);
94 #define MAXJOYSTICK (JOYSTICKID2 + 30)
100 AXIS_Z
, /* Winmm Z */
101 AXIS_RX
, /* Winmm V */
102 AXIS_RY
, /* Winmm U */
103 AXIS_RZ
, /* Winmm R */
108 IOHIDElementRef element
;
109 CFIndex min_value
, max_value
;
114 IOHIDElementRef element
;
115 struct axis axes
[NUM_AXES
];
116 CFMutableArrayRef buttons
;
117 IOHIDElementRef hatswitch
;
121 static joystick_t joysticks
[MAXJOYSTICK
];
122 static CFMutableArrayRef device_main_elements
= NULL
;
124 static long get_device_property_long(IOHIDDeviceRef device
, CFStringRef key
)
131 assert(IOHIDDeviceGetTypeID() == CFGetTypeID(device
));
133 ref
= IOHIDDeviceGetProperty(device
, key
);
135 if (ref
&& CFNumberGetTypeID() == CFGetTypeID(ref
))
136 CFNumberGetValue((CFNumberRef
)ref
, kCFNumberLongType
, &result
);
142 static CFStringRef
copy_device_name(IOHIDDeviceRef device
)
150 assert(IOHIDDeviceGetTypeID() == CFGetTypeID(device
));
152 ref_name
= IOHIDDeviceGetProperty(device
, CFSTR(kIOHIDProductKey
));
154 if (ref_name
&& CFStringGetTypeID() == CFGetTypeID(ref_name
))
155 name
= CFStringCreateCopy(kCFAllocatorDefault
, ref_name
);
160 vendID
= get_device_property_long(device
, CFSTR(kIOHIDVendorIDKey
));
161 prodID
= get_device_property_long(device
, CFSTR(kIOHIDProductIDKey
));
162 name
= CFStringCreateWithFormat(kCFAllocatorDefault
, NULL
, CFSTR("0x%04lx 0x%04lx"), vendID
, prodID
);
167 ERR("NULL device\n");
168 name
= CFStringCreateCopy(kCFAllocatorDefault
, CFSTR(""));
174 static long get_device_location_ID(IOHIDDeviceRef device
)
176 return get_device_property_long(device
, CFSTR(kIOHIDLocationIDKey
));
179 static const char* debugstr_cf(CFTypeRef t
)
184 if (!t
) return "(null)";
186 if (CFGetTypeID(t
) == CFStringGetTypeID())
189 s
= CFCopyDescription(t
);
190 ret
= CFStringGetCStringPtr(s
, kCFStringEncodingUTF8
);
191 if (ret
) ret
= debugstr_a(ret
);
194 const UniChar
* u
= CFStringGetCharactersPtr(s
);
196 ret
= debugstr_wn((const WCHAR
*)u
, CFStringGetLength(s
));
201 int len
= min(CFStringGetLength(s
), sizeof(buf
)/sizeof(buf
[0]));
202 CFStringGetCharacters(s
, CFRangeMake(0, len
), buf
);
203 ret
= debugstr_wn(buf
, len
);
205 if (s
!= t
) CFRelease(s
);
209 static const char* debugstr_device(IOHIDDeviceRef device
)
211 return wine_dbg_sprintf("<IOHIDDevice %p product %s IOHIDLocationID %lu>", device
,
212 debugstr_cf(IOHIDDeviceGetProperty(device
, CFSTR(kIOHIDProductKey
))),
213 get_device_location_ID(device
));
216 static const char* debugstr_element(IOHIDElementRef element
)
218 return wine_dbg_sprintf("<IOHIDElement %p type %d usage %u/%u device %p>", element
,
219 IOHIDElementGetType(element
), IOHIDElementGetUsagePage(element
),
220 IOHIDElementGetUsage(element
), IOHIDElementGetDevice(element
));
223 static int axis_for_usage_GD(int usage
)
227 case kHIDUsage_GD_X
: return AXIS_X
;
228 case kHIDUsage_GD_Y
: return AXIS_Y
;
229 case kHIDUsage_GD_Z
: return AXIS_Z
;
230 case kHIDUsage_GD_Rx
: return AXIS_RX
;
231 case kHIDUsage_GD_Ry
: return AXIS_RY
;
232 case kHIDUsage_GD_Rz
: return AXIS_RZ
;
238 static int axis_for_usage_Sim(int usage
)
242 case kHIDUsage_Sim_Rudder
: return AXIS_RZ
;
243 case kHIDUsage_Sim_Throttle
: return AXIS_Z
;
244 case kHIDUsage_Sim_Steering
: return AXIS_X
;
245 case kHIDUsage_Sim_Accelerator
: return AXIS_Y
;
246 case kHIDUsage_Sim_Brake
: return AXIS_RZ
;
252 /**************************************************************************
255 static joystick_t
* joystick_from_id(DWORD_PTR device_id
)
259 if ((device_id
- (DWORD_PTR
)joysticks
) % sizeof(joysticks
[0]) != 0)
261 index
= (device_id
- (DWORD_PTR
)joysticks
) / sizeof(joysticks
[0]);
262 if (index
< 0 || index
>= MAXJOYSTICK
|| !((joystick_t
*)device_id
)->in_use
)
265 return (joystick_t
*)device_id
;
268 /**************************************************************************
269 * create_osx_device_match
271 static CFDictionaryRef
create_osx_device_match(int usage
)
273 CFDictionaryRef result
= NULL
;
275 CFStringRef keys
[] = { CFSTR(kIOHIDDeviceUsagePageKey
), CFSTR(kIOHIDDeviceUsageKey
) };
276 CFNumberRef values
[2];
279 TRACE("usage %d\n", usage
);
281 number
= kHIDPage_GenericDesktop
;
282 values
[0] = CFNumberCreate(kCFAllocatorDefault
, kCFNumberIntType
, &number
);
283 values
[1] = CFNumberCreate(kCFAllocatorDefault
, kCFNumberIntType
, &usage
);
285 if (values
[0] && values
[1])
287 result
= CFDictionaryCreate(NULL
, (const void**)keys
, (const void**)values
, sizeof(values
) / sizeof(values
[0]),
288 &kCFCopyStringDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
291 ERR("CFDictionaryCreate failed.\n");
294 ERR("CFNumberCreate failed.\n");
296 for (i
= 0; i
< sizeof(values
) / sizeof(values
[0]); i
++)
297 if (values
[i
]) CFRelease(values
[i
]);
302 /**************************************************************************
305 static CFIndex
find_top_level(IOHIDDeviceRef hid_device
, CFMutableArrayRef main_elements
)
310 TRACE("hid_device %s\n", debugstr_device(hid_device
));
315 elements
= IOHIDDeviceCopyMatchingElements(hid_device
, NULL
, 0);
319 CFIndex i
, count
= CFArrayGetCount(elements
);
320 for (i
= 0; i
< count
; i
++)
322 IOHIDElementRef element
= (IOHIDElementRef
)CFArrayGetValueAtIndex(elements
, i
);
323 int type
= IOHIDElementGetType(element
);
325 TRACE("element %s\n", debugstr_element(element
));
327 /* Check for top-level gaming device collections */
328 if (type
== kIOHIDElementTypeCollection
&& IOHIDElementGetParent(element
) == 0)
330 int usage_page
= IOHIDElementGetUsagePage(element
);
331 int usage
= IOHIDElementGetUsage(element
);
333 if (usage_page
== kHIDPage_GenericDesktop
&&
334 (usage
== kHIDUsage_GD_Joystick
|| usage
== kHIDUsage_GD_GamePad
|| usage
== kHIDUsage_GD_MultiAxisController
))
336 CFArrayAppendValue(main_elements
, element
);
344 TRACE("-> total %d\n", (int)total
);
347 /**************************************************************************
348 * device_name_comparator
350 * Virtual joysticks may not have a kIOHIDLocationIDKey and will default to location ID of 0, this orders virtual joysticks by their name
352 static CFComparisonResult
device_name_comparator(IOHIDDeviceRef device1
, IOHIDDeviceRef device2
)
354 CFStringRef name1
= copy_device_name(device1
), name2
= copy_device_name(device2
);
355 CFComparisonResult result
= CFStringCompare(name1
, name2
, (kCFCompareForcedOrdering
| kCFCompareNumerically
));
361 /**************************************************************************
362 * device_location_name_comparator
364 * Helper to sort device array first by location ID, since location IDs are consistent across boots & launches, then by product name
366 static CFComparisonResult
device_location_name_comparator(const void *val1
, const void *val2
, void *context
)
368 IOHIDDeviceRef device1
= (IOHIDDeviceRef
)val1
, device2
= (IOHIDDeviceRef
)val2
;
369 long loc1
= get_device_location_ID(device1
), loc2
= get_device_location_ID(device2
);
372 return kCFCompareLessThan
;
373 else if (loc1
> loc2
)
374 return kCFCompareGreaterThan
;
375 return device_name_comparator(device1
, device2
);
378 /**************************************************************************
381 * Helper to copy the CFSet to a CFArray
383 static void copy_set_to_array(const void *value
, void *context
)
385 CFArrayAppendValue(context
, value
);
388 /**************************************************************************
391 static int find_osx_devices(void)
393 IOHIDManagerRef hid_manager
;
394 int usages
[] = { kHIDUsage_GD_Joystick
, kHIDUsage_GD_GamePad
, kHIDUsage_GD_MultiAxisController
};
396 CFDictionaryRef matching_dicts
[sizeof(usages
) / sizeof(usages
[0])];
402 hid_manager
= IOHIDManagerCreate(kCFAllocatorDefault
, 0L);
403 if (IOHIDManagerOpen(hid_manager
, 0) != kIOReturnSuccess
)
405 ERR("Couldn't open IOHIDManager.\n");
406 CFRelease(hid_manager
);
410 for (i
= 0; i
< sizeof(matching_dicts
) / sizeof(matching_dicts
[0]); i
++)
412 matching_dicts
[i
] = create_osx_device_match(usages
[i
]);
413 if (!matching_dicts
[i
])
416 CFRelease(matching_dicts
[--i
]);
421 matching
= CFArrayCreate(NULL
, (const void**)matching_dicts
, sizeof(matching_dicts
) / sizeof(matching_dicts
[0]),
422 &kCFTypeArrayCallBacks
);
424 for (i
= 0; i
< sizeof(matching_dicts
) / sizeof(matching_dicts
[0]); i
++)
425 CFRelease(matching_dicts
[i
]);
427 IOHIDManagerSetDeviceMatchingMultiple(hid_manager
, matching
);
429 devset
= IOHIDManagerCopyDevices(hid_manager
);
432 CFIndex num_devices
, num_main_elements
;
433 CFMutableArrayRef devices
;
435 num_devices
= CFSetGetCount(devset
);
436 devices
= CFArrayCreateMutable(kCFAllocatorDefault
, num_devices
, &kCFTypeArrayCallBacks
);
437 CFSetApplyFunction(devset
, copy_set_to_array
, (void *)devices
);
438 CFArraySortValues(devices
, CFRangeMake(0, num_devices
), device_location_name_comparator
, NULL
);
444 device_main_elements
= CFArrayCreateMutable(kCFAllocatorDefault
, 0, &kCFTypeArrayCallBacks
);
445 if (!device_main_elements
)
451 num_main_elements
= 0;
452 for (i
= 0; i
< num_devices
; i
++)
454 IOHIDDeviceRef hid_device
= (IOHIDDeviceRef
)CFArrayGetValueAtIndex(devices
, i
);
455 TRACE("hid_device %s\n", debugstr_device(hid_device
));
456 num_main_elements
+= find_top_level(hid_device
, device_main_elements
);
461 TRACE("found %i device(s), %i collection(s)\n",(int)num_devices
,(int)num_main_elements
);
462 return (int)num_main_elements
;
466 IOHIDManagerClose(hid_manager
, 0);
467 CFRelease(hid_manager
);
471 /**************************************************************************
472 * collect_joystick_elements
474 static void collect_joystick_elements(joystick_t
* joystick
, IOHIDElementRef collection
)
477 CFArrayRef children
= IOHIDElementGetChildren(collection
);
479 TRACE("collection %s\n", debugstr_element(collection
));
481 count
= CFArrayGetCount(children
);
482 for (i
= 0; i
< count
; i
++)
484 IOHIDElementRef child
;
488 child
= (IOHIDElementRef
)CFArrayGetValueAtIndex(children
, i
);
489 TRACE("child %s\n", debugstr_element(child
));
490 type
= IOHIDElementGetType(child
);
491 usage_page
= IOHIDElementGetUsagePage(child
);
495 case kIOHIDElementTypeCollection
:
496 collect_joystick_elements(joystick
, child
);
498 case kIOHIDElementTypeInput_Button
:
500 TRACE("kIOHIDElementTypeInput_Button usage_page %d\n", usage_page
);
502 /* avoid strange elements found on the 360 controller */
503 if (usage_page
== kHIDPage_Button
)
504 CFArrayAppendValue(joystick
->buttons
, child
);
507 case kIOHIDElementTypeInput_Axis
:
508 case kIOHIDElementTypeInput_Misc
:
510 uint32_t usage
= IOHIDElementGetUsage( child
);
513 case kHIDPage_GenericDesktop
:
517 case kHIDUsage_GD_Hatswitch
:
519 TRACE("kIOHIDElementTypeInput_Axis/Misc / kHIDUsage_GD_Hatswitch\n");
520 if (joystick
->hatswitch
)
521 TRACE(" ignoring additional hatswitch\n");
523 joystick
->hatswitch
= (IOHIDElementRef
)CFRetain(child
);
529 case kHIDUsage_GD_Rx
:
530 case kHIDUsage_GD_Ry
:
531 case kHIDUsage_GD_Rz
:
533 int axis
= axis_for_usage_GD(usage
);
534 TRACE("kIOHIDElementTypeInput_Axis/Misc / kHIDUsage_GD_<axis> (%d) axis %d\n", usage
, axis
);
535 if (axis
< 0 || joystick
->axes
[axis
].element
)
536 TRACE(" ignoring\n");
539 joystick
->axes
[axis
].element
= (IOHIDElementRef
)CFRetain(child
);
540 joystick
->axes
[axis
].min_value
= IOHIDElementGetLogicalMin(child
);
541 joystick
->axes
[axis
].max_value
= IOHIDElementGetLogicalMax(child
);
545 case kHIDUsage_GD_Slider
:
546 case kHIDUsage_GD_Dial
:
547 case kHIDUsage_GD_Wheel
:
549 /* if one axis is taken, fall to the next until axes are filled */
550 int possible_axes
[3] = {AXIS_Z
,AXIS_RY
,AXIS_RX
};
552 while(axis
< 3 && joystick
->axes
[possible_axes
[axis
]].element
)
555 TRACE("kIOHIDElementTypeInput_Axis/Misc / kHIDUsage_GD_<axis> (%d)\n ignoring\n", usage
);
558 TRACE("kIOHIDElementTypeInput_Axis/Misc / kHIDUsage_GD_<axis> (%d) axis %d\n", usage
, possible_axes
[axis
]);
559 joystick
->axes
[possible_axes
[axis
]].element
= (IOHIDElementRef
)CFRetain(child
);
560 joystick
->axes
[possible_axes
[axis
]].min_value
= IOHIDElementGetLogicalMin(child
);
561 joystick
->axes
[possible_axes
[axis
]].max_value
= IOHIDElementGetLogicalMax(child
);
566 FIXME("kIOHIDElementTypeInput_Axis/Misc / Unhandled GD Page usage %d\n", usage
);
571 case kHIDPage_Simulation
:
575 case kHIDUsage_Sim_Rudder
:
576 case kHIDUsage_Sim_Throttle
:
577 case kHIDUsage_Sim_Steering
:
578 case kHIDUsage_Sim_Accelerator
:
579 case kHIDUsage_Sim_Brake
:
581 int axis
= axis_for_usage_Sim(usage
);
582 TRACE("kIOHIDElementTypeInput_Axis/Misc / kHIDUsage_Sim_<axis> (%d) axis %d\n", usage
, axis
);
583 if (axis
< 0 || joystick
->axes
[axis
].element
)
584 TRACE(" ignoring\n");
587 joystick
->axes
[axis
].element
= (IOHIDElementRef
)CFRetain(child
);
588 joystick
->axes
[axis
].min_value
= IOHIDElementGetLogicalMin(child
);
589 joystick
->axes
[axis
].max_value
= IOHIDElementGetLogicalMax(child
);
594 FIXME("kIOHIDElementTypeInput_Axis/Misc / Unhandled Sim Page usage %d\n", usage
);
600 FIXME("kIOHIDElementTypeInput_Axis/Misc / Unhandled Usage Page %d\n", usage_page
);
605 case kIOHIDElementTypeFeature
:
606 /* Describes input and output elements not intended for consumption by the end user. Ignoring. */
609 FIXME("Unhandled type %i\n",type
);
615 /**************************************************************************
616 * button_usage_comparator
618 static CFComparisonResult
button_usage_comparator(const void *val1
, const void *val2
, void *context
)
620 IOHIDElementRef element1
= (IOHIDElementRef
)val1
, element2
= (IOHIDElementRef
)val2
;
621 int usage1
= IOHIDElementGetUsage(element1
), usage2
= IOHIDElementGetUsage(element2
);
624 return kCFCompareLessThan
;
626 return kCFCompareGreaterThan
;
627 return kCFCompareEqualTo
;
630 /**************************************************************************
633 LRESULT
driver_open(LPSTR str
, DWORD index
)
635 if (index
>= MAXJOYSTICK
|| joysticks
[index
].in_use
)
638 joysticks
[index
].in_use
= TRUE
;
639 return (LRESULT
)&joysticks
[index
];
642 /**************************************************************************
645 LRESULT
driver_close(DWORD_PTR device_id
)
647 joystick_t
* joystick
= joystick_from_id(device_id
);
650 if (joystick
== NULL
)
653 CFRelease(joystick
->element
);
654 for (i
= 0; i
< NUM_AXES
; i
++)
656 if (joystick
->axes
[i
].element
)
657 CFRelease(joystick
->axes
[i
].element
);
659 if (joystick
->buttons
)
660 CFRelease(joystick
->buttons
);
661 if (joystick
->hatswitch
)
662 CFRelease(joystick
->hatswitch
);
664 memset(joystick
, 0, sizeof(*joystick
));
668 /**************************************************************************
671 static BOOL
open_joystick(joystick_t
* joystick
)
676 if (joystick
->element
)
679 if (!device_main_elements
)
682 if (!device_main_elements
)
686 index
= joystick
- joysticks
;
687 if (index
>= CFArrayGetCount(device_main_elements
))
690 joystick
->element
= (IOHIDElementRef
)CFArrayGetValueAtIndex(device_main_elements
, index
);
691 joystick
->buttons
= CFArrayCreateMutable(kCFAllocatorDefault
, 0, &kCFTypeArrayCallBacks
);
692 collect_joystick_elements(joystick
, joystick
->element
);
694 /* Sort buttons into correct order */
696 range
.length
= CFArrayGetCount(joystick
->buttons
);
697 CFArraySortValues(joystick
->buttons
, range
, button_usage_comparator
, NULL
);
698 if (range
.length
> 32)
700 /* Delete any buttons beyond the first 32 */
703 CFArrayReplaceValues(joystick
->buttons
, range
, NULL
, 0);
710 /**************************************************************************
711 * driver_joyGetDevCaps
713 LRESULT
driver_joyGetDevCaps(DWORD_PTR device_id
, JOYCAPSW
* caps
, DWORD size
)
715 joystick_t
* joystick
;
716 IOHIDDeviceRef device
;
718 if ((joystick
= joystick_from_id(device_id
)) == NULL
)
719 return MMSYSERR_NODRIVER
;
721 if (!open_joystick(joystick
))
724 caps
->szPname
[0] = 0;
726 device
= IOHIDElementGetDevice(joystick
->element
);
729 CFStringRef product_name
= IOHIDDeviceGetProperty(device
, CFSTR(kIOHIDProductKey
));
735 range
.length
= min(MAXPNAMELEN
- 1, CFStringGetLength(product_name
));
736 CFStringGetCharacters(product_name
, range
, (UniChar
*)caps
->szPname
);
737 caps
->szPname
[range
.length
] = 0;
741 caps
->wMid
= MM_MICROSOFT
;
742 caps
->wPid
= MM_PC_JOYSTICK
;
744 caps
->wXmax
= 0xFFFF;
746 caps
->wYmax
= 0xFFFF;
748 caps
->wZmax
= joystick
->axes
[AXIS_Z
].element
? 0xFFFF : 0;
749 caps
->wNumButtons
= CFArrayGetCount(joystick
->buttons
);
750 if (size
== sizeof(JOYCAPSW
))
754 /* complete 95 structure */
756 caps
->wRmax
= 0xFFFF;
758 caps
->wUmax
= 0xFFFF;
760 caps
->wVmax
= 0xFFFF;
761 caps
->wMaxAxes
= 6; /* same as MS Joystick Driver */
763 caps
->wMaxButtons
= 32; /* same as MS Joystick Driver */
764 caps
->szRegKey
[0] = 0;
765 caps
->szOEMVxD
[0] = 0;
768 for (i
= 0; i
< NUM_AXES
; i
++)
770 if (joystick
->axes
[i
].element
)
775 case AXIS_Z
: caps
->wCaps
|= JOYCAPS_HASZ
; break;
776 case AXIS_RX
: caps
->wCaps
|= JOYCAPS_HASV
; break;
777 case AXIS_RY
: caps
->wCaps
|= JOYCAPS_HASU
; break;
778 case AXIS_RZ
: caps
->wCaps
|= JOYCAPS_HASR
; break;
783 if (joystick
->hatswitch
)
784 caps
->wCaps
|= JOYCAPS_HASPOV
| JOYCAPS_POV4DIR
;
787 TRACE("name %s buttons %u axes %d caps 0x%08x\n", debugstr_w(caps
->szPname
), caps
->wNumButtons
, caps
->wNumAxes
, caps
->wCaps
);
789 return JOYERR_NOERROR
;
793 * Helper to get the value from an element
795 static LRESULT
driver_getElementValue(IOHIDDeviceRef device
, IOHIDElementRef element
, IOHIDValueRef
*pValueRef
)
798 ret
= IOHIDDeviceGetValue(device
, element
, pValueRef
);
801 case kIOReturnSuccess
:
802 return JOYERR_NOERROR
;
803 case kIOReturnNotAttached
:
804 return JOYERR_UNPLUGGED
;
806 ERR("IOHIDDeviceGetValue returned 0x%x\n",ret
);
807 return JOYERR_NOCANDO
;
811 /**************************************************************************
814 LRESULT
driver_joyGetPosEx(DWORD_PTR device_id
, JOYINFOEX
* info
)
816 static const struct {
819 } axis_map
[NUM_AXES
] = {
820 { JOY_RETURNX
, FIELD_OFFSET(JOYINFOEX
, dwXpos
) },
821 { JOY_RETURNY
, FIELD_OFFSET(JOYINFOEX
, dwYpos
) },
822 { JOY_RETURNZ
, FIELD_OFFSET(JOYINFOEX
, dwZpos
) },
823 { JOY_RETURNV
, FIELD_OFFSET(JOYINFOEX
, dwVpos
) },
824 { JOY_RETURNU
, FIELD_OFFSET(JOYINFOEX
, dwUpos
) },
825 { JOY_RETURNR
, FIELD_OFFSET(JOYINFOEX
, dwRpos
) },
828 joystick_t
* joystick
;
829 IOHIDDeviceRef device
;
831 IOHIDValueRef valueRef
;
835 if ((joystick
= joystick_from_id(device_id
)) == NULL
)
836 return MMSYSERR_NODRIVER
;
838 if (!open_joystick(joystick
))
841 device
= IOHIDElementGetDevice(joystick
->element
);
843 if (info
->dwFlags
& JOY_RETURNBUTTONS
)
846 info
->dwButtonNumber
= 0;
848 count
= CFArrayGetCount(joystick
->buttons
);
849 for (i
= 0; i
< count
; i
++)
851 IOHIDElementRef button
= (IOHIDElementRef
)CFArrayGetValueAtIndex(joystick
->buttons
, i
);
852 rc
= driver_getElementValue(device
, button
, &valueRef
);
853 if (rc
!= JOYERR_NOERROR
)
855 value
= IOHIDValueGetIntegerValue(valueRef
);
858 info
->dwButtons
|= 1 << i
;
859 info
->dwButtonNumber
++;
864 for (i
= 0; i
< NUM_AXES
; i
++)
866 if (info
->dwFlags
& axis_map
[i
].flag
)
868 DWORD
* field
= (DWORD
*)((char*)info
+ axis_map
[i
].offset
);
869 if (joystick
->axes
[i
].element
)
871 rc
= driver_getElementValue(device
, joystick
->axes
[i
].element
, &valueRef
);
872 if (rc
!= JOYERR_NOERROR
)
874 value
= IOHIDValueGetIntegerValue(valueRef
) - joystick
->axes
[i
].min_value
;
875 *field
= MulDiv(value
, 0xFFFF, joystick
->axes
[i
].max_value
- joystick
->axes
[i
].min_value
);
880 info
->dwFlags
&= ~axis_map
[i
].flag
;
885 if (info
->dwFlags
& JOY_RETURNPOV
)
887 if (joystick
->hatswitch
)
889 rc
= driver_getElementValue(device
, joystick
->hatswitch
, &valueRef
);
890 if (rc
!= JOYERR_NOERROR
)
892 value
= IOHIDValueGetIntegerValue(valueRef
);
894 info
->dwPOV
= JOY_POVCENTERED
;
896 info
->dwPOV
= value
* 4500;
900 info
->dwPOV
= JOY_POVCENTERED
;
901 info
->dwFlags
&= ~JOY_RETURNPOV
;
905 TRACE("x: %d, y: %d, z: %d, r: %d, u: %d, v: %d, buttons: 0x%04x, pov %d, flags: 0x%04x\n",
906 info
->dwXpos
, info
->dwYpos
, info
->dwZpos
, info
->dwRpos
, info
->dwUpos
, info
->dwVpos
, info
->dwButtons
, info
->dwPOV
, info
->dwFlags
);
908 return JOYERR_NOERROR
;
911 /**************************************************************************
914 LRESULT
driver_joyGetPos(DWORD_PTR device_id
, JOYINFO
* info
)
919 memset(&ji
, 0, sizeof(ji
));
921 ji
.dwSize
= sizeof(ji
);
922 ji
.dwFlags
= JOY_RETURNX
| JOY_RETURNY
| JOY_RETURNZ
| JOY_RETURNBUTTONS
;
923 ret
= driver_joyGetPosEx(device_id
, &ji
);
924 if (ret
== JOYERR_NOERROR
)
926 info
->wXpos
= ji
.dwXpos
;
927 info
->wYpos
= ji
.dwYpos
;
928 info
->wZpos
= ji
.dwZpos
;
929 info
->wButtons
= ji
.dwButtons
;
935 #endif /* HAVE_IOKIT_HID_IOHIDLIB_H */