kernel32/tests: Add more tests for UpdateResource.
[wine.git] / dlls / dinput / joystick_osx.c
blob4ec724655004b4e798401315cddd387c03a2fbed
1 /* DirectInput Joystick device for Mac OS/X
3 * Copyright 1998 Marcus Meissner
4 * Copyright 1998,1999 Lionel Ulmer
5 * Copyright 2000-2001 TransGaming Technologies Inc.
6 * Copyright 2009 CodeWeavers, Aric Stewart
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation; either
11 * version 2.1 of the License, or (at your option) any later version.
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with this library; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
23 #include "config.h"
24 #include "wine/port.h"
26 #if defined(HAVE_IOKIT_HID_IOHIDLIB_H)
27 #define DWORD UInt32
28 #define LPDWORD UInt32*
29 #define LONG SInt32
30 #define LPLONG SInt32*
31 #define E_PENDING __carbon_E_PENDING
32 #define ULONG __carbon_ULONG
33 #define E_INVALIDARG __carbon_E_INVALIDARG
34 #define E_OUTOFMEMORY __carbon_E_OUTOFMEMORY
35 #define E_HANDLE __carbon_E_HANDLE
36 #define E_ACCESSDENIED __carbon_E_ACCESSDENIED
37 #define E_UNEXPECTED __carbon_E_UNEXPECTED
38 #define E_FAIL __carbon_E_FAIL
39 #define E_ABORT __carbon_E_ABORT
40 #define E_POINTER __carbon_E_POINTER
41 #define E_NOINTERFACE __carbon_E_NOINTERFACE
42 #define E_NOTIMPL __carbon_E_NOTIMPL
43 #define S_FALSE __carbon_S_FALSE
44 #define S_OK __carbon_S_OK
45 #define HRESULT_FACILITY __carbon_HRESULT_FACILITY
46 #define IS_ERROR __carbon_IS_ERROR
47 #define FAILED __carbon_FAILED
48 #define SUCCEEDED __carbon_SUCCEEDED
49 #define MAKE_HRESULT __carbon_MAKE_HRESULT
50 #define HRESULT __carbon_HRESULT
51 #define STDMETHODCALLTYPE __carbon_STDMETHODCALLTYPE
52 #include <IOKit/IOKitLib.h>
53 #include <IOKit/hid/IOHIDLib.h>
54 #include <ForceFeedback/ForceFeedback.h>
55 #undef ULONG
56 #undef E_INVALIDARG
57 #undef E_OUTOFMEMORY
58 #undef E_HANDLE
59 #undef E_ACCESSDENIED
60 #undef E_UNEXPECTED
61 #undef E_FAIL
62 #undef E_ABORT
63 #undef E_POINTER
64 #undef E_NOINTERFACE
65 #undef E_NOTIMPL
66 #undef S_FALSE
67 #undef S_OK
68 #undef HRESULT_FACILITY
69 #undef IS_ERROR
70 #undef FAILED
71 #undef SUCCEEDED
72 #undef MAKE_HRESULT
73 #undef HRESULT
74 #undef STDMETHODCALLTYPE
75 #undef DWORD
76 #undef LPDWORD
77 #undef LONG
78 #undef LPLONG
79 #undef E_PENDING
80 #endif /* HAVE_IOKIT_HID_IOHIDLIB_H */
82 #include "wine/debug.h"
83 #include "wine/unicode.h"
84 #include "windef.h"
85 #include "winbase.h"
86 #include "winerror.h"
87 #include "winreg.h"
88 #include "devguid.h"
89 #include "dinput.h"
91 #include "dinput_private.h"
92 #include "device_private.h"
93 #include "joystick_private.h"
95 #ifdef HAVE_IOHIDMANAGERCREATE
97 WINE_DEFAULT_DEBUG_CHANNEL(dinput);
99 static CFMutableArrayRef device_main_elements = NULL;
101 typedef struct JoystickImpl JoystickImpl;
102 static const IDirectInputDevice8AVtbl JoystickAvt;
103 static const IDirectInputDevice8WVtbl JoystickWvt;
105 struct JoystickImpl
107 struct JoystickGenericImpl generic;
109 /* osx private */
110 int id;
111 CFArrayRef elements;
112 ObjProps **propmap;
113 FFDeviceObjectReference ff;
114 struct list effects;
117 static inline JoystickImpl *impl_from_IDirectInputDevice8A(IDirectInputDevice8A *iface)
119 return CONTAINING_RECORD(CONTAINING_RECORD(CONTAINING_RECORD(iface, IDirectInputDeviceImpl, IDirectInputDevice8A_iface),
120 JoystickGenericImpl, base), JoystickImpl, generic);
122 static inline JoystickImpl *impl_from_IDirectInputDevice8W(IDirectInputDevice8W *iface)
124 return CONTAINING_RECORD(CONTAINING_RECORD(CONTAINING_RECORD(iface, IDirectInputDeviceImpl, IDirectInputDevice8W_iface),
125 JoystickGenericImpl, base), JoystickImpl, generic);
128 static inline IDirectInputDevice8W *IDirectInputDevice8W_from_impl(JoystickImpl *This)
130 return &This->generic.base.IDirectInputDevice8W_iface;
133 typedef struct _EffectImpl {
134 IDirectInputEffect IDirectInputEffect_iface;
135 LONG ref;
137 JoystickImpl *device;
138 FFEffectObjectReference effect;
139 GUID guid;
141 struct list entry;
142 } EffectImpl;
144 static EffectImpl *impl_from_IDirectInputEffect(IDirectInputEffect *iface)
146 return CONTAINING_RECORD(iface, EffectImpl, IDirectInputEffect_iface);
149 static const IDirectInputEffectVtbl EffectVtbl;
151 static const GUID DInput_Wine_OsX_Joystick_GUID = { /* 59CAD8F6-E617-41E2-8EB7-47B23EEEDC5A */
152 0x59CAD8F6, 0xE617, 0x41E2, {0x8E, 0xB7, 0x47, 0xB2, 0x3E, 0xEE, 0xDC, 0x5A}
155 static HRESULT osx_to_win32_hresult(HRESULT in)
157 /* OSX returns 16-bit COM runtime errors, which we should
158 * convert to win32 */
159 switch(in){
160 case 0x80000001:
161 return E_NOTIMPL;
162 case 0x80000002:
163 return E_OUTOFMEMORY;
164 case 0x80000003:
165 return E_INVALIDARG;
166 case 0x80000004:
167 return E_NOINTERFACE;
168 case 0x80000005:
169 return E_POINTER;
170 case 0x80000006:
171 return E_HANDLE;
172 case 0x80000007:
173 return E_ABORT;
174 case 0x80000008:
175 return E_FAIL;
176 case 0x80000009:
177 return E_ACCESSDENIED;
178 case 0x8000FFFF:
179 return E_UNEXPECTED;
181 return in;
184 static long get_device_property_long(IOHIDDeviceRef device, CFStringRef key)
186 CFTypeRef ref;
187 long result = 0;
189 if (device)
191 assert(IOHIDDeviceGetTypeID() == CFGetTypeID(device));
193 ref = IOHIDDeviceGetProperty(device, key);
195 if (ref && CFNumberGetTypeID() == CFGetTypeID(ref))
196 CFNumberGetValue((CFNumberRef)ref, kCFNumberLongType, &result);
199 return result;
202 static CFStringRef copy_device_name(IOHIDDeviceRef device)
204 CFStringRef name;
206 if (device)
208 CFTypeRef ref_name;
210 assert(IOHIDDeviceGetTypeID() == CFGetTypeID(device));
212 ref_name = IOHIDDeviceGetProperty(device, CFSTR(kIOHIDProductKey));
214 if (ref_name && CFStringGetTypeID() == CFGetTypeID(ref_name))
215 name = CFStringCreateCopy(kCFAllocatorDefault, ref_name);
216 else
218 long vendID, prodID;
220 vendID = get_device_property_long(device, CFSTR(kIOHIDVendorIDKey));
221 prodID = get_device_property_long(device, CFSTR(kIOHIDProductIDKey));
222 name = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("0x%04lx 0x%04lx"), vendID, prodID);
225 else
227 ERR("NULL device\n");
228 name = CFStringCreateCopy(kCFAllocatorDefault, CFSTR(""));
231 return name;
234 static long get_device_location_ID(IOHIDDeviceRef device)
236 return get_device_property_long(device, CFSTR(kIOHIDLocationIDKey));
239 static void copy_set_to_array(const void *value, void *context)
241 CFArrayAppendValue(context, value);
244 static CFComparisonResult device_name_comparator(IOHIDDeviceRef device1, IOHIDDeviceRef device2)
246 CFStringRef name1 = copy_device_name(device1), name2 = copy_device_name(device2);
247 CFComparisonResult result = CFStringCompare(name1, name2, (kCFCompareForcedOrdering | kCFCompareNumerically));
248 CFRelease(name1);
249 CFRelease(name2);
250 return result;
253 static CFComparisonResult device_location_name_comparator(const void *val1, const void *val2, void *context)
255 IOHIDDeviceRef device1 = (IOHIDDeviceRef)val1, device2 = (IOHIDDeviceRef)val2;
256 long loc1 = get_device_location_ID(device1), loc2 = get_device_location_ID(device2);
258 if (loc1 < loc2)
259 return kCFCompareLessThan;
260 else if (loc1 > loc2)
261 return kCFCompareGreaterThan;
262 /* virtual joysticks may not have a kIOHIDLocationIDKey and will default to location ID of 0, this orders virtual joysticks by their name */
263 return device_name_comparator(device1, device2);
266 static const char* debugstr_cf(CFTypeRef t)
268 CFStringRef s;
269 const char* ret;
271 if (!t) return "(null)";
273 if (CFGetTypeID(t) == CFStringGetTypeID())
274 s = t;
275 else
276 s = CFCopyDescription(t);
277 ret = CFStringGetCStringPtr(s, kCFStringEncodingUTF8);
278 if (ret) ret = debugstr_a(ret);
279 if (!ret)
281 const UniChar* u = CFStringGetCharactersPtr(s);
282 if (u)
283 ret = debugstr_wn((const WCHAR*)u, CFStringGetLength(s));
285 if (!ret)
287 UniChar buf[200];
288 int len = min(CFStringGetLength(s), ARRAY_SIZE(buf));
289 CFStringGetCharacters(s, CFRangeMake(0, len), buf);
290 ret = debugstr_wn(buf, len);
292 if (s != t) CFRelease(s);
293 return ret;
296 static const char* debugstr_device(IOHIDDeviceRef device)
298 return wine_dbg_sprintf("<IOHIDDevice %p product %s IOHIDLocationID %lu>", device,
299 debugstr_cf(IOHIDDeviceGetProperty(device, CFSTR(kIOHIDProductKey))),
300 get_device_location_ID(device));
303 static const char* debugstr_element(IOHIDElementRef element)
305 return wine_dbg_sprintf("<IOHIDElement %p type %d usage %u/%u device %p>", element,
306 IOHIDElementGetType(element), IOHIDElementGetUsagePage(element),
307 IOHIDElementGetUsage(element), IOHIDElementGetDevice(element));
310 static IOHIDDeviceRef get_device_ref(int id)
312 IOHIDElementRef device_main_element;
313 IOHIDDeviceRef hid_device;
315 TRACE("id %d\n", id);
317 if (!device_main_elements || id >= CFArrayGetCount(device_main_elements))
318 return 0;
320 device_main_element = (IOHIDElementRef)CFArrayGetValueAtIndex(device_main_elements, id);
321 if (!device_main_element)
323 ERR("Invalid Element requested %i\n",id);
324 return 0;
327 hid_device = IOHIDElementGetDevice(device_main_element);
328 if (!hid_device)
330 ERR("Invalid Device requested %i\n",id);
331 return 0;
334 TRACE("-> %s\n", debugstr_device(hid_device));
335 return hid_device;
338 static HRESULT get_ff(IOHIDDeviceRef device, FFDeviceObjectReference *ret)
340 io_service_t service;
341 CFMutableDictionaryRef matching;
342 CFTypeRef location_id;
343 HRESULT hr;
345 TRACE("device %s\n", debugstr_device(device));
347 matching = IOServiceMatching(kIOHIDDeviceKey);
348 if(!matching){
349 WARN("IOServiceMatching failed, force feedback disabled\n");
350 return DIERR_DEVICENOTREG;
353 location_id = IOHIDDeviceGetProperty(device, CFSTR(kIOHIDLocationIDKey));
354 if(!location_id){
355 CFRelease(matching);
356 WARN("IOHIDDeviceGetProperty failed, force feedback disabled\n");
357 return DIERR_DEVICENOTREG;
360 CFDictionaryAddValue(matching, CFSTR(kIOHIDLocationIDKey), location_id);
362 service = IOServiceGetMatchingService(kIOMasterPortDefault, matching);
364 if (ret)
365 hr = osx_to_win32_hresult(FFCreateDevice(service, ret));
366 else
367 hr = FFIsForceFeedback(service) == FF_OK ? S_OK : S_FALSE;
369 IOObjectRelease(service);
370 TRACE("-> hr 0x%08x *ret %p\n", hr, ret ? *ret : NULL);
371 return hr;
374 static CFMutableDictionaryRef create_osx_device_match(int usage)
376 CFMutableDictionaryRef result;
378 TRACE("usage %d\n", usage);
380 result = CFDictionaryCreateMutable( kCFAllocatorDefault, 0,
381 &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks );
383 if ( result )
385 int number = kHIDPage_GenericDesktop;
386 CFNumberRef page = CFNumberCreate( kCFAllocatorDefault,
387 kCFNumberIntType, &number);
389 if (page)
391 CFNumberRef cf_usage;
393 CFDictionarySetValue( result, CFSTR( kIOHIDDeviceUsagePageKey ), page );
394 CFRelease( page );
396 cf_usage = CFNumberCreate( kCFAllocatorDefault,
397 kCFNumberIntType, &usage);
398 if (cf_usage)
400 CFDictionarySetValue( result, CFSTR( kIOHIDDeviceUsageKey ), cf_usage );
401 CFRelease( cf_usage );
403 else
405 ERR("CFNumberCreate() failed.\n");
406 CFRelease(result);
407 return NULL;
410 else
412 ERR("CFNumberCreate failed.\n");
413 CFRelease(result);
414 return NULL;
417 else
419 ERR("CFDictionaryCreateMutable failed.\n");
420 return NULL;
423 return result;
426 static CFIndex find_top_level(IOHIDDeviceRef hid_device, CFMutableArrayRef main_elements)
428 CFArrayRef elements;
429 CFIndex total = 0;
431 TRACE("hid_device %s\n", debugstr_device(hid_device));
433 if (!hid_device)
434 return 0;
436 elements = IOHIDDeviceCopyMatchingElements(hid_device, NULL, 0);
438 if (elements)
440 CFIndex idx, cnt = CFArrayGetCount(elements);
441 for (idx=0; idx<cnt; idx++)
443 IOHIDElementRef element = (IOHIDElementRef)CFArrayGetValueAtIndex(elements, idx);
444 int type = IOHIDElementGetType(element);
446 TRACE("element %s\n", debugstr_element(element));
448 /* Check for top-level gaming device collections */
449 if (type == kIOHIDElementTypeCollection && IOHIDElementGetParent(element) == 0)
451 int usage_page = IOHIDElementGetUsagePage(element);
452 int usage = IOHIDElementGetUsage(element);
454 if (usage_page == kHIDPage_GenericDesktop &&
455 (usage == kHIDUsage_GD_Joystick || usage == kHIDUsage_GD_GamePad))
457 CFArrayAppendValue(main_elements, element);
458 total++;
462 CFRelease(elements);
465 TRACE("-> total %d\n", (int)total);
466 return total;
469 static void get_element_children(IOHIDElementRef element, CFMutableArrayRef all_children)
471 CFIndex idx, cnt;
472 CFArrayRef element_children = IOHIDElementGetChildren(element);
474 TRACE("element %s\n", debugstr_element(element));
476 cnt = CFArrayGetCount(element_children);
478 /* Either add the element to the array or grab its children */
479 for (idx=0; idx<cnt; idx++)
481 IOHIDElementRef child;
483 child = (IOHIDElementRef)CFArrayGetValueAtIndex(element_children, idx);
484 TRACE("child %s\n", debugstr_element(child));
485 if (IOHIDElementGetType(child) == kIOHIDElementTypeCollection)
486 get_element_children(child, all_children);
487 else
488 CFArrayAppendValue(all_children, child);
492 static int find_osx_devices(void)
494 IOHIDManagerRef hid_manager;
495 CFMutableDictionaryRef result;
496 CFSetRef devset;
497 CFMutableArrayRef matching;
499 TRACE("()\n");
501 hid_manager = IOHIDManagerCreate( kCFAllocatorDefault, 0L );
502 if (IOHIDManagerOpen( hid_manager, 0 ) != kIOReturnSuccess)
504 ERR("Couldn't open IOHIDManager.\n");
505 CFRelease( hid_manager );
506 return 0;
509 matching = CFArrayCreateMutable( kCFAllocatorDefault, 0,
510 &kCFTypeArrayCallBacks );
512 /* build matching dictionary */
513 result = create_osx_device_match(kHIDUsage_GD_Joystick);
514 if (!result)
516 CFRelease(matching);
517 goto fail;
519 CFArrayAppendValue( matching, result );
520 CFRelease( result );
521 result = create_osx_device_match(kHIDUsage_GD_GamePad);
522 if (!result)
524 CFRelease(matching);
525 goto fail;
527 CFArrayAppendValue( matching, result );
528 CFRelease( result );
530 IOHIDManagerSetDeviceMatchingMultiple( hid_manager, matching);
531 CFRelease( matching );
532 devset = IOHIDManagerCopyDevices( hid_manager );
533 if (devset)
535 CFIndex num_devices, num_main_elements, idx;
536 CFMutableArrayRef devices;
538 num_devices = CFSetGetCount(devset);
539 devices = CFArrayCreateMutable(kCFAllocatorDefault, num_devices, &kCFTypeArrayCallBacks);
540 CFSetApplyFunction(devset, copy_set_to_array, devices);
541 CFRelease(devset);
542 CFArraySortValues(devices, CFRangeMake(0, num_devices), device_location_name_comparator, NULL);
544 device_main_elements = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
545 if (!device_main_elements)
547 CFRelease( devices );
548 goto fail;
551 num_main_elements = 0;
552 for (idx = 0; idx < num_devices; idx++)
554 CFIndex top;
555 IOHIDDeviceRef hid_device;
557 hid_device = (IOHIDDeviceRef) CFArrayGetValueAtIndex(devices, idx);
558 TRACE("hid_device %s\n", debugstr_device(hid_device));
559 top = find_top_level(hid_device, device_main_elements);
560 num_main_elements += top;
563 CFRelease(devices);
565 TRACE("found %i device(s), %i collection(s)\n",(int)num_devices,(int)num_main_elements);
566 return (int)num_main_elements;
569 fail:
570 IOHIDManagerClose( hid_manager, 0 );
571 CFRelease( hid_manager );
572 return 0;
575 static int get_osx_device_name(int id, char *name, int length)
577 CFStringRef str;
578 IOHIDDeviceRef hid_device;
580 hid_device = get_device_ref(id);
582 TRACE("id %d hid_device %s\n", id, debugstr_device(hid_device));
584 if (name)
585 name[0] = 0;
587 if (!hid_device)
588 return 0;
590 str = IOHIDDeviceGetProperty(hid_device, CFSTR( kIOHIDProductKey ));
591 if (str)
593 CFIndex len = CFStringGetLength(str);
594 if (length >= len)
596 CFStringGetCString(str,name,length,kCFStringEncodingASCII);
597 return len;
599 else
600 return (len+1);
602 return 0;
605 static CFComparisonResult button_usage_comparator(const void *val1, const void *val2, void *context)
607 IOHIDElementRef element1 = (IOHIDElementRef)val1, element2 = (IOHIDElementRef)val2;
608 int usage1 = IOHIDElementGetUsage(element1), usage2 = IOHIDElementGetUsage(element2);
610 if (usage1 < usage2)
611 return kCFCompareLessThan;
612 if (usage1 > usage2)
613 return kCFCompareGreaterThan;
614 return kCFCompareEqualTo;
617 static void get_osx_device_elements(JoystickImpl *device, int axis_map[8])
619 IOHIDElementRef device_main_element;
620 CFMutableArrayRef elements;
621 DWORD sliders = 0;
623 TRACE("device %p device->id %d\n", device, device->id);
625 device->elements = NULL;
627 if (!device_main_elements || device->id >= CFArrayGetCount(device_main_elements))
628 return;
630 device_main_element = (IOHIDElementRef)CFArrayGetValueAtIndex(device_main_elements, device->id);
631 TRACE("device_main_element %s\n", debugstr_element(device_main_element));
632 if (!device_main_element)
633 return;
635 elements = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
636 get_element_children(device_main_element, elements);
638 if (elements)
640 CFIndex idx, cnt = CFArrayGetCount( elements );
641 CFMutableArrayRef axes = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
642 CFMutableArrayRef buttons = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
643 CFMutableArrayRef povs = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
645 for ( idx = 0; idx < cnt; idx++ )
647 IOHIDElementRef element = ( IOHIDElementRef ) CFArrayGetValueAtIndex( elements, idx );
648 int type = IOHIDElementGetType( element );
649 int usage_page = IOHIDElementGetUsagePage( element );
651 TRACE("element %s\n", debugstr_element(element));
653 if (usage_page >= kHIDPage_VendorDefinedStart)
655 /* vendor pages can repurpose type ids, resulting in incorrect case matches below (e.g. ds4 controllers) */
656 continue;
659 switch(type)
661 case kIOHIDElementTypeInput_Button:
663 TRACE("kIOHIDElementTypeInput_Button usage_page %d\n", usage_page);
664 if (usage_page != kHIDPage_Button)
666 /* avoid strange elements found on the 360 controller */
667 continue;
670 if (CFArrayGetCount(buttons) < 128)
671 CFArrayAppendValue(buttons, element);
672 break;
674 case kIOHIDElementTypeInput_Axis:
676 TRACE("kIOHIDElementTypeInput_Axis\n");
677 CFArrayAppendValue(axes, element);
678 break;
680 case kIOHIDElementTypeInput_Misc:
682 uint32_t usage = IOHIDElementGetUsage( element );
683 switch(usage)
685 case kHIDUsage_GD_Hatswitch:
687 TRACE("kIOHIDElementTypeInput_Misc / kHIDUsage_GD_Hatswitch\n");
688 CFArrayAppendValue(povs, element);
689 break;
691 case kHIDUsage_GD_Slider:
692 sliders ++;
693 if (sliders > 2)
694 break;
695 /* fallthrough, sliders are axis */
696 case kHIDUsage_GD_X:
697 case kHIDUsage_GD_Y:
698 case kHIDUsage_GD_Z:
699 case kHIDUsage_GD_Rx:
700 case kHIDUsage_GD_Ry:
701 case kHIDUsage_GD_Rz:
703 TRACE("kIOHIDElementTypeInput_Misc / kHIDUsage_GD_* (%d)\n", usage);
704 axis_map[CFArrayGetCount(axes)]=usage;
705 CFArrayAppendValue(axes, element);
706 break;
708 default:
709 FIXME("kIOHIDElementTypeInput_Misc / Unhandled usage %i\n", usage);
711 break;
713 default:
714 FIXME("Unhandled type %i\n",type);
718 /* Sort buttons into correct order */
719 CFArraySortValues(buttons, CFRangeMake(0, CFArrayGetCount(buttons)), button_usage_comparator, NULL);
721 device->generic.devcaps.dwAxes = CFArrayGetCount(axes);
722 device->generic.devcaps.dwButtons = CFArrayGetCount(buttons);
723 device->generic.devcaps.dwPOVs = CFArrayGetCount(povs);
725 TRACE("axes %u povs %u buttons %u\n", device->generic.devcaps.dwAxes, device->generic.devcaps.dwPOVs,
726 device->generic.devcaps.dwButtons);
728 /* build our element array in the order that dinput expects */
729 CFArrayAppendArray(axes, povs, CFRangeMake(0, device->generic.devcaps.dwPOVs));
730 CFArrayAppendArray(axes, buttons, CFRangeMake(0, device->generic.devcaps.dwButtons));
731 device->elements = axes;
732 axes = NULL;
734 CFRelease(povs);
735 CFRelease(buttons);
736 CFRelease(elements);
738 else
740 device->generic.devcaps.dwAxes = 0;
741 device->generic.devcaps.dwButtons = 0;
742 device->generic.devcaps.dwPOVs = 0;
746 static void get_osx_device_elements_props(JoystickImpl *device)
748 TRACE("device %p\n", device);
750 if (device->elements)
752 CFIndex idx, cnt = CFArrayGetCount( device->elements );
754 for ( idx = 0; idx < cnt; idx++ )
756 IOHIDElementRef element = ( IOHIDElementRef ) CFArrayGetValueAtIndex( device->elements, idx );
758 TRACE("element %s\n", debugstr_element(element));
760 device->generic.props[idx].lDevMin = IOHIDElementGetLogicalMin(element);
761 device->generic.props[idx].lDevMax = IOHIDElementGetLogicalMax(element);
762 device->generic.props[idx].lMin = 0;
763 device->generic.props[idx].lMax = 0xffff;
764 device->generic.props[idx].lDeadZone = 0;
765 device->generic.props[idx].lSaturation = 0;
770 static void poll_osx_device_state(LPDIRECTINPUTDEVICE8A iface)
772 JoystickImpl *device = impl_from_IDirectInputDevice8A(iface);
773 IOHIDElementRef device_main_element;
774 IOHIDDeviceRef hid_device;
776 TRACE("device %p device->id %i\n", device, device->id);
778 if (!device_main_elements || device->id >= CFArrayGetCount(device_main_elements))
779 return;
781 device_main_element = (IOHIDElementRef) CFArrayGetValueAtIndex(device_main_elements, device->id);
782 hid_device = IOHIDElementGetDevice(device_main_element);
783 TRACE("main element %s hid_device %s\n", debugstr_element(device_main_element), debugstr_device(hid_device));
784 if (!hid_device)
785 return;
787 if (device->elements)
789 int button_idx = 0;
790 int pov_idx = 0;
791 int slider_idx = 0;
792 int inst_id;
793 CFIndex idx, cnt = CFArrayGetCount( device->elements );
795 for ( idx = 0; idx < cnt; idx++ )
797 IOHIDValueRef valueRef;
798 int val, oldVal, newVal;
799 IOHIDElementRef element = ( IOHIDElementRef ) CFArrayGetValueAtIndex( device->elements, idx );
800 int type = IOHIDElementGetType( element );
802 TRACE("element %s\n", debugstr_element(element));
804 switch(type)
806 case kIOHIDElementTypeInput_Button:
807 TRACE("kIOHIDElementTypeInput_Button\n");
808 if(button_idx < 128)
810 valueRef = NULL;
811 if (IOHIDDeviceGetValue(hid_device, element, &valueRef) != kIOReturnSuccess)
812 return;
813 if (valueRef == NULL)
814 return;
815 val = IOHIDValueGetIntegerValue(valueRef);
816 newVal = val ? 0x80 : 0x0;
817 oldVal = device->generic.js.rgbButtons[button_idx];
818 device->generic.js.rgbButtons[button_idx] = newVal;
819 TRACE("valueRef %s val %d oldVal %d newVal %d\n", debugstr_cf(valueRef), val, oldVal, newVal);
820 if (oldVal != newVal)
822 inst_id = DIDFT_MAKEINSTANCE(button_idx) | DIDFT_PSHBUTTON;
823 queue_event(iface,inst_id,newVal,GetCurrentTime(),device->generic.base.dinput->evsequence++);
825 button_idx ++;
827 break;
828 case kIOHIDElementTypeInput_Misc:
830 uint32_t usage = IOHIDElementGetUsage( element );
831 switch(usage)
833 case kHIDUsage_GD_Hatswitch:
835 TRACE("kIOHIDElementTypeInput_Misc / kHIDUsage_GD_Hatswitch\n");
836 valueRef = NULL;
837 if (IOHIDDeviceGetValue(hid_device, element, &valueRef) != kIOReturnSuccess)
838 return;
839 if (valueRef == NULL)
840 return;
841 val = IOHIDValueGetIntegerValue(valueRef);
842 oldVal = device->generic.js.rgdwPOV[pov_idx];
843 if (val >= 8)
844 newVal = -1;
845 else
846 newVal = val * 4500;
847 device->generic.js.rgdwPOV[pov_idx] = newVal;
848 TRACE("valueRef %s val %d oldVal %d newVal %d\n", debugstr_cf(valueRef), val, oldVal, newVal);
849 if (oldVal != newVal)
851 inst_id = DIDFT_MAKEINSTANCE(pov_idx) | DIDFT_POV;
852 queue_event(iface,inst_id,newVal,GetCurrentTime(),device->generic.base.dinput->evsequence++);
854 pov_idx ++;
855 break;
857 case kHIDUsage_GD_X:
858 case kHIDUsage_GD_Y:
859 case kHIDUsage_GD_Z:
860 case kHIDUsage_GD_Rx:
861 case kHIDUsage_GD_Ry:
862 case kHIDUsage_GD_Rz:
863 case kHIDUsage_GD_Slider:
865 int wine_obj = -1;
867 valueRef = NULL;
868 if (IOHIDDeviceGetValue(hid_device, element, &valueRef) != kIOReturnSuccess)
869 return;
870 if (valueRef == NULL)
871 return;
872 val = IOHIDValueGetIntegerValue(valueRef);
873 newVal = joystick_map_axis(&device->generic.props[idx], val);
874 switch (usage)
876 case kHIDUsage_GD_X:
877 TRACE("kIOHIDElementTypeInput_Misc / kHIDUsage_GD_X\n");
878 wine_obj = 0;
879 oldVal = device->generic.js.lX;
880 device->generic.js.lX = newVal;
881 break;
882 case kHIDUsage_GD_Y:
883 TRACE("kIOHIDElementTypeInput_Misc / kHIDUsage_GD_Y\n");
884 wine_obj = 1;
885 oldVal = device->generic.js.lY;
886 device->generic.js.lY = newVal;
887 break;
888 case kHIDUsage_GD_Z:
889 TRACE("kIOHIDElementTypeInput_Misc / kHIDUsage_GD_Z\n");
890 wine_obj = 2;
891 oldVal = device->generic.js.lZ;
892 device->generic.js.lZ = newVal;
893 break;
894 case kHIDUsage_GD_Rx:
895 TRACE("kIOHIDElementTypeInput_Misc / kHIDUsage_GD_Rx\n");
896 wine_obj = 3;
897 oldVal = device->generic.js.lRx;
898 device->generic.js.lRx = newVal;
899 break;
900 case kHIDUsage_GD_Ry:
901 TRACE("kIOHIDElementTypeInput_Misc / kHIDUsage_GD_Ry\n");
902 wine_obj = 4;
903 oldVal = device->generic.js.lRy;
904 device->generic.js.lRy = newVal;
905 break;
906 case kHIDUsage_GD_Rz:
907 TRACE("kIOHIDElementTypeInput_Misc / kHIDUsage_GD_Rz\n");
908 wine_obj = 5;
909 oldVal = device->generic.js.lRz;
910 device->generic.js.lRz = newVal;
911 break;
912 case kHIDUsage_GD_Slider:
913 TRACE("kIOHIDElementTypeInput_Misc / kHIDUsage_GD_Slider\n");
914 wine_obj = 6 + slider_idx;
915 oldVal = device->generic.js.rglSlider[slider_idx];
916 device->generic.js.rglSlider[slider_idx] = newVal;
917 slider_idx ++;
918 break;
920 TRACE("valueRef %s val %d oldVal %d newVal %d\n", debugstr_cf(valueRef), val, oldVal, newVal);
921 if ((wine_obj != -1) &&
922 (oldVal != newVal))
924 inst_id = DIDFT_MAKEINSTANCE(wine_obj) | DIDFT_ABSAXIS;
925 queue_event(iface,inst_id,newVal,GetCurrentTime(),device->generic.base.dinput->evsequence++);
928 break;
930 default:
931 FIXME("kIOHIDElementTypeInput_Misc / unhandled usage %i\n", usage);
933 break;
935 default:
936 FIXME("Unhandled type %i\n",type);
942 static INT find_joystick_devices(void)
944 static INT joystick_devices_count = -1;
946 if (joystick_devices_count != -1) return joystick_devices_count;
948 joystick_devices_count = find_osx_devices();
950 return joystick_devices_count;
953 static DWORD make_vid_pid(IOHIDDeviceRef device)
955 long vendID, prodID;
957 vendID = get_device_property_long(device, CFSTR(kIOHIDVendorIDKey));
958 prodID = get_device_property_long(device, CFSTR(kIOHIDProductIDKey));
960 return MAKELONG(vendID, prodID);
963 static HRESULT joydev_enum_deviceA(DWORD dwDevType, DWORD dwFlags, LPDIDEVICEINSTANCEA lpddi, DWORD version, int id)
965 IOHIDDeviceRef device;
966 BOOL is_joystick;
968 TRACE("dwDevType %u dwFlags 0x%08x version 0x%04x id %d\n", dwDevType, dwFlags, version, id);
970 if (id >= find_joystick_devices()) return E_FAIL;
972 device = get_device_ref(id);
974 if ((dwDevType == 0) ||
975 ((dwDevType == DIDEVTYPE_JOYSTICK) && (version >= 0x0300 && version < 0x0800)) ||
976 (((dwDevType == DI8DEVCLASS_GAMECTRL) || (dwDevType == DI8DEVTYPE_JOYSTICK)) && (version >= 0x0800)))
978 if (dwFlags & DIEDFL_FORCEFEEDBACK) {
979 if(!device)
980 return S_FALSE;
981 if(get_ff(device, NULL) != S_OK)
982 return S_FALSE;
984 is_joystick = get_device_property_long(device, CFSTR(kIOHIDDeviceUsageKey)) == kHIDUsage_GD_Joystick;
985 /* Return joystick */
986 lpddi->guidInstance = DInput_Wine_OsX_Joystick_GUID;
987 lpddi->guidInstance.Data3 = id;
988 lpddi->guidProduct = DInput_PIDVID_Product_GUID;
989 lpddi->guidProduct.Data1 = make_vid_pid(device);
990 lpddi->dwDevType = get_device_type(version, is_joystick);
991 lpddi->dwDevType |= DIDEVTYPE_HID;
992 lpddi->wUsagePage = 0x01; /* Desktop */
993 if (is_joystick)
994 lpddi->wUsage = 0x04; /* Joystick */
995 else
996 lpddi->wUsage = 0x05; /* Game Pad */
997 sprintf(lpddi->tszInstanceName, "Joystick %d", id);
999 /* get the device name */
1000 get_osx_device_name(id, lpddi->tszProductName, MAX_PATH);
1002 lpddi->guidFFDriver = GUID_NULL;
1003 return S_OK;
1006 return S_FALSE;
1009 static HRESULT joydev_enum_deviceW(DWORD dwDevType, DWORD dwFlags, LPDIDEVICEINSTANCEW lpddi, DWORD version, int id)
1011 char name[MAX_PATH];
1012 char friendly[32];
1013 IOHIDDeviceRef device;
1014 BOOL is_joystick;
1016 TRACE("dwDevType %u dwFlags 0x%08x version 0x%04x id %d\n", dwDevType, dwFlags, version, id);
1018 if (id >= find_joystick_devices()) return E_FAIL;
1020 device = get_device_ref(id);
1022 if ((dwDevType == 0) ||
1023 ((dwDevType == DIDEVTYPE_JOYSTICK) && (version >= 0x0300 && version < 0x0800)) ||
1024 (((dwDevType == DI8DEVCLASS_GAMECTRL) || (dwDevType == DI8DEVTYPE_JOYSTICK)) && (version >= 0x0800))) {
1026 if (dwFlags & DIEDFL_FORCEFEEDBACK) {
1027 if(!device)
1028 return S_FALSE;
1029 if(get_ff(device, NULL) != S_OK)
1030 return S_FALSE;
1032 is_joystick = get_device_property_long(device, CFSTR(kIOHIDDeviceUsageKey)) == kHIDUsage_GD_Joystick;
1033 /* Return joystick */
1034 lpddi->guidInstance = DInput_Wine_OsX_Joystick_GUID;
1035 lpddi->guidInstance.Data3 = id;
1036 lpddi->guidProduct = DInput_PIDVID_Product_GUID;
1037 lpddi->guidProduct.Data1 = make_vid_pid(device);
1038 lpddi->dwDevType = get_device_type(version, is_joystick);
1039 lpddi->dwDevType |= DIDEVTYPE_HID;
1040 lpddi->wUsagePage = 0x01; /* Desktop */
1041 if (is_joystick)
1042 lpddi->wUsage = 0x04; /* Joystick */
1043 else
1044 lpddi->wUsage = 0x05; /* Game Pad */
1045 sprintf(friendly, "Joystick %d", id);
1046 MultiByteToWideChar(CP_ACP, 0, friendly, -1, lpddi->tszInstanceName, MAX_PATH);
1048 /* get the device name */
1049 get_osx_device_name(id, name, MAX_PATH);
1051 MultiByteToWideChar(CP_ACP, 0, name, -1, lpddi->tszProductName, MAX_PATH);
1052 lpddi->guidFFDriver = GUID_NULL;
1053 return S_OK;
1056 return S_FALSE;
1059 static const char *osx_ff_axis_name(UInt8 axis)
1061 static char ret[6];
1062 switch(axis){
1063 case FFJOFS_X:
1064 return "FFJOFS_X";
1065 case FFJOFS_Y:
1066 return "FFJOFS_Y";
1067 case FFJOFS_Z:
1068 return "FFJOFS_Z";
1070 sprintf(ret, "%u", (unsigned int)axis);
1071 return ret;
1074 static BOOL osx_axis_has_ff(FFCAPABILITIES *ffcaps, UInt8 axis)
1076 int i;
1077 for(i = 0; i < ffcaps->numFfAxes; ++i)
1078 if(ffcaps->ffAxes[i] == axis)
1079 return TRUE;
1080 return FALSE;
1083 static HRESULT alloc_device(REFGUID rguid, IDirectInputImpl *dinput,
1084 JoystickImpl **pdev, unsigned short index)
1086 DWORD i;
1087 IOHIDDeviceRef device;
1088 JoystickImpl* newDevice;
1089 char name[MAX_PATH];
1090 HRESULT hr;
1091 LPDIDATAFORMAT df = NULL;
1092 int idx = 0;
1093 int axis_map[8]; /* max axes */
1094 int slider_count = 0;
1095 FFCAPABILITIES ffcaps;
1097 TRACE("%s %p %p %hu\n", debugstr_guid(rguid), dinput, pdev, index);
1099 newDevice = HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(JoystickImpl));
1100 if (newDevice == 0) {
1101 WARN("out of memory\n");
1102 *pdev = 0;
1103 return DIERR_OUTOFMEMORY;
1106 newDevice->id = index;
1108 device = get_device_ref(index);
1110 newDevice->generic.guidInstance = DInput_Wine_OsX_Joystick_GUID;
1111 newDevice->generic.guidInstance.Data3 = index;
1112 newDevice->generic.guidProduct = DInput_PIDVID_Product_GUID;
1113 newDevice->generic.guidProduct.Data1 = make_vid_pid(device);
1114 newDevice->generic.joy_polldev = poll_osx_device_state;
1116 /* get the device name */
1117 get_osx_device_name(index, name, MAX_PATH);
1118 TRACE("Name %s\n",name);
1120 /* copy the device name */
1121 newDevice->generic.name = HeapAlloc(GetProcessHeap(),0,strlen(name) + 1);
1122 strcpy(newDevice->generic.name, name);
1124 list_init(&newDevice->effects);
1125 if(get_ff(device, &newDevice->ff) == S_OK){
1126 newDevice->generic.devcaps.dwFlags |= DIDC_FORCEFEEDBACK;
1128 hr = FFDeviceGetForceFeedbackCapabilities(newDevice->ff, &ffcaps);
1129 if(SUCCEEDED(hr)){
1130 TRACE("FF Capabilities:\n");
1131 TRACE("\tsupportedEffects: 0x%x\n", (unsigned int)ffcaps.supportedEffects);
1132 TRACE("\temulatedEffects: 0x%x\n", (unsigned int)ffcaps.emulatedEffects);
1133 TRACE("\tsubType: 0x%x\n", (unsigned int)ffcaps.subType);
1134 TRACE("\tnumFfAxes: %u\n", (unsigned int)ffcaps.numFfAxes);
1135 TRACE("\tffAxes: [");
1136 for(i = 0; i < ffcaps.numFfAxes; ++i){
1137 TRACE("%s", osx_ff_axis_name(ffcaps.ffAxes[i]));
1138 if(i < ffcaps.numFfAxes - 1)
1139 TRACE(", ");
1141 TRACE("]\n");
1142 TRACE("\tstorageCapacity: %u\n", (unsigned int)ffcaps.storageCapacity);
1143 TRACE("\tplaybackCapacity: %u\n", (unsigned int)ffcaps.playbackCapacity);
1146 hr = FFDeviceSendForceFeedbackCommand(newDevice->ff, FFSFFC_RESET);
1147 if(FAILED(hr))
1148 WARN("FFDeviceSendForceFeedbackCommand(FFSFFC_RESET) failed: %08x\n", hr);
1150 hr = FFDeviceSendForceFeedbackCommand(newDevice->ff, FFSFFC_SETACTUATORSON);
1151 if(FAILED(hr))
1152 WARN("FFDeviceSendForceFeedbackCommand(FFSFFC_SETACTUATORSON) failed: %08x\n", hr);
1155 memset(axis_map, 0, sizeof(axis_map));
1156 get_osx_device_elements(newDevice, axis_map);
1158 TRACE("%i axes %i buttons %i povs\n",newDevice->generic.devcaps.dwAxes,newDevice->generic.devcaps.dwButtons,newDevice->generic.devcaps.dwPOVs);
1160 if (newDevice->generic.devcaps.dwButtons > 128)
1162 WARN("Can't support %d buttons. Clamping down to 128\n", newDevice->generic.devcaps.dwButtons);
1163 newDevice->generic.devcaps.dwButtons = 128;
1166 newDevice->generic.base.IDirectInputDevice8A_iface.lpVtbl = &JoystickAvt;
1167 newDevice->generic.base.IDirectInputDevice8W_iface.lpVtbl = &JoystickWvt;
1168 newDevice->generic.base.ref = 1;
1169 newDevice->generic.base.dinput = dinput;
1170 newDevice->generic.base.guid = *rguid;
1171 InitializeCriticalSection(&newDevice->generic.base.crit);
1172 newDevice->generic.base.crit.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": JoystickImpl*->generic.base.crit");
1174 /* Create copy of default data format */
1175 if (!(df = HeapAlloc(GetProcessHeap(), 0, c_dfDIJoystick2.dwSize))) goto FAILED;
1176 memcpy(df, &c_dfDIJoystick2, c_dfDIJoystick2.dwSize);
1178 df->dwNumObjs = newDevice->generic.devcaps.dwAxes + newDevice->generic.devcaps.dwPOVs + newDevice->generic.devcaps.dwButtons;
1179 if (!(df->rgodf = HeapAlloc(GetProcessHeap(), 0, df->dwNumObjs * df->dwObjSize))) goto FAILED;
1181 for (i = 0; i < newDevice->generic.devcaps.dwAxes; i++)
1183 int wine_obj = -1;
1184 BOOL has_ff = FALSE;
1185 switch (axis_map[i])
1187 case kHIDUsage_GD_X:
1188 wine_obj = 0;
1189 has_ff = (newDevice->ff != 0) && osx_axis_has_ff(&ffcaps, FFJOFS_X);
1190 break;
1191 case kHIDUsage_GD_Y:
1192 wine_obj = 1;
1193 has_ff = (newDevice->ff != 0) && osx_axis_has_ff(&ffcaps, FFJOFS_Y);
1194 break;
1195 case kHIDUsage_GD_Z:
1196 wine_obj = 2;
1197 has_ff = (newDevice->ff != 0) && osx_axis_has_ff(&ffcaps, FFJOFS_Z);
1198 break;
1199 case kHIDUsage_GD_Rx:
1200 wine_obj = 3;
1201 has_ff = (newDevice->ff != 0) && osx_axis_has_ff(&ffcaps, FFJOFS_RX);
1202 break;
1203 case kHIDUsage_GD_Ry:
1204 wine_obj = 4;
1205 has_ff = (newDevice->ff != 0) && osx_axis_has_ff(&ffcaps, FFJOFS_RY);
1206 break;
1207 case kHIDUsage_GD_Rz:
1208 wine_obj = 5;
1209 has_ff = (newDevice->ff != 0) && osx_axis_has_ff(&ffcaps, FFJOFS_RZ);
1210 break;
1211 case kHIDUsage_GD_Slider:
1212 wine_obj = 6 + slider_count;
1213 has_ff = (newDevice->ff != 0) && osx_axis_has_ff(&ffcaps, FFJOFS_SLIDER(slider_count));
1214 slider_count++;
1215 break;
1217 if (wine_obj < 0 ) continue;
1219 memcpy(&df->rgodf[idx], &c_dfDIJoystick2.rgodf[wine_obj], df->dwObjSize);
1220 df->rgodf[idx].dwType = DIDFT_MAKEINSTANCE(wine_obj) | DIDFT_ABSAXIS;
1221 if(has_ff)
1222 df->rgodf[idx].dwFlags |= DIDOI_FFACTUATOR;
1223 ++idx;
1226 for (i = 0; i < newDevice->generic.devcaps.dwPOVs; i++)
1228 memcpy(&df->rgodf[idx], &c_dfDIJoystick2.rgodf[i + 8], df->dwObjSize);
1229 df->rgodf[idx++].dwType = DIDFT_MAKEINSTANCE(i) | DIDFT_POV;
1232 for (i = 0; i < newDevice->generic.devcaps.dwButtons; i++)
1234 memcpy(&df->rgodf[idx], &c_dfDIJoystick2.rgodf[i + 12], df->dwObjSize);
1235 df->rgodf[idx ].pguid = &GUID_Button;
1236 df->rgodf[idx++].dwType = DIDFT_MAKEINSTANCE(i) | DIDFT_PSHBUTTON;
1238 newDevice->generic.base.data_format.wine_df = df;
1240 /* initialize default properties */
1241 get_osx_device_elements_props(newDevice);
1243 IDirectInput_AddRef(&newDevice->generic.base.dinput->IDirectInput7A_iface);
1245 newDevice->generic.devcaps.dwSize = sizeof(newDevice->generic.devcaps);
1246 newDevice->generic.devcaps.dwFlags |= DIDC_ATTACHED;
1247 if (newDevice->generic.base.dinput->dwVersion >= 0x0800)
1248 newDevice->generic.devcaps.dwDevType = DI8DEVTYPE_JOYSTICK | (DI8DEVTYPEJOYSTICK_STANDARD << 8);
1249 else
1250 newDevice->generic.devcaps.dwDevType = DIDEVTYPE_JOYSTICK | (DIDEVTYPEJOYSTICK_TRADITIONAL << 8);
1251 newDevice->generic.devcaps.dwFFSamplePeriod = 0;
1252 newDevice->generic.devcaps.dwFFMinTimeResolution = 0;
1253 newDevice->generic.devcaps.dwFirmwareRevision = 0;
1254 newDevice->generic.devcaps.dwHardwareRevision = 0;
1255 newDevice->generic.devcaps.dwFFDriverVersion = 0;
1257 if (TRACE_ON(dinput)) {
1258 TRACE("allocated device %p\n", newDevice);
1259 _dump_DIDATAFORMAT(newDevice->generic.base.data_format.wine_df);
1260 _dump_DIDEVCAPS(&newDevice->generic.devcaps);
1263 *pdev = newDevice;
1265 return DI_OK;
1267 FAILED:
1268 hr = DIERR_OUTOFMEMORY;
1269 if (newDevice->ff) FFReleaseDevice(newDevice->ff);
1270 if (newDevice->elements) CFRelease(newDevice->elements);
1271 if (df) HeapFree(GetProcessHeap(), 0, df->rgodf);
1272 HeapFree(GetProcessHeap(), 0, df);
1273 release_DataFormat(&newDevice->generic.base.data_format);
1274 HeapFree(GetProcessHeap(),0,newDevice->generic.name);
1275 HeapFree(GetProcessHeap(),0,newDevice);
1276 *pdev = 0;
1278 return hr;
1281 /******************************************************************************
1282 * get_joystick_index : Get the joystick index from a given GUID
1284 static unsigned short get_joystick_index(REFGUID guid)
1286 GUID wine_joystick = DInput_Wine_OsX_Joystick_GUID;
1287 GUID dev_guid = *guid;
1289 wine_joystick.Data3 = 0;
1290 dev_guid.Data3 = 0;
1292 /* for the standard joystick GUID use index 0 */
1293 if(IsEqualGUID(&GUID_Joystick,guid)) return 0;
1295 /* for the wine joystick GUIDs use the index stored in Data3 */
1296 if(IsEqualGUID(&wine_joystick, &dev_guid)) return guid->Data3;
1298 return 0xffff;
1301 static HRESULT joydev_create_device(IDirectInputImpl *dinput, REFGUID rguid, REFIID riid, LPVOID *pdev, int unicode)
1303 unsigned short index;
1304 int joystick_devices_count;
1306 TRACE("%p %s %s %p %i\n", dinput, debugstr_guid(rguid), debugstr_guid(riid), pdev, unicode);
1307 *pdev = NULL;
1309 if ((joystick_devices_count = find_joystick_devices()) == 0)
1310 return DIERR_DEVICENOTREG;
1312 if ((index = get_joystick_index(rguid)) < 0xffff &&
1313 joystick_devices_count && index < joystick_devices_count)
1315 JoystickImpl *This;
1316 HRESULT hr;
1318 if (riid == NULL)
1319 ;/* nothing */
1320 else if (IsEqualGUID(&IID_IDirectInputDeviceA, riid) ||
1321 IsEqualGUID(&IID_IDirectInputDevice2A, riid) ||
1322 IsEqualGUID(&IID_IDirectInputDevice7A, riid) ||
1323 IsEqualGUID(&IID_IDirectInputDevice8A, riid))
1325 unicode = 0;
1327 else if (IsEqualGUID(&IID_IDirectInputDeviceW, riid) ||
1328 IsEqualGUID(&IID_IDirectInputDevice2W, riid) ||
1329 IsEqualGUID(&IID_IDirectInputDevice7W, riid) ||
1330 IsEqualGUID(&IID_IDirectInputDevice8W, riid))
1332 unicode = 1;
1334 else
1336 WARN("no interface\n");
1337 return DIERR_NOINTERFACE;
1340 hr = alloc_device(rguid, dinput, &This, index);
1341 if (!This) return hr;
1343 if (unicode)
1344 *pdev = &This->generic.base.IDirectInputDevice8W_iface;
1345 else
1346 *pdev = &This->generic.base.IDirectInputDevice8A_iface;
1347 return hr;
1350 return DIERR_DEVICENOTREG;
1353 static HRESULT WINAPI JoystickWImpl_GetProperty(LPDIRECTINPUTDEVICE8W iface, REFGUID rguid, LPDIPROPHEADER pdiph)
1355 JoystickImpl *This = impl_from_IDirectInputDevice8W(iface);
1357 TRACE("(%p)->(%s,%p)\n", This, debugstr_guid(rguid), pdiph);
1358 _dump_DIPROPHEADER(pdiph);
1360 if (!IS_DIPROP(rguid)) return DI_OK;
1362 switch (LOWORD(rguid)) {
1363 case (DWORD_PTR) DIPROP_GUIDANDPATH:
1365 static const WCHAR formatW[] = {'\\','\\','?','\\','h','i','d','#','v','i','d','_','%','0','4','x','&',
1366 'p','i','d','_','%','0','4','x','&','%','s','_','%','i',0};
1367 static const WCHAR miW[] = {'m','i',0};
1368 static const WCHAR igW[] = {'i','g',0};
1370 BOOL is_gamepad;
1371 IOHIDDeviceRef device = get_device_ref(This->id);
1372 LPDIPROPGUIDANDPATH pd = (LPDIPROPGUIDANDPATH)pdiph;
1373 WORD vid = get_device_property_long(device, CFSTR(kIOHIDVendorIDKey));
1374 WORD pid = get_device_property_long(device, CFSTR(kIOHIDProductIDKey));
1376 if (!pid || !vid)
1377 return DIERR_UNSUPPORTED;
1379 is_gamepad = is_xinput_device(&This->generic.devcaps, vid, pid);
1380 pd->guidClass = GUID_DEVCLASS_HIDCLASS;
1381 sprintfW(pd->wszPath, formatW, vid, pid, is_gamepad ? igW : miW, This->id);
1383 TRACE("DIPROP_GUIDANDPATH(%s, %s): returning fake path\n", debugstr_guid(&pd->guidClass), debugstr_w(pd->wszPath));
1384 break;
1387 default:
1388 return JoystickWGenericImpl_GetProperty(iface, rguid, pdiph);
1391 return DI_OK;
1394 static HRESULT WINAPI JoystickAImpl_GetProperty(LPDIRECTINPUTDEVICE8A iface, REFGUID rguid, LPDIPROPHEADER pdiph)
1396 JoystickImpl *This = impl_from_IDirectInputDevice8A(iface);
1397 return JoystickWImpl_GetProperty(IDirectInputDevice8W_from_impl(This), rguid, pdiph);
1400 static HRESULT osx_set_autocenter(JoystickImpl *This,
1401 const DIPROPDWORD *header)
1403 UInt32 v;
1404 HRESULT hr;
1405 if(!This->ff)
1406 return DIERR_UNSUPPORTED;
1407 v = header->dwData;
1408 hr = osx_to_win32_hresult(FFDeviceSetForceFeedbackProperty(This->ff, FFPROP_AUTOCENTER, &v));
1409 TRACE("returning: %08x\n", hr);
1410 return hr;
1413 static HRESULT osx_set_ffgain(JoystickImpl *This, const DIPROPDWORD *header)
1415 UInt32 v;
1416 HRESULT hr;
1417 if(!This->ff)
1418 return DIERR_UNSUPPORTED;
1419 v = header->dwData;
1420 hr = osx_to_win32_hresult(FFDeviceSetForceFeedbackProperty(This->ff, FFPROP_FFGAIN, &v));
1421 TRACE("returning: %08x\n", hr);
1422 return hr;
1425 static HRESULT WINAPI JoystickWImpl_SetProperty(IDirectInputDevice8W *iface,
1426 const GUID *prop, const DIPROPHEADER *header)
1428 JoystickImpl *This = impl_from_IDirectInputDevice8W(iface);
1430 TRACE("%p %s %p\n", This, debugstr_guid(prop), header);
1432 switch(LOWORD(prop))
1434 case (DWORD_PTR)DIPROP_AUTOCENTER:
1435 return osx_set_autocenter(This, (const DIPROPDWORD *)header);
1436 case (DWORD_PTR)DIPROP_FFGAIN:
1437 return osx_set_ffgain(This, (const DIPROPDWORD *)header);
1440 return JoystickWGenericImpl_SetProperty(iface, prop, header);
1443 static HRESULT WINAPI JoystickAImpl_SetProperty(IDirectInputDevice8A *iface,
1444 const GUID *prop, const DIPROPHEADER *header)
1446 JoystickImpl *This = impl_from_IDirectInputDevice8A(iface);
1448 TRACE("%p %s %p\n", This, debugstr_guid(prop), header);
1450 switch(LOWORD(prop))
1452 case (DWORD_PTR)DIPROP_AUTOCENTER:
1453 return osx_set_autocenter(This, (const DIPROPDWORD *)header);
1454 case (DWORD_PTR)DIPROP_FFGAIN:
1455 return osx_set_ffgain(This, (const DIPROPDWORD *)header);
1458 return JoystickAGenericImpl_SetProperty(iface, prop, header);
1461 static CFUUIDRef effect_win_to_mac(const GUID *effect)
1463 #define DO_MAP(X) \
1464 if(IsEqualGUID(&GUID_##X, effect)) \
1465 return kFFEffectType_##X##_ID;
1466 DO_MAP(ConstantForce)
1467 DO_MAP(RampForce)
1468 DO_MAP(Square)
1469 DO_MAP(Sine)
1470 DO_MAP(Triangle)
1471 DO_MAP(SawtoothUp)
1472 DO_MAP(SawtoothDown)
1473 DO_MAP(Spring)
1474 DO_MAP(Damper)
1475 DO_MAP(Inertia)
1476 DO_MAP(Friction)
1477 DO_MAP(CustomForce)
1478 #undef DO_MAP
1479 WARN("Unknown effect GUID! %s\n", debugstr_guid(effect));
1480 return 0;
1483 static HRESULT WINAPI JoystickWImpl_CreateEffect(IDirectInputDevice8W *iface,
1484 const GUID *type, const DIEFFECT *params, IDirectInputEffect **out,
1485 IUnknown *outer)
1487 JoystickImpl *This = impl_from_IDirectInputDevice8W(iface);
1488 EffectImpl *effect;
1489 HRESULT hr;
1491 TRACE("(%p)->(%s %p %p %p)\n", This, debugstr_guid(type), params, out, outer);
1492 dump_DIEFFECT(params, type, 0);
1494 if(!This->ff){
1495 TRACE("No force feedback support\n");
1496 *out = NULL;
1497 return DIERR_UNSUPPORTED;
1500 if(outer)
1501 WARN("aggregation not implemented\n");
1503 effect = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*This));
1504 effect->IDirectInputEffect_iface.lpVtbl = &EffectVtbl;
1505 effect->ref = 1;
1506 effect->guid = *type;
1507 effect->device = This;
1509 /* Mac's FFEFFECT and Win's DIEFFECT are binary identical. */
1510 hr = osx_to_win32_hresult(FFDeviceCreateEffect(This->ff,
1511 effect_win_to_mac(type), (FFEFFECT*)params, &effect->effect));
1512 if(FAILED(hr)){
1513 WARN("FFDeviceCreateEffect failed: %08x\n", hr);
1514 HeapFree(GetProcessHeap(), 0, effect);
1515 return hr;
1518 list_add_tail(&This->effects, &effect->entry);
1519 *out = &effect->IDirectInputEffect_iface;
1521 TRACE("allocated effect: %p\n", effect);
1523 return S_OK;
1526 static HRESULT WINAPI JoystickAImpl_CreateEffect(IDirectInputDevice8A *iface,
1527 const GUID *type, const DIEFFECT *params, IDirectInputEffect **out,
1528 IUnknown *outer)
1530 JoystickImpl *This = impl_from_IDirectInputDevice8A(iface);
1532 TRACE("(%p)->(%s %p %p %p)\n", This, debugstr_guid(type), params, out, outer);
1534 return JoystickWImpl_CreateEffect(&This->generic.base.IDirectInputDevice8W_iface,
1535 type, params, out, outer);
1538 static HRESULT WINAPI JoystickWImpl_SendForceFeedbackCommand(IDirectInputDevice8W *iface,
1539 DWORD flags)
1541 JoystickImpl *This = impl_from_IDirectInputDevice8W(iface);
1542 HRESULT hr;
1544 TRACE("%p 0x%x\n", This, flags);
1546 if(!This->ff)
1547 return DI_NOEFFECT;
1549 hr = osx_to_win32_hresult(FFDeviceSendForceFeedbackCommand(This->ff, flags));
1550 if(FAILED(hr)){
1551 WARN("FFDeviceSendForceFeedbackCommand failed: %08x\n", hr);
1552 return hr;
1555 return S_OK;
1558 static HRESULT WINAPI JoystickAImpl_SendForceFeedbackCommand(IDirectInputDevice8A *iface,
1559 DWORD flags)
1561 JoystickImpl *This = impl_from_IDirectInputDevice8A(iface);
1563 TRACE("%p 0x%x\n", This, flags);
1565 return JoystickWImpl_SendForceFeedbackCommand(&This->generic.base.IDirectInputDevice8W_iface, flags);
1568 const struct dinput_device joystick_osx_device = {
1569 "Wine OS X joystick driver",
1570 joydev_enum_deviceA,
1571 joydev_enum_deviceW,
1572 joydev_create_device
1575 static const IDirectInputDevice8AVtbl JoystickAvt =
1577 IDirectInputDevice2AImpl_QueryInterface,
1578 IDirectInputDevice2AImpl_AddRef,
1579 IDirectInputDevice2AImpl_Release,
1580 JoystickAGenericImpl_GetCapabilities,
1581 IDirectInputDevice2AImpl_EnumObjects,
1582 JoystickAImpl_GetProperty,
1583 JoystickAImpl_SetProperty,
1584 IDirectInputDevice2AImpl_Acquire,
1585 IDirectInputDevice2AImpl_Unacquire,
1586 JoystickAGenericImpl_GetDeviceState,
1587 IDirectInputDevice2AImpl_GetDeviceData,
1588 IDirectInputDevice2AImpl_SetDataFormat,
1589 IDirectInputDevice2AImpl_SetEventNotification,
1590 IDirectInputDevice2AImpl_SetCooperativeLevel,
1591 JoystickAGenericImpl_GetObjectInfo,
1592 JoystickAGenericImpl_GetDeviceInfo,
1593 IDirectInputDevice2AImpl_RunControlPanel,
1594 IDirectInputDevice2AImpl_Initialize,
1595 JoystickAImpl_CreateEffect,
1596 IDirectInputDevice2AImpl_EnumEffects,
1597 IDirectInputDevice2AImpl_GetEffectInfo,
1598 IDirectInputDevice2AImpl_GetForceFeedbackState,
1599 JoystickAImpl_SendForceFeedbackCommand,
1600 IDirectInputDevice2AImpl_EnumCreatedEffectObjects,
1601 IDirectInputDevice2AImpl_Escape,
1602 JoystickAGenericImpl_Poll,
1603 IDirectInputDevice2AImpl_SendDeviceData,
1604 IDirectInputDevice7AImpl_EnumEffectsInFile,
1605 IDirectInputDevice7AImpl_WriteEffectToFile,
1606 JoystickAGenericImpl_BuildActionMap,
1607 JoystickAGenericImpl_SetActionMap,
1608 IDirectInputDevice8AImpl_GetImageInfo
1611 static const IDirectInputDevice8WVtbl JoystickWvt =
1613 IDirectInputDevice2WImpl_QueryInterface,
1614 IDirectInputDevice2WImpl_AddRef,
1615 IDirectInputDevice2WImpl_Release,
1616 JoystickWGenericImpl_GetCapabilities,
1617 IDirectInputDevice2WImpl_EnumObjects,
1618 JoystickWImpl_GetProperty,
1619 JoystickWImpl_SetProperty,
1620 IDirectInputDevice2WImpl_Acquire,
1621 IDirectInputDevice2WImpl_Unacquire,
1622 JoystickWGenericImpl_GetDeviceState,
1623 IDirectInputDevice2WImpl_GetDeviceData,
1624 IDirectInputDevice2WImpl_SetDataFormat,
1625 IDirectInputDevice2WImpl_SetEventNotification,
1626 IDirectInputDevice2WImpl_SetCooperativeLevel,
1627 JoystickWGenericImpl_GetObjectInfo,
1628 JoystickWGenericImpl_GetDeviceInfo,
1629 IDirectInputDevice2WImpl_RunControlPanel,
1630 IDirectInputDevice2WImpl_Initialize,
1631 JoystickWImpl_CreateEffect,
1632 IDirectInputDevice2WImpl_EnumEffects,
1633 IDirectInputDevice2WImpl_GetEffectInfo,
1634 IDirectInputDevice2WImpl_GetForceFeedbackState,
1635 JoystickWImpl_SendForceFeedbackCommand,
1636 IDirectInputDevice2WImpl_EnumCreatedEffectObjects,
1637 IDirectInputDevice2WImpl_Escape,
1638 JoystickWGenericImpl_Poll,
1639 IDirectInputDevice2WImpl_SendDeviceData,
1640 IDirectInputDevice7WImpl_EnumEffectsInFile,
1641 IDirectInputDevice7WImpl_WriteEffectToFile,
1642 JoystickWGenericImpl_BuildActionMap,
1643 JoystickWGenericImpl_SetActionMap,
1644 IDirectInputDevice8WImpl_GetImageInfo
1647 static HRESULT WINAPI effect_QueryInterface(IDirectInputEffect *iface,
1648 const GUID *guid, void **out)
1650 EffectImpl *This = impl_from_IDirectInputEffect(iface);
1652 TRACE("%p %s %p\n", This, debugstr_guid(guid), out);
1654 if(IsEqualIID(guid, &IID_IUnknown) || IsEqualIID(guid, &IID_IDirectInputEffect)){
1655 *out = iface;
1656 IDirectInputEffect_AddRef(iface);
1657 return S_OK;
1660 return E_NOINTERFACE;
1663 static ULONG WINAPI effect_AddRef(IDirectInputEffect *iface)
1665 EffectImpl *This = impl_from_IDirectInputEffect(iface);
1666 ULONG ref = InterlockedIncrement(&This->ref);
1667 TRACE("%p, ref is now: %u\n", This, ref);
1668 return ref;
1671 static ULONG WINAPI effect_Release(IDirectInputEffect *iface)
1673 EffectImpl *This = impl_from_IDirectInputEffect(iface);
1674 ULONG ref = InterlockedDecrement(&This->ref);
1675 TRACE("%p, ref is now: %u\n", This, ref);
1677 if(!ref){
1678 list_remove(&This->entry);
1679 FFDeviceReleaseEffect(This->device->ff, This->effect);
1680 HeapFree(GetProcessHeap(), 0, This);
1683 return ref;
1686 static HRESULT WINAPI effect_Initialize(IDirectInputEffect *iface, HINSTANCE hinst,
1687 DWORD version, const GUID *guid)
1689 EffectImpl *This = impl_from_IDirectInputEffect(iface);
1690 TRACE("%p %p 0x%x, %s\n", This, hinst, version, debugstr_guid(guid));
1691 return S_OK;
1694 static HRESULT WINAPI effect_GetEffectGuid(IDirectInputEffect *iface, GUID *out)
1696 EffectImpl *This = impl_from_IDirectInputEffect(iface);
1697 TRACE("%p %p\n", This, out);
1698 *out = This->guid;
1699 return S_OK;
1702 static HRESULT WINAPI effect_GetParameters(IDirectInputEffect *iface,
1703 DIEFFECT *effect, DWORD flags)
1705 EffectImpl *This = impl_from_IDirectInputEffect(iface);
1706 TRACE("%p %p 0x%x\n", This, effect, flags);
1707 return osx_to_win32_hresult(FFEffectGetParameters(This->effect, (FFEFFECT*)effect, flags));
1710 static HRESULT WINAPI effect_SetParameters(IDirectInputEffect *iface,
1711 const DIEFFECT *effect, DWORD flags)
1713 EffectImpl *This = impl_from_IDirectInputEffect(iface);
1714 TRACE("%p %p 0x%x\n", This, effect, flags);
1715 dump_DIEFFECT(effect, &This->guid, flags);
1716 return osx_to_win32_hresult(FFEffectSetParameters(This->effect, (FFEFFECT*)effect, flags));
1719 static HRESULT WINAPI effect_Start(IDirectInputEffect *iface, DWORD iterations,
1720 DWORD flags)
1722 EffectImpl *This = impl_from_IDirectInputEffect(iface);
1723 TRACE("%p 0x%x 0x%x\n", This, iterations, flags);
1724 return osx_to_win32_hresult(FFEffectStart(This->effect, iterations, flags));
1727 static HRESULT WINAPI effect_Stop(IDirectInputEffect *iface)
1729 EffectImpl *This = impl_from_IDirectInputEffect(iface);
1730 TRACE("%p\n", This);
1731 return osx_to_win32_hresult(FFEffectStop(This->effect));
1734 static HRESULT WINAPI effect_GetEffectStatus(IDirectInputEffect *iface, DWORD *flags)
1736 EffectImpl *This = impl_from_IDirectInputEffect(iface);
1737 TRACE("%p %p\n", This, flags);
1738 return osx_to_win32_hresult(FFEffectGetEffectStatus(This->effect, (UInt32*)flags));
1741 static HRESULT WINAPI effect_Download(IDirectInputEffect *iface)
1743 EffectImpl *This = impl_from_IDirectInputEffect(iface);
1744 TRACE("%p\n", This);
1745 return osx_to_win32_hresult(FFEffectDownload(This->effect));
1748 static HRESULT WINAPI effect_Unload(IDirectInputEffect *iface)
1750 EffectImpl *This = impl_from_IDirectInputEffect(iface);
1751 TRACE("%p\n", This);
1752 return osx_to_win32_hresult(FFEffectUnload(This->effect));
1755 static HRESULT WINAPI effect_Escape(IDirectInputEffect *iface, DIEFFESCAPE *escape)
1757 EffectImpl *This = impl_from_IDirectInputEffect(iface);
1758 TRACE("%p %p\n", This, escape);
1759 return osx_to_win32_hresult(FFEffectEscape(This->effect, (FFEFFESCAPE*)escape));
1762 static const IDirectInputEffectVtbl EffectVtbl = {
1763 effect_QueryInterface,
1764 effect_AddRef,
1765 effect_Release,
1766 effect_Initialize,
1767 effect_GetEffectGuid,
1768 effect_GetParameters,
1769 effect_SetParameters,
1770 effect_Start,
1771 effect_Stop,
1772 effect_GetEffectStatus,
1773 effect_Download,
1774 effect_Unload,
1775 effect_Escape
1778 #else /* HAVE_IOHIDMANAGERCREATE */
1780 const struct dinput_device joystick_osx_device = {
1781 "Wine OS X joystick driver",
1782 NULL,
1783 NULL,
1784 NULL
1787 #endif /* HAVE_IOHIDMANAGERCREATE */