crypt32: Test CryptStringToBinary with weird Base64.
[wine.git] / dlls / winejoystick.drv / joystick_osx.c
blobbf82f671161fa1073bb31039912bf5d545f774bf
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.
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
29 #include "config.h"
31 #if defined(HAVE_IOKIT_HID_IOHIDLIB_H)
33 #define DWORD UInt32
34 #define LPDWORD UInt32*
35 #define LONG SInt32
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>
60 #undef ULONG
61 #undef E_INVALIDARG
62 #undef E_OUTOFMEMORY
63 #undef E_HANDLE
64 #undef E_ACCESSDENIED
65 #undef E_UNEXPECTED
66 #undef E_FAIL
67 #undef E_ABORT
68 #undef E_POINTER
69 #undef E_NOINTERFACE
70 #undef E_NOTIMPL
71 #undef S_FALSE
72 #undef S_OK
73 #undef HRESULT_FACILITY
74 #undef IS_ERROR
75 #undef FAILED
76 #undef SUCCEEDED
77 #undef MAKE_HRESULT
78 #undef HRESULT
79 #undef STDMETHODCALLTYPE
80 #undef DWORD
81 #undef LPDWORD
82 #undef LONG
83 #undef LPLONG
84 #undef E_PENDING
86 #include "joystick.h"
88 #include "wine/debug.h"
91 WINE_DEFAULT_DEBUG_CHANNEL(joystick);
94 #define MAXJOYSTICK (JOYSTICKID2 + 30)
97 enum {
98 AXIS_X, /* Winmm X */
99 AXIS_Y, /* Winmm Y */
100 AXIS_Z, /* Winmm Z */
101 AXIS_RX, /* Winmm V */
102 AXIS_RY, /* Winmm U */
103 AXIS_RZ, /* Winmm R */
104 NUM_AXES
107 struct axis {
108 IOHIDElementRef element;
109 CFIndex min_value, max_value;
112 typedef struct {
113 BOOL in_use;
114 IOHIDElementRef element;
115 struct axis axes[NUM_AXES];
116 CFMutableArrayRef buttons;
117 IOHIDElementRef hatswitch;
118 } joystick_t;
121 static joystick_t joysticks[MAXJOYSTICK];
122 static CFMutableArrayRef device_main_elements = NULL;
124 static long get_device_property_long(IOHIDDeviceRef device, CFStringRef key)
126 CFTypeRef ref;
127 long result = 0;
129 if (device)
131 assert(IOHIDDeviceGetTypeID() == CFGetTypeID(device));
133 ref = IOHIDDeviceGetProperty(device, key);
135 if (ref && CFNumberGetTypeID() == CFGetTypeID(ref))
136 CFNumberGetValue((CFNumberRef)ref, kCFNumberLongType, &result);
139 return result;
142 static CFStringRef copy_device_name(IOHIDDeviceRef device)
144 CFStringRef name;
146 if (device)
148 CFTypeRef ref_name;
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);
156 else
158 long vendID, prodID;
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);
165 else
167 ERR("NULL device\n");
168 name = CFStringCreateCopy(kCFAllocatorDefault, CFSTR(""));
171 return name;
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)
181 CFStringRef s;
182 const char* ret;
184 if (!t) return "(null)";
186 if (CFGetTypeID(t) == CFStringGetTypeID())
187 s = t;
188 else
189 s = CFCopyDescription(t);
190 ret = CFStringGetCStringPtr(s, kCFStringEncodingUTF8);
191 if (ret) ret = debugstr_a(ret);
192 if (!ret)
194 const UniChar* u = CFStringGetCharactersPtr(s);
195 if (u)
196 ret = debugstr_wn((const WCHAR*)u, CFStringGetLength(s));
198 if (!ret)
200 UniChar buf[200];
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);
206 return ret;
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)
225 switch (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;
235 return -1;
238 static int axis_for_usage_Sim(int usage)
240 switch (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;
249 return -1;
252 /**************************************************************************
253 * joystick_from_id
255 static joystick_t* joystick_from_id(DWORD_PTR device_id)
257 int index;
259 if ((device_id - (DWORD_PTR)joysticks) % sizeof(joysticks[0]) != 0)
260 return NULL;
261 index = (device_id - (DWORD_PTR)joysticks) / sizeof(joysticks[0]);
262 if (index < 0 || index >= MAXJOYSTICK || !((joystick_t*)device_id)->in_use)
263 return NULL;
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;
274 int number;
275 CFStringRef keys[] = { CFSTR(kIOHIDDeviceUsagePageKey), CFSTR(kIOHIDDeviceUsageKey) };
276 CFNumberRef values[2];
277 int i;
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);
290 if (!result)
291 ERR("CFDictionaryCreate failed.\n");
293 else
294 ERR("CFNumberCreate failed.\n");
296 for (i = 0; i < sizeof(values) / sizeof(values[0]); i++)
297 if (values[i]) CFRelease(values[i]);
299 return result;
302 /**************************************************************************
303 * find_top_level
305 static CFIndex find_top_level(IOHIDDeviceRef hid_device, CFMutableArrayRef main_elements)
307 CFArrayRef elements;
308 CFIndex total = 0;
310 TRACE("hid_device %s\n", debugstr_device(hid_device));
312 if (!hid_device)
313 return 0;
315 elements = IOHIDDeviceCopyMatchingElements(hid_device, NULL, 0);
317 if (elements)
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);
337 total++;
341 CFRelease(elements);
344 TRACE("-> total %d\n", (int)total);
345 return 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));
356 CFRelease(name1);
357 CFRelease(name2);
358 return result;
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);
371 if (loc1 < loc2)
372 return kCFCompareLessThan;
373 else if (loc1 > loc2)
374 return kCFCompareGreaterThan;
375 return device_name_comparator(device1, device2);
378 /**************************************************************************
379 * copy_set_to_array
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 /**************************************************************************
389 * find_osx_devices
391 static int find_osx_devices(void)
393 IOHIDManagerRef hid_manager;
394 int usages[] = { kHIDUsage_GD_Joystick, kHIDUsage_GD_GamePad, kHIDUsage_GD_MultiAxisController };
395 int i;
396 CFDictionaryRef matching_dicts[sizeof(usages) / sizeof(usages[0])];
397 CFArrayRef matching;
398 CFSetRef devset;
400 TRACE("()\n");
402 hid_manager = IOHIDManagerCreate(kCFAllocatorDefault, 0L);
403 if (IOHIDManagerOpen(hid_manager, 0) != kIOReturnSuccess)
405 ERR("Couldn't open IOHIDManager.\n");
406 CFRelease(hid_manager);
407 return 0;
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])
415 while (i > 0)
416 CFRelease(matching_dicts[--i]);
417 goto fail;
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);
428 CFRelease(matching);
429 devset = IOHIDManagerCopyDevices(hid_manager);
430 if (devset)
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);
440 CFRelease(devset);
441 if (!devices)
442 goto fail;
444 device_main_elements = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
445 if (!device_main_elements)
447 CFRelease(devices);
448 goto fail;
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);
459 CFRelease(devices);
461 TRACE("found %i device(s), %i collection(s)\n",(int)num_devices,(int)num_main_elements);
462 return (int)num_main_elements;
465 fail:
466 IOHIDManagerClose(hid_manager, 0);
467 CFRelease(hid_manager);
468 return 0;
471 /**************************************************************************
472 * collect_joystick_elements
474 static void collect_joystick_elements(joystick_t* joystick, IOHIDElementRef collection)
476 CFIndex i, count;
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;
485 int type;
486 uint32_t usage_page;
488 child = (IOHIDElementRef)CFArrayGetValueAtIndex(children, i);
489 TRACE("child %s\n", debugstr_element(child));
490 type = IOHIDElementGetType(child);
491 usage_page = IOHIDElementGetUsagePage(child);
493 switch (type)
495 case kIOHIDElementTypeCollection:
496 collect_joystick_elements(joystick, child);
497 break;
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);
505 break;
507 case kIOHIDElementTypeInput_Axis:
508 case kIOHIDElementTypeInput_Misc:
510 uint32_t usage = IOHIDElementGetUsage( child );
511 switch (usage_page)
513 case kHIDPage_GenericDesktop:
515 switch(usage)
517 case kHIDUsage_GD_Hatswitch:
519 TRACE("kIOHIDElementTypeInput_Axis/Misc / kHIDUsage_GD_Hatswitch\n");
520 if (joystick->hatswitch)
521 TRACE(" ignoring additional hatswitch\n");
522 else
523 joystick->hatswitch = (IOHIDElementRef)CFRetain(child);
524 break;
526 case kHIDUsage_GD_X:
527 case kHIDUsage_GD_Y:
528 case kHIDUsage_GD_Z:
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");
537 else
539 joystick->axes[axis].element = (IOHIDElementRef)CFRetain(child);
540 joystick->axes[axis].min_value = IOHIDElementGetLogicalMin(child);
541 joystick->axes[axis].max_value = IOHIDElementGetLogicalMax(child);
543 break;
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};
551 int axis = 0;
552 while(axis < 3 && joystick->axes[possible_axes[axis]].element)
553 axis++;
554 if (axis == 3)
555 TRACE("kIOHIDElementTypeInput_Axis/Misc / kHIDUsage_GD_<axis> (%d)\n ignoring\n", usage);
556 else
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);
563 break;
565 default:
566 FIXME("kIOHIDElementTypeInput_Axis/Misc / Unhandled GD Page usage %d\n", usage);
567 break;
569 break;
571 case kHIDPage_Simulation:
573 switch(usage)
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");
585 else
587 joystick->axes[axis].element = (IOHIDElementRef)CFRetain(child);
588 joystick->axes[axis].min_value = IOHIDElementGetLogicalMin(child);
589 joystick->axes[axis].max_value = IOHIDElementGetLogicalMax(child);
591 break;
593 default:
594 FIXME("kIOHIDElementTypeInput_Axis/Misc / Unhandled Sim Page usage %d\n", usage);
595 break;
597 break;
599 default:
600 FIXME("kIOHIDElementTypeInput_Axis/Misc / Unhandled Usage Page %d\n", usage_page);
601 break;
603 break;
605 case kIOHIDElementTypeFeature:
606 /* Describes input and output elements not intended for consumption by the end user. Ignoring. */
607 break;
608 default:
609 FIXME("Unhandled type %i\n",type);
610 break;
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);
623 if (usage1 < usage2)
624 return kCFCompareLessThan;
625 if (usage1 > usage2)
626 return kCFCompareGreaterThan;
627 return kCFCompareEqualTo;
630 /**************************************************************************
631 * driver_open
633 LRESULT driver_open(LPSTR str, DWORD index)
635 if (index >= MAXJOYSTICK || joysticks[index].in_use)
636 return 0;
638 joysticks[index].in_use = TRUE;
639 return (LRESULT)&joysticks[index];
642 /**************************************************************************
643 * driver_close
645 LRESULT driver_close(DWORD_PTR device_id)
647 joystick_t* joystick = joystick_from_id(device_id);
648 int i;
650 if (joystick == NULL)
651 return 0;
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));
665 return 1;
668 /**************************************************************************
669 * open_joystick
671 static BOOL open_joystick(joystick_t* joystick)
673 CFIndex index;
674 CFRange range;
676 if (joystick->element)
677 return TRUE;
679 if (!device_main_elements)
681 find_osx_devices();
682 if (!device_main_elements)
683 return FALSE;
686 index = joystick - joysticks;
687 if (index >= CFArrayGetCount(device_main_elements))
688 return FALSE;
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 */
695 range.location = 0;
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 */
701 range.location = 32;
702 range.length -= 32;
703 CFArrayReplaceValues(joystick->buttons, range, NULL, 0);
706 return TRUE;
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))
722 return JOYERR_PARMS;
724 caps->szPname[0] = 0;
726 device = IOHIDElementGetDevice(joystick->element);
727 if (device)
729 CFStringRef product_name = IOHIDDeviceGetProperty(device, CFSTR(kIOHIDProductKey));
730 if (product_name)
732 CFRange range;
734 range.location = 0;
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;
743 caps->wXmin = 0;
744 caps->wXmax = 0xFFFF;
745 caps->wYmin = 0;
746 caps->wYmax = 0xFFFF;
747 caps->wZmin = 0;
748 caps->wZmax = joystick->axes[AXIS_Z].element ? 0xFFFF : 0;
749 caps->wNumButtons = CFArrayGetCount(joystick->buttons);
750 if (size == sizeof(JOYCAPSW))
752 int i;
754 /* complete 95 structure */
755 caps->wRmin = 0;
756 caps->wRmax = 0xFFFF;
757 caps->wUmin = 0;
758 caps->wUmax = 0xFFFF;
759 caps->wVmin = 0;
760 caps->wVmax = 0xFFFF;
761 caps->wMaxAxes = 6; /* same as MS Joystick Driver */
762 caps->wNumAxes = 0;
763 caps->wMaxButtons = 32; /* same as MS Joystick Driver */
764 caps->szRegKey[0] = 0;
765 caps->szOEMVxD[0] = 0;
766 caps->wCaps = 0;
768 for (i = 0; i < NUM_AXES; i++)
770 if (joystick->axes[i].element)
772 caps->wNumAxes++;
773 switch (i)
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)
797 IOReturn ret;
798 ret = IOHIDDeviceGetValue(device, element, pValueRef);
799 switch (ret)
801 case kIOReturnSuccess:
802 return JOYERR_NOERROR;
803 case kIOReturnNotAttached:
804 return JOYERR_UNPLUGGED;
805 default:
806 ERR("IOHIDDeviceGetValue returned 0x%x\n",ret);
807 return JOYERR_NOCANDO;
811 /**************************************************************************
812 * driver_joyGetPosEx
814 LRESULT driver_joyGetPosEx(DWORD_PTR device_id, JOYINFOEX* info)
816 static const struct {
817 DWORD flag;
818 off_t offset;
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;
830 CFIndex i, count;
831 IOHIDValueRef valueRef;
832 long value;
833 LRESULT rc;
835 if ((joystick = joystick_from_id(device_id)) == NULL)
836 return MMSYSERR_NODRIVER;
838 if (!open_joystick(joystick))
839 return JOYERR_PARMS;
841 device = IOHIDElementGetDevice(joystick->element);
843 if (info->dwFlags & JOY_RETURNBUTTONS)
845 info->dwButtons = 0;
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)
854 return rc;
855 value = IOHIDValueGetIntegerValue(valueRef);
856 if (value)
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)
873 return rc;
874 value = IOHIDValueGetIntegerValue(valueRef) - joystick->axes[i].min_value;
875 *field = MulDiv(value, 0xFFFF, joystick->axes[i].max_value - joystick->axes[i].min_value);
877 else
879 *field = 0;
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)
891 return rc;
892 value = IOHIDValueGetIntegerValue(valueRef);
893 if (value >= 8)
894 info->dwPOV = JOY_POVCENTERED;
895 else
896 info->dwPOV = value * 4500;
898 else
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 /**************************************************************************
912 * driver_joyGetPos
914 LRESULT driver_joyGetPos(DWORD_PTR device_id, JOYINFO* info)
916 JOYINFOEX ji;
917 LONG ret;
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;
932 return ret;
935 #endif /* HAVE_IOKIT_HID_IOHIDLIB_H */