xref: /reactos/dll/directx/wine/dinput/joystick_osx.c (revision f986527d)
1 /*  DirectInput Joystick device for Mac OS/X
2  *
3  * Copyright 1998 Marcus Meissner
4  * Copyright 1998,1999 Lionel Ulmer
5  * Copyright 2000-2001 TransGaming Technologies Inc.
6  * Copyright 2009 CodeWeavers, Aric Stewart
7  *
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.
12  *
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.
17  *
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
21  */
22 
23 #include "config.h"
24 #include "wine/port.h"
25 
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 */
81 
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"
90 
91 #include "dinput_private.h"
92 #include "device_private.h"
93 #include "joystick_private.h"
94 
95 #ifdef HAVE_IOHIDMANAGERCREATE
96 
97 WINE_DEFAULT_DEBUG_CHANNEL(dinput);
98 
99 static CFMutableArrayRef device_main_elements = NULL;
100 
101 typedef struct JoystickImpl JoystickImpl;
102 static const IDirectInputDevice8AVtbl JoystickAvt;
103 static const IDirectInputDevice8WVtbl JoystickWvt;
104 
105 struct JoystickImpl
106 {
107     struct JoystickGenericImpl generic;
108 
109     /* osx private */
110     int                    id;
111     CFArrayRef             elements;
112     ObjProps               **propmap;
113     FFDeviceObjectReference ff;
114     struct list effects;
115 };
116 
117 static inline JoystickImpl *impl_from_IDirectInputDevice8A(IDirectInputDevice8A *iface)
118 {
119     return CONTAINING_RECORD(CONTAINING_RECORD(CONTAINING_RECORD(iface, IDirectInputDeviceImpl, IDirectInputDevice8A_iface),
120            JoystickGenericImpl, base), JoystickImpl, generic);
121 }
122 static inline JoystickImpl *impl_from_IDirectInputDevice8W(IDirectInputDevice8W *iface)
123 {
124     return CONTAINING_RECORD(CONTAINING_RECORD(CONTAINING_RECORD(iface, IDirectInputDeviceImpl, IDirectInputDevice8W_iface),
125            JoystickGenericImpl, base), JoystickImpl, generic);
126 }
127 
128 static inline IDirectInputDevice8W *IDirectInputDevice8W_from_impl(JoystickImpl *This)
129 {
130     return &This->generic.base.IDirectInputDevice8W_iface;
131 }
132 
133 typedef struct _EffectImpl {
134     IDirectInputEffect IDirectInputEffect_iface;
135     LONG ref;
136 
137     JoystickImpl *device;
138     FFEffectObjectReference effect;
139     GUID guid;
140 
141     struct list entry;
142 } EffectImpl;
143 
144 static EffectImpl *impl_from_IDirectInputEffect(IDirectInputEffect *iface)
145 {
146     return CONTAINING_RECORD(iface, EffectImpl, IDirectInputEffect_iface);
147 }
148 
149 static const IDirectInputEffectVtbl EffectVtbl;
150 
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}
153 };
154 
155 static HRESULT osx_to_win32_hresult(HRESULT in)
156 {
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;
180     }
181     return in;
182 }
183 
184 static long get_device_property_long(IOHIDDeviceRef device, CFStringRef key)
185 {
186     CFTypeRef ref;
187     long result = 0;
188 
189     if (device)
190     {
191         assert(IOHIDDeviceGetTypeID() == CFGetTypeID(device));
192 
193         ref = IOHIDDeviceGetProperty(device, key);
194 
195         if (ref && CFNumberGetTypeID() == CFGetTypeID(ref))
196             CFNumberGetValue((CFNumberRef)ref, kCFNumberLongType, &result);
197     }
198 
199     return result;
200 }
201 
202 static CFStringRef copy_device_name(IOHIDDeviceRef device)
203 {
204     CFStringRef name;
205 
206     if (device)
207     {
208         CFTypeRef ref_name;
209 
210         assert(IOHIDDeviceGetTypeID() == CFGetTypeID(device));
211 
212         ref_name = IOHIDDeviceGetProperty(device, CFSTR(kIOHIDProductKey));
213 
214         if (ref_name && CFStringGetTypeID() == CFGetTypeID(ref_name))
215             name = CFStringCreateCopy(kCFAllocatorDefault, ref_name);
216         else
217         {
218             long vendID, prodID;
219 
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);
223         }
224     }
225     else
226     {
227         ERR("NULL device\n");
228         name = CFStringCreateCopy(kCFAllocatorDefault, CFSTR(""));
229     }
230 
231     return name;
232 }
233 
234 static long get_device_location_ID(IOHIDDeviceRef device)
235 {
236     return get_device_property_long(device, CFSTR(kIOHIDLocationIDKey));
237 }
238 
239 static void copy_set_to_array(const void *value, void *context)
240 {
241     CFArrayAppendValue(context, value);
242 }
243 
244 static CFComparisonResult device_name_comparator(IOHIDDeviceRef device1, IOHIDDeviceRef device2)
245 {
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;
251 }
252 
253 static CFComparisonResult device_location_name_comparator(const void *val1, const void *val2, void *context)
254 {
255     IOHIDDeviceRef device1 = (IOHIDDeviceRef)val1, device2 = (IOHIDDeviceRef)val2;
256     long loc1 = get_device_location_ID(device1), loc2 = get_device_location_ID(device2);
257 
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);
264 }
265 
266 static const char* debugstr_cf(CFTypeRef t)
267 {
268     CFStringRef s;
269     const char* ret;
270 
271     if (!t) return "(null)";
272 
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)
280     {
281         const UniChar* u = CFStringGetCharactersPtr(s);
282         if (u)
283             ret = debugstr_wn((const WCHAR*)u, CFStringGetLength(s));
284     }
285     if (!ret)
286     {
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);
291     }
292     if (s != t) CFRelease(s);
293     return ret;
294 }
295 
296 static const char* debugstr_device(IOHIDDeviceRef device)
297 {
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));
301 }
302 
303 static const char* debugstr_element(IOHIDElementRef element)
304 {
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));
308 }
309 
310 static IOHIDDeviceRef get_device_ref(int id)
311 {
312     IOHIDElementRef device_main_element;
313     IOHIDDeviceRef hid_device;
314 
315     TRACE("id %d\n", id);
316 
317     if (!device_main_elements || id >= CFArrayGetCount(device_main_elements))
318         return 0;
319 
320     device_main_element = (IOHIDElementRef)CFArrayGetValueAtIndex(device_main_elements, id);
321     if (!device_main_element)
322     {
323         ERR("Invalid Element requested %i\n",id);
324         return 0;
325     }
326 
327     hid_device = IOHIDElementGetDevice(device_main_element);
328     if (!hid_device)
329     {
330         ERR("Invalid Device requested %i\n",id);
331         return 0;
332     }
333 
334     TRACE("-> %s\n", debugstr_device(hid_device));
335     return hid_device;
336 }
337 
338 static HRESULT get_ff(IOHIDDeviceRef device, FFDeviceObjectReference *ret)
339 {
340     io_service_t service;
341     CFMutableDictionaryRef matching;
342     CFTypeRef location_id;
343     HRESULT hr;
344 
345     TRACE("device %s\n", debugstr_device(device));
346 
347     matching = IOServiceMatching(kIOHIDDeviceKey);
348     if(!matching){
349         WARN("IOServiceMatching failed, force feedback disabled\n");
350         return DIERR_DEVICENOTREG;
351     }
352 
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;
358     }
359 
360     CFDictionaryAddValue(matching, CFSTR(kIOHIDLocationIDKey), location_id);
361 
362     service = IOServiceGetMatchingService(kIOMasterPortDefault, matching);
363 
364     if (ret)
365         hr = osx_to_win32_hresult(FFCreateDevice(service, ret));
366     else
367         hr = FFIsForceFeedback(service) == FF_OK ? S_OK : S_FALSE;
368 
369     IOObjectRelease(service);
370     TRACE("-> hr 0x%08x *ret %p\n", hr, ret ? *ret : NULL);
371     return hr;
372 }
373 
374 static CFMutableDictionaryRef create_osx_device_match(int usage)
375 {
376     CFMutableDictionaryRef result;
377 
378     TRACE("usage %d\n", usage);
379 
380     result = CFDictionaryCreateMutable( kCFAllocatorDefault, 0,
381             &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks );
382 
383     if ( result )
384     {
385         int number = kHIDPage_GenericDesktop;
386         CFNumberRef page = CFNumberCreate( kCFAllocatorDefault,
387                           kCFNumberIntType, &number);
388 
389         if (page)
390         {
391             CFNumberRef cf_usage;
392 
393             CFDictionarySetValue( result, CFSTR( kIOHIDDeviceUsagePageKey ), page );
394             CFRelease( page );
395 
396             cf_usage = CFNumberCreate( kCFAllocatorDefault,
397                         kCFNumberIntType, &usage);
398             if (cf_usage)
399             {
400                 CFDictionarySetValue( result, CFSTR( kIOHIDDeviceUsageKey ), cf_usage );
401                 CFRelease( cf_usage );
402             }
403             else
404             {
405                 ERR("CFNumberCreate() failed.\n");
406                 CFRelease(result);
407                 return NULL;
408             }
409         }
410         else
411         {
412             ERR("CFNumberCreate failed.\n");
413             CFRelease(result);
414             return NULL;
415         }
416     }
417     else
418     {
419         ERR("CFDictionaryCreateMutable failed.\n");
420         return NULL;
421     }
422 
423     return result;
424 }
425 
426 static CFIndex find_top_level(IOHIDDeviceRef hid_device, CFMutableArrayRef main_elements)
427 {
428     CFArrayRef      elements;
429     CFIndex         total = 0;
430 
431     TRACE("hid_device %s\n", debugstr_device(hid_device));
432 
433     if (!hid_device)
434         return 0;
435 
436     elements = IOHIDDeviceCopyMatchingElements(hid_device, NULL, 0);
437 
438     if (elements)
439     {
440         CFIndex idx, cnt = CFArrayGetCount(elements);
441         for (idx=0; idx<cnt; idx++)
442         {
443             IOHIDElementRef element = (IOHIDElementRef)CFArrayGetValueAtIndex(elements, idx);
444             int type = IOHIDElementGetType(element);
445 
446             TRACE("element %s\n", debugstr_element(element));
447 
448             /* Check for top-level gaming device collections */
449             if (type == kIOHIDElementTypeCollection && IOHIDElementGetParent(element) == 0)
450             {
451                 int usage_page = IOHIDElementGetUsagePage(element);
452                 int usage = IOHIDElementGetUsage(element);
453 
454                 if (usage_page == kHIDPage_GenericDesktop &&
455                     (usage == kHIDUsage_GD_Joystick || usage == kHIDUsage_GD_GamePad))
456                 {
457                     CFArrayAppendValue(main_elements, element);
458                     total++;
459                 }
460             }
461         }
462         CFRelease(elements);
463     }
464 
465     TRACE("-> total %d\n", (int)total);
466     return total;
467 }
468 
469 static void get_element_children(IOHIDElementRef element, CFMutableArrayRef all_children)
470 {
471     CFIndex    idx, cnt;
472     CFArrayRef element_children = IOHIDElementGetChildren(element);
473 
474     TRACE("element %s\n", debugstr_element(element));
475 
476     cnt = CFArrayGetCount(element_children);
477 
478     /* Either add the element to the array or grab its children */
479     for (idx=0; idx<cnt; idx++)
480     {
481         IOHIDElementRef child;
482 
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);
489     }
490 }
491 
492 static int find_osx_devices(void)
493 {
494     IOHIDManagerRef hid_manager;
495     CFMutableDictionaryRef result;
496     CFSetRef devset;
497     CFMutableArrayRef matching;
498 
499     TRACE("()\n");
500 
501     hid_manager = IOHIDManagerCreate( kCFAllocatorDefault, 0L );
502     if (IOHIDManagerOpen( hid_manager, 0 ) != kIOReturnSuccess)
503     {
504         ERR("Couldn't open IOHIDManager.\n");
505         CFRelease( hid_manager );
506         return 0;
507     }
508 
509      matching = CFArrayCreateMutable( kCFAllocatorDefault, 0,
510                         &kCFTypeArrayCallBacks );
511 
512     /* build matching dictionary */
513     result = create_osx_device_match(kHIDUsage_GD_Joystick);
514     if (!result)
515     {
516         CFRelease(matching);
517         goto fail;
518     }
519     CFArrayAppendValue( matching, result );
520     CFRelease( result );
521     result = create_osx_device_match(kHIDUsage_GD_GamePad);
522     if (!result)
523     {
524         CFRelease(matching);
525         goto fail;
526     }
527     CFArrayAppendValue( matching, result );
528     CFRelease( result );
529 
530     IOHIDManagerSetDeviceMatchingMultiple( hid_manager, matching);
531     CFRelease( matching );
532     devset = IOHIDManagerCopyDevices( hid_manager );
533     if (devset)
534     {
535         CFIndex num_devices, num_main_elements, idx;
536         CFMutableArrayRef devices;
537 
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);
543 
544         device_main_elements = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
545         if (!device_main_elements)
546         {
547             CFRelease( devices );
548             goto fail;
549         }
550 
551         num_main_elements = 0;
552         for (idx = 0; idx < num_devices; idx++)
553         {
554             CFIndex top;
555             IOHIDDeviceRef hid_device;
556 
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;
561         }
562 
563         CFRelease(devices);
564 
565         TRACE("found %i device(s), %i collection(s)\n",(int)num_devices,(int)num_main_elements);
566         return (int)num_main_elements;
567     }
568 
569 fail:
570     IOHIDManagerClose( hid_manager, 0 );
571     CFRelease( hid_manager );
572     return 0;
573 }
574 
575 static int get_osx_device_name(int id, char *name, int length)
576 {
577     CFStringRef str;
578     IOHIDDeviceRef hid_device;
579 
580     hid_device = get_device_ref(id);
581 
582     TRACE("id %d hid_device %s\n", id, debugstr_device(hid_device));
583 
584     if (name)
585         name[0] = 0;
586 
587     if (!hid_device)
588         return 0;
589 
590     str = IOHIDDeviceGetProperty(hid_device, CFSTR( kIOHIDProductKey ));
591     if (str)
592     {
593         CFIndex len = CFStringGetLength(str);
594         if (length >= len)
595         {
596             CFStringGetCString(str,name,length,kCFStringEncodingASCII);
597             return len;
598         }
599         else
600             return (len+1);
601     }
602     return 0;
603 }
604 
605 static CFComparisonResult button_usage_comparator(const void *val1, const void *val2, void *context)
606 {
607     IOHIDElementRef element1 = (IOHIDElementRef)val1, element2 = (IOHIDElementRef)val2;
608     int usage1 = IOHIDElementGetUsage(element1), usage2 = IOHIDElementGetUsage(element2);
609 
610     if (usage1 < usage2)
611         return kCFCompareLessThan;
612     if (usage1 > usage2)
613         return kCFCompareGreaterThan;
614     return kCFCompareEqualTo;
615 }
616 
617 static void get_osx_device_elements(JoystickImpl *device, int axis_map[8])
618 {
619     IOHIDElementRef device_main_element;
620     CFMutableArrayRef elements;
621     DWORD           sliders = 0;
622 
623     TRACE("device %p device->id %d\n", device, device->id);
624 
625     device->elements = NULL;
626 
627     if (!device_main_elements || device->id >= CFArrayGetCount(device_main_elements))
628         return;
629 
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;
634 
635     elements = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
636     get_element_children(device_main_element, elements);
637 
638     if (elements)
639     {
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);
644 
645         for ( idx = 0; idx < cnt; idx++ )
646         {
647             IOHIDElementRef element = ( IOHIDElementRef ) CFArrayGetValueAtIndex( elements, idx );
648             int type = IOHIDElementGetType( element );
649             int usage_page = IOHIDElementGetUsagePage( element );
650 
651             TRACE("element %s\n", debugstr_element(element));
652 
653             if (usage_page >= kHIDPage_VendorDefinedStart)
654             {
655                 /* vendor pages can repurpose type ids, resulting in incorrect case matches below (e.g. ds4 controllers) */
656                 continue;
657             }
658 
659             switch(type)
660             {
661                 case kIOHIDElementTypeInput_Button:
662                 {
663                     TRACE("kIOHIDElementTypeInput_Button usage_page %d\n", usage_page);
664                     if (usage_page != kHIDPage_Button)
665                     {
666                         /* avoid strange elements found on the 360 controller */
667                         continue;
668                     }
669 
670                     if (CFArrayGetCount(buttons) < 128)
671                         CFArrayAppendValue(buttons, element);
672                     break;
673                 }
674                 case kIOHIDElementTypeInput_Axis:
675                 {
676                     TRACE("kIOHIDElementTypeInput_Axis\n");
677                     CFArrayAppendValue(axes, element);
678                     break;
679                 }
680                 case kIOHIDElementTypeInput_Misc:
681                 {
682                     uint32_t usage = IOHIDElementGetUsage( element );
683                     switch(usage)
684                     {
685                         case kHIDUsage_GD_Hatswitch:
686                         {
687                             TRACE("kIOHIDElementTypeInput_Misc / kHIDUsage_GD_Hatswitch\n");
688                             CFArrayAppendValue(povs, element);
689                             break;
690                         }
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:
702                         {
703                             TRACE("kIOHIDElementTypeInput_Misc / kHIDUsage_GD_* (%d)\n", usage);
704                             axis_map[CFArrayGetCount(axes)]=usage;
705                             CFArrayAppendValue(axes, element);
706                             break;
707                         }
708                         default:
709                             FIXME("kIOHIDElementTypeInput_Misc / Unhandled usage %i\n", usage);
710                     }
711                     break;
712                 }
713                 default:
714                     FIXME("Unhandled type %i\n",type);
715             }
716         }
717 
718         /* Sort buttons into correct order */
719         CFArraySortValues(buttons, CFRangeMake(0, CFArrayGetCount(buttons)), button_usage_comparator, NULL);
720 
721         device->generic.devcaps.dwAxes = CFArrayGetCount(axes);
722         device->generic.devcaps.dwButtons = CFArrayGetCount(buttons);
723         device->generic.devcaps.dwPOVs = CFArrayGetCount(povs);
724 
725         TRACE("axes %u povs %u buttons %u\n", device->generic.devcaps.dwAxes, device->generic.devcaps.dwPOVs,
726               device->generic.devcaps.dwButtons);
727 
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;
733 
734         CFRelease(povs);
735         CFRelease(buttons);
736         CFRelease(elements);
737     }
738     else
739     {
740         device->generic.devcaps.dwAxes = 0;
741         device->generic.devcaps.dwButtons = 0;
742         device->generic.devcaps.dwPOVs = 0;
743     }
744 }
745 
746 static void get_osx_device_elements_props(JoystickImpl *device)
747 {
748     TRACE("device %p\n", device);
749 
750     if (device->elements)
751     {
752         CFIndex idx, cnt = CFArrayGetCount( device->elements );
753 
754         for ( idx = 0; idx < cnt; idx++ )
755         {
756             IOHIDElementRef element = ( IOHIDElementRef ) CFArrayGetValueAtIndex( device->elements, idx );
757 
758             TRACE("element %s\n", debugstr_element(element));
759 
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;
766         }
767     }
768 }
769 
770 static void poll_osx_device_state(LPDIRECTINPUTDEVICE8A iface)
771 {
772     JoystickImpl *device = impl_from_IDirectInputDevice8A(iface);
773     IOHIDElementRef device_main_element;
774     IOHIDDeviceRef hid_device;
775 
776     TRACE("device %p device->id %i\n", device, device->id);
777 
778     if (!device_main_elements || device->id >= CFArrayGetCount(device_main_elements))
779         return;
780 
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;
786 
787     if (device->elements)
788     {
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 );
794 
795         for ( idx = 0; idx < cnt; idx++ )
796         {
797             IOHIDValueRef valueRef;
798             int val, oldVal, newVal;
799             IOHIDElementRef element = ( IOHIDElementRef ) CFArrayGetValueAtIndex( device->elements, idx );
800             int type = IOHIDElementGetType( element );
801 
802             TRACE("element %s\n", debugstr_element(element));
803 
804             switch(type)
805             {
806                 case kIOHIDElementTypeInput_Button:
807                     TRACE("kIOHIDElementTypeInput_Button\n");
808                     if(button_idx < 128)
809                     {
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)
821                         {
822                             button_idx = device->generic.button_map[button_idx];
823 
824                             inst_id = DIDFT_MAKEINSTANCE(button_idx) | DIDFT_PSHBUTTON;
825                             queue_event(iface,inst_id,newVal,GetCurrentTime(),device->generic.base.dinput->evsequence++);
826                         }
827                         button_idx ++;
828                     }
829                     break;
830                 case kIOHIDElementTypeInput_Misc:
831                 {
832                     uint32_t usage = IOHIDElementGetUsage( element );
833                     switch(usage)
834                     {
835                         case kHIDUsage_GD_Hatswitch:
836                         {
837                             TRACE("kIOHIDElementTypeInput_Misc / kHIDUsage_GD_Hatswitch\n");
838                             valueRef = NULL;
839                             if (IOHIDDeviceGetValue(hid_device, element, &valueRef) != kIOReturnSuccess)
840                                 return;
841                             if (valueRef == NULL)
842                                 return;
843                             val = IOHIDValueGetIntegerValue(valueRef);
844                             oldVal = device->generic.js.rgdwPOV[pov_idx];
845                             if (val >= 8)
846                                 newVal = -1;
847                             else
848                                 newVal = val * 4500;
849                             device->generic.js.rgdwPOV[pov_idx] = newVal;
850                             TRACE("valueRef %s val %d oldVal %d newVal %d\n", debugstr_cf(valueRef), val, oldVal, newVal);
851                             if (oldVal != newVal)
852                             {
853                                 inst_id = DIDFT_MAKEINSTANCE(pov_idx) | DIDFT_POV;
854                                 queue_event(iface,inst_id,newVal,GetCurrentTime(),device->generic.base.dinput->evsequence++);
855                             }
856                             pov_idx ++;
857                             break;
858                         }
859                         case kHIDUsage_GD_X:
860                         case kHIDUsage_GD_Y:
861                         case kHIDUsage_GD_Z:
862                         case kHIDUsage_GD_Rx:
863                         case kHIDUsage_GD_Ry:
864                         case kHIDUsage_GD_Rz:
865                         case kHIDUsage_GD_Slider:
866                         {
867                             int wine_obj = -1;
868 
869                             valueRef = NULL;
870                             if (IOHIDDeviceGetValue(hid_device, element, &valueRef) != kIOReturnSuccess)
871                                 return;
872                             if (valueRef == NULL)
873                                 return;
874                             val = IOHIDValueGetIntegerValue(valueRef);
875                             newVal = joystick_map_axis(&device->generic.props[idx], val);
876                             switch (usage)
877                             {
878                             case kHIDUsage_GD_X:
879                                 TRACE("kIOHIDElementTypeInput_Misc / kHIDUsage_GD_X\n");
880                                 wine_obj = 0;
881                                 oldVal = device->generic.js.lX;
882                                 device->generic.js.lX = newVal;
883                                 break;
884                             case kHIDUsage_GD_Y:
885                                 TRACE("kIOHIDElementTypeInput_Misc / kHIDUsage_GD_Y\n");
886                                 wine_obj = 1;
887                                 oldVal = device->generic.js.lY;
888                                 device->generic.js.lY = newVal;
889                                 break;
890                             case kHIDUsage_GD_Z:
891                                 TRACE("kIOHIDElementTypeInput_Misc / kHIDUsage_GD_Z\n");
892                                 wine_obj = 2;
893                                 oldVal = device->generic.js.lZ;
894                                 device->generic.js.lZ = newVal;
895                                 break;
896                             case kHIDUsage_GD_Rx:
897                                 TRACE("kIOHIDElementTypeInput_Misc / kHIDUsage_GD_Rx\n");
898                                 wine_obj = 3;
899                                 oldVal = device->generic.js.lRx;
900                                 device->generic.js.lRx = newVal;
901                                 break;
902                             case kHIDUsage_GD_Ry:
903                                 TRACE("kIOHIDElementTypeInput_Misc / kHIDUsage_GD_Ry\n");
904                                 wine_obj = 4;
905                                 oldVal = device->generic.js.lRy;
906                                 device->generic.js.lRy = newVal;
907                                 break;
908                             case kHIDUsage_GD_Rz:
909                                 TRACE("kIOHIDElementTypeInput_Misc / kHIDUsage_GD_Rz\n");
910                                 wine_obj = 5;
911                                 oldVal = device->generic.js.lRz;
912                                 device->generic.js.lRz = newVal;
913                                 break;
914                             case kHIDUsage_GD_Slider:
915                                 TRACE("kIOHIDElementTypeInput_Misc / kHIDUsage_GD_Slider\n");
916                                 wine_obj = 6 + slider_idx;
917                                 oldVal = device->generic.js.rglSlider[slider_idx];
918                                 device->generic.js.rglSlider[slider_idx] = newVal;
919                                 slider_idx ++;
920                                 break;
921                             }
922                             TRACE("valueRef %s val %d oldVal %d newVal %d\n", debugstr_cf(valueRef), val, oldVal, newVal);
923                             if ((wine_obj != -1) &&
924                                  (oldVal != newVal))
925                             {
926                                 inst_id = DIDFT_MAKEINSTANCE(wine_obj) | DIDFT_ABSAXIS;
927                                 queue_event(iface,inst_id,newVal,GetCurrentTime(),device->generic.base.dinput->evsequence++);
928                             }
929 
930                             break;
931                         }
932                         default:
933                             FIXME("kIOHIDElementTypeInput_Misc / unhandled usage %i\n", usage);
934                     }
935                     break;
936                 }
937                 default:
938                     FIXME("Unhandled type %i\n",type);
939             }
940         }
941     }
942 }
943 
944 static INT find_joystick_devices(void)
945 {
946     static INT joystick_devices_count = -1;
947 
948     if (joystick_devices_count != -1) return joystick_devices_count;
949 
950     joystick_devices_count = find_osx_devices();
951 
952     return  joystick_devices_count;
953 }
954 
955 static DWORD make_vid_pid(IOHIDDeviceRef device)
956 {
957     long vendID, prodID;
958 
959     vendID = get_device_property_long(device, CFSTR(kIOHIDVendorIDKey));
960     prodID = get_device_property_long(device, CFSTR(kIOHIDProductIDKey));
961 
962     return MAKELONG(vendID, prodID);
963 }
964 
965 static HRESULT joydev_enum_deviceA(DWORD dwDevType, DWORD dwFlags, LPDIDEVICEINSTANCEA lpddi, DWORD version, int id)
966 {
967     IOHIDDeviceRef device;
968     BOOL is_joystick;
969 
970     TRACE("dwDevType %u dwFlags 0x%08x version 0x%04x id %d\n", dwDevType, dwFlags, version, id);
971 
972     if (id >= find_joystick_devices()) return E_FAIL;
973 
974     device = get_device_ref(id);
975 
976     if ((dwDevType == 0) ||
977     ((dwDevType == DIDEVTYPE_JOYSTICK) && (version >= 0x0300 && version < 0x0800)) ||
978     (((dwDevType == DI8DEVCLASS_GAMECTRL) || (dwDevType == DI8DEVTYPE_JOYSTICK)) && (version >= 0x0800)))
979     {
980         if (dwFlags & DIEDFL_FORCEFEEDBACK) {
981             if(!device)
982                 return S_FALSE;
983             if(get_ff(device, NULL) != S_OK)
984                 return S_FALSE;
985         }
986         is_joystick = get_device_property_long(device, CFSTR(kIOHIDDeviceUsageKey)) == kHIDUsage_GD_Joystick;
987         /* Return joystick */
988         lpddi->guidInstance = DInput_Wine_OsX_Joystick_GUID;
989         lpddi->guidInstance.Data3 = id;
990         lpddi->guidProduct = DInput_PIDVID_Product_GUID;
991         lpddi->guidProduct.Data1 = make_vid_pid(device);
992         lpddi->dwDevType = get_device_type(version, is_joystick);
993         lpddi->dwDevType |= DIDEVTYPE_HID;
994         lpddi->wUsagePage = 0x01; /* Desktop */
995         if (is_joystick)
996             lpddi->wUsage = 0x04; /* Joystick */
997         else
998             lpddi->wUsage = 0x05; /* Game Pad */
999         sprintf(lpddi->tszInstanceName, "Joystick %d", id);
1000 
1001         /* get the device name */
1002         get_osx_device_name(id, lpddi->tszProductName, MAX_PATH);
1003 
1004         lpddi->guidFFDriver = GUID_NULL;
1005         return S_OK;
1006     }
1007 
1008     return S_FALSE;
1009 }
1010 
1011 static HRESULT joydev_enum_deviceW(DWORD dwDevType, DWORD dwFlags, LPDIDEVICEINSTANCEW lpddi, DWORD version, int id)
1012 {
1013     char name[MAX_PATH];
1014     char friendly[32];
1015     IOHIDDeviceRef device;
1016     BOOL is_joystick;
1017 
1018     TRACE("dwDevType %u dwFlags 0x%08x version 0x%04x id %d\n", dwDevType, dwFlags, version, id);
1019 
1020     if (id >= find_joystick_devices()) return E_FAIL;
1021 
1022     device = get_device_ref(id);
1023 
1024     if ((dwDevType == 0) ||
1025     ((dwDevType == DIDEVTYPE_JOYSTICK) && (version >= 0x0300 && version < 0x0800)) ||
1026     (((dwDevType == DI8DEVCLASS_GAMECTRL) || (dwDevType == DI8DEVTYPE_JOYSTICK)) && (version >= 0x0800))) {
1027 
1028         if (dwFlags & DIEDFL_FORCEFEEDBACK) {
1029             if(!device)
1030                 return S_FALSE;
1031             if(get_ff(device, NULL) != S_OK)
1032                 return S_FALSE;
1033         }
1034         is_joystick = get_device_property_long(device, CFSTR(kIOHIDDeviceUsageKey)) == kHIDUsage_GD_Joystick;
1035         /* Return joystick */
1036         lpddi->guidInstance = DInput_Wine_OsX_Joystick_GUID;
1037         lpddi->guidInstance.Data3 = id;
1038         lpddi->guidProduct = DInput_PIDVID_Product_GUID;
1039         lpddi->guidProduct.Data1 = make_vid_pid(device);
1040         lpddi->dwDevType = get_device_type(version, is_joystick);
1041         lpddi->dwDevType |= DIDEVTYPE_HID;
1042         lpddi->wUsagePage = 0x01; /* Desktop */
1043         if (is_joystick)
1044             lpddi->wUsage = 0x04; /* Joystick */
1045         else
1046             lpddi->wUsage = 0x05; /* Game Pad */
1047         sprintf(friendly, "Joystick %d", id);
1048         MultiByteToWideChar(CP_ACP, 0, friendly, -1, lpddi->tszInstanceName, MAX_PATH);
1049 
1050         /* get the device name */
1051         get_osx_device_name(id, name, MAX_PATH);
1052 
1053         MultiByteToWideChar(CP_ACP, 0, name, -1, lpddi->tszProductName, MAX_PATH);
1054         lpddi->guidFFDriver = GUID_NULL;
1055         return S_OK;
1056     }
1057 
1058     return S_FALSE;
1059 }
1060 
1061 static const char *osx_ff_axis_name(UInt8 axis)
1062 {
1063     static char ret[6];
1064     switch(axis){
1065     case FFJOFS_X:
1066         return "FFJOFS_X";
1067     case FFJOFS_Y:
1068         return "FFJOFS_Y";
1069     case FFJOFS_Z:
1070         return "FFJOFS_Z";
1071     }
1072     sprintf(ret, "%u", (unsigned int)axis);
1073     return ret;
1074 }
1075 
1076 static BOOL osx_axis_has_ff(FFCAPABILITIES *ffcaps, UInt8 axis)
1077 {
1078     int i;
1079     for(i = 0; i < ffcaps->numFfAxes; ++i)
1080         if(ffcaps->ffAxes[i] == axis)
1081             return TRUE;
1082     return FALSE;
1083 }
1084 
1085 static HRESULT alloc_device(REFGUID rguid, IDirectInputImpl *dinput,
1086                             JoystickImpl **pdev, unsigned short index)
1087 {
1088     DWORD i;
1089     IOHIDDeviceRef device;
1090     JoystickImpl* newDevice;
1091     char name[MAX_PATH];
1092     HRESULT hr;
1093     LPDIDATAFORMAT df = NULL;
1094     int idx = 0;
1095     int axis_map[8]; /* max axes */
1096     int slider_count = 0;
1097     FFCAPABILITIES ffcaps;
1098 
1099     TRACE("%s %p %p %hu\n", debugstr_guid(rguid), dinput, pdev, index);
1100 
1101     newDevice = HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(JoystickImpl));
1102     if (newDevice == 0) {
1103         WARN("out of memory\n");
1104         *pdev = 0;
1105         return DIERR_OUTOFMEMORY;
1106     }
1107 
1108     newDevice->id = index;
1109 
1110     device = get_device_ref(index);
1111 
1112     newDevice->generic.guidInstance = DInput_Wine_OsX_Joystick_GUID;
1113     newDevice->generic.guidInstance.Data3 = index;
1114     newDevice->generic.guidProduct = DInput_PIDVID_Product_GUID;
1115     newDevice->generic.guidProduct.Data1 = make_vid_pid(device);
1116     newDevice->generic.joy_polldev = poll_osx_device_state;
1117 
1118     /* get the device name */
1119     get_osx_device_name(index, name, MAX_PATH);
1120     TRACE("Name %s\n",name);
1121 
1122     /* copy the device name */
1123     newDevice->generic.name = HeapAlloc(GetProcessHeap(),0,strlen(name) + 1);
1124     strcpy(newDevice->generic.name, name);
1125 
1126     list_init(&newDevice->effects);
1127     if(get_ff(device, &newDevice->ff) == S_OK){
1128         newDevice->generic.devcaps.dwFlags |= DIDC_FORCEFEEDBACK;
1129 
1130         hr = FFDeviceGetForceFeedbackCapabilities(newDevice->ff, &ffcaps);
1131         if(SUCCEEDED(hr)){
1132             TRACE("FF Capabilities:\n");
1133             TRACE("\tsupportedEffects: 0x%x\n", (unsigned int)ffcaps.supportedEffects);
1134             TRACE("\temulatedEffects: 0x%x\n", (unsigned int)ffcaps.emulatedEffects);
1135             TRACE("\tsubType: 0x%x\n", (unsigned int)ffcaps.subType);
1136             TRACE("\tnumFfAxes: %u\n", (unsigned int)ffcaps.numFfAxes);
1137             TRACE("\tffAxes: [");
1138             for(i = 0; i < ffcaps.numFfAxes; ++i){
1139                 TRACE("%s", osx_ff_axis_name(ffcaps.ffAxes[i]));
1140                 if(i < ffcaps.numFfAxes - 1)
1141                     TRACE(", ");
1142             }
1143             TRACE("]\n");
1144             TRACE("\tstorageCapacity: %u\n", (unsigned int)ffcaps.storageCapacity);
1145             TRACE("\tplaybackCapacity: %u\n", (unsigned int)ffcaps.playbackCapacity);
1146         }
1147 
1148         hr = FFDeviceSendForceFeedbackCommand(newDevice->ff, FFSFFC_RESET);
1149         if(FAILED(hr))
1150             WARN("FFDeviceSendForceFeedbackCommand(FFSFFC_RESET) failed: %08x\n", hr);
1151 
1152         hr = FFDeviceSendForceFeedbackCommand(newDevice->ff, FFSFFC_SETACTUATORSON);
1153         if(FAILED(hr))
1154             WARN("FFDeviceSendForceFeedbackCommand(FFSFFC_SETACTUATORSON) failed: %08x\n", hr);
1155     }
1156 
1157     memset(axis_map, 0, sizeof(axis_map));
1158     get_osx_device_elements(newDevice, axis_map);
1159 
1160     TRACE("%i axes %i buttons %i povs\n",newDevice->generic.devcaps.dwAxes,newDevice->generic.devcaps.dwButtons,newDevice->generic.devcaps.dwPOVs);
1161 
1162     if (newDevice->generic.devcaps.dwButtons > 128)
1163     {
1164         WARN("Can't support %d buttons. Clamping down to 128\n", newDevice->generic.devcaps.dwButtons);
1165         newDevice->generic.devcaps.dwButtons = 128;
1166     }
1167 
1168     newDevice->generic.base.IDirectInputDevice8A_iface.lpVtbl = &JoystickAvt;
1169     newDevice->generic.base.IDirectInputDevice8W_iface.lpVtbl = &JoystickWvt;
1170     newDevice->generic.base.ref = 1;
1171     newDevice->generic.base.dinput = dinput;
1172     newDevice->generic.base.guid = *rguid;
1173     InitializeCriticalSection(&newDevice->generic.base.crit);
1174     newDevice->generic.base.crit.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": JoystickImpl*->generic.base.crit");
1175 
1176     /* Create copy of default data format */
1177     if (!(df = HeapAlloc(GetProcessHeap(), 0, c_dfDIJoystick2.dwSize))) goto FAILED;
1178     memcpy(df, &c_dfDIJoystick2, c_dfDIJoystick2.dwSize);
1179 
1180     df->dwNumObjs = newDevice->generic.devcaps.dwAxes + newDevice->generic.devcaps.dwPOVs + newDevice->generic.devcaps.dwButtons;
1181     if (!(df->rgodf = HeapAlloc(GetProcessHeap(), 0, df->dwNumObjs * df->dwObjSize))) goto FAILED;
1182 
1183     for (i = 0; i < newDevice->generic.devcaps.dwAxes; i++)
1184     {
1185         int wine_obj = -1;
1186         BOOL has_ff  = FALSE;
1187         switch (axis_map[i])
1188         {
1189             case kHIDUsage_GD_X:
1190                 wine_obj = 0;
1191                 has_ff = (newDevice->ff != 0) && osx_axis_has_ff(&ffcaps, FFJOFS_X);
1192                 break;
1193             case kHIDUsage_GD_Y:
1194                 wine_obj = 1;
1195                 has_ff = (newDevice->ff != 0) && osx_axis_has_ff(&ffcaps, FFJOFS_Y);
1196                 break;
1197             case kHIDUsage_GD_Z:
1198                 wine_obj = 2;
1199                 has_ff = (newDevice->ff != 0) && osx_axis_has_ff(&ffcaps, FFJOFS_Z);
1200                 break;
1201             case kHIDUsage_GD_Rx:
1202                 wine_obj = 3;
1203                 has_ff = (newDevice->ff != 0) && osx_axis_has_ff(&ffcaps, FFJOFS_RX);
1204                 break;
1205             case kHIDUsage_GD_Ry:
1206                 wine_obj = 4;
1207                 has_ff = (newDevice->ff != 0) && osx_axis_has_ff(&ffcaps, FFJOFS_RY);
1208                 break;
1209             case kHIDUsage_GD_Rz:
1210                 wine_obj = 5;
1211                 has_ff = (newDevice->ff != 0) && osx_axis_has_ff(&ffcaps, FFJOFS_RZ);
1212                 break;
1213             case kHIDUsage_GD_Slider:
1214                 wine_obj = 6 + slider_count;
1215                 has_ff = (newDevice->ff != 0) && osx_axis_has_ff(&ffcaps, FFJOFS_SLIDER(slider_count));
1216                 slider_count++;
1217                 break;
1218         }
1219         if (wine_obj < 0 ) continue;
1220 
1221         memcpy(&df->rgodf[idx], &c_dfDIJoystick2.rgodf[wine_obj], df->dwObjSize);
1222         df->rgodf[idx].dwType = DIDFT_MAKEINSTANCE(wine_obj) | DIDFT_ABSAXIS;
1223         if(has_ff)
1224             df->rgodf[idx].dwFlags |= DIDOI_FFACTUATOR;
1225         ++idx;
1226     }
1227 
1228     for (i = 0; i < newDevice->generic.devcaps.dwPOVs; i++)
1229     {
1230         memcpy(&df->rgodf[idx], &c_dfDIJoystick2.rgodf[i + 8], df->dwObjSize);
1231         df->rgodf[idx++].dwType = DIDFT_MAKEINSTANCE(i) | DIDFT_POV;
1232     }
1233 
1234     for (i = 0; i < newDevice->generic.devcaps.dwButtons; i++)
1235     {
1236         memcpy(&df->rgodf[idx], &c_dfDIJoystick2.rgodf[i + 12], df->dwObjSize);
1237         df->rgodf[idx  ].pguid = &GUID_Button;
1238         df->rgodf[idx++].dwType = DIDFT_MAKEINSTANCE(i) | DIDFT_PSHBUTTON;
1239     }
1240     newDevice->generic.base.data_format.wine_df = df;
1241 
1242     /* initialize default properties */
1243     get_osx_device_elements_props(newDevice);
1244 
1245     IDirectInput_AddRef(&newDevice->generic.base.dinput->IDirectInput7A_iface);
1246 
1247     EnterCriticalSection(&dinput->crit);
1248     list_add_tail(&dinput->devices_list, &newDevice->generic.base.entry);
1249     LeaveCriticalSection(&dinput->crit);
1250 
1251     newDevice->generic.devcaps.dwSize = sizeof(newDevice->generic.devcaps);
1252     newDevice->generic.devcaps.dwFlags |= DIDC_ATTACHED;
1253     if (newDevice->generic.base.dinput->dwVersion >= 0x0800)
1254         newDevice->generic.devcaps.dwDevType = DI8DEVTYPE_JOYSTICK | (DI8DEVTYPEJOYSTICK_STANDARD << 8);
1255     else
1256         newDevice->generic.devcaps.dwDevType = DIDEVTYPE_JOYSTICK | (DIDEVTYPEJOYSTICK_TRADITIONAL << 8);
1257     newDevice->generic.devcaps.dwFFSamplePeriod = 0;
1258     newDevice->generic.devcaps.dwFFMinTimeResolution = 0;
1259     newDevice->generic.devcaps.dwFirmwareRevision = 0;
1260     newDevice->generic.devcaps.dwHardwareRevision = 0;
1261     newDevice->generic.devcaps.dwFFDriverVersion = 0;
1262 
1263     if (TRACE_ON(dinput)) {
1264         TRACE("allocated device %p\n", newDevice);
1265         _dump_DIDATAFORMAT(newDevice->generic.base.data_format.wine_df);
1266         _dump_DIDEVCAPS(&newDevice->generic.devcaps);
1267     }
1268 
1269     *pdev = newDevice;
1270 
1271     return DI_OK;
1272 
1273 FAILED:
1274     hr = DIERR_OUTOFMEMORY;
1275     if (newDevice->ff) FFReleaseDevice(newDevice->ff);
1276     if (newDevice->elements) CFRelease(newDevice->elements);
1277     if (df) HeapFree(GetProcessHeap(), 0, df->rgodf);
1278     HeapFree(GetProcessHeap(), 0, df);
1279     release_DataFormat(&newDevice->generic.base.data_format);
1280     HeapFree(GetProcessHeap(),0,newDevice->generic.name);
1281     HeapFree(GetProcessHeap(),0,newDevice);
1282     *pdev = 0;
1283 
1284     return hr;
1285 }
1286 
1287 /******************************************************************************
1288   *     get_joystick_index : Get the joystick index from a given GUID
1289   */
1290 static unsigned short get_joystick_index(REFGUID guid)
1291 {
1292     GUID wine_joystick = DInput_Wine_OsX_Joystick_GUID;
1293     GUID dev_guid = *guid;
1294 
1295     wine_joystick.Data3 = 0;
1296     dev_guid.Data3 = 0;
1297 
1298     /* for the standard joystick GUID use index 0 */
1299     if(IsEqualGUID(&GUID_Joystick,guid)) return 0;
1300 
1301     /* for the wine joystick GUIDs use the index stored in Data3 */
1302     if(IsEqualGUID(&wine_joystick, &dev_guid)) return guid->Data3;
1303 
1304     return 0xffff;
1305 }
1306 
1307 static HRESULT joydev_create_device(IDirectInputImpl *dinput, REFGUID rguid, REFIID riid, LPVOID *pdev, int unicode)
1308 {
1309     unsigned short index;
1310     int joystick_devices_count;
1311 
1312     TRACE("%p %s %s %p %i\n", dinput, debugstr_guid(rguid), debugstr_guid(riid), pdev, unicode);
1313     *pdev = NULL;
1314 
1315     if ((joystick_devices_count = find_joystick_devices()) == 0)
1316         return DIERR_DEVICENOTREG;
1317 
1318     if ((index = get_joystick_index(rguid)) < 0xffff &&
1319         joystick_devices_count && index < joystick_devices_count)
1320     {
1321         JoystickImpl *This;
1322         HRESULT hr;
1323 
1324         if (riid == NULL)
1325             ;/* nothing */
1326         else if (IsEqualGUID(&IID_IDirectInputDeviceA,  riid) ||
1327                  IsEqualGUID(&IID_IDirectInputDevice2A, riid) ||
1328                  IsEqualGUID(&IID_IDirectInputDevice7A, riid) ||
1329                  IsEqualGUID(&IID_IDirectInputDevice8A, riid))
1330         {
1331             unicode = 0;
1332         }
1333         else if (IsEqualGUID(&IID_IDirectInputDeviceW,  riid) ||
1334                  IsEqualGUID(&IID_IDirectInputDevice2W, riid) ||
1335                  IsEqualGUID(&IID_IDirectInputDevice7W, riid) ||
1336                  IsEqualGUID(&IID_IDirectInputDevice8W, riid))
1337         {
1338             unicode = 1;
1339         }
1340         else
1341         {
1342             WARN("no interface\n");
1343             return DIERR_NOINTERFACE;
1344         }
1345 
1346         hr = alloc_device(rguid, dinput, &This, index);
1347         if (!This) return hr;
1348 
1349         if (unicode)
1350             *pdev = &This->generic.base.IDirectInputDevice8W_iface;
1351         else
1352             *pdev = &This->generic.base.IDirectInputDevice8A_iface;
1353         return hr;
1354     }
1355 
1356     return DIERR_DEVICENOTREG;
1357 }
1358 
1359 static HRESULT WINAPI JoystickWImpl_GetProperty(LPDIRECTINPUTDEVICE8W iface, REFGUID rguid, LPDIPROPHEADER pdiph)
1360 {
1361     JoystickImpl *This = impl_from_IDirectInputDevice8W(iface);
1362 
1363     TRACE("(%p)->(%s,%p)\n", This, debugstr_guid(rguid), pdiph);
1364     _dump_DIPROPHEADER(pdiph);
1365 
1366     if (!IS_DIPROP(rguid)) return DI_OK;
1367 
1368     switch (LOWORD(rguid)) {
1369     case (DWORD_PTR) DIPROP_GUIDANDPATH:
1370     {
1371         static const WCHAR formatW[] = {'\\','\\','?','\\','h','i','d','#','v','i','d','_','%','0','4','x','&',
1372                                         'p','i','d','_','%','0','4','x','&','%','s','_','%','i',0};
1373         static const WCHAR miW[] = {'m','i',0};
1374         static const WCHAR igW[] = {'i','g',0};
1375 
1376         BOOL is_gamepad;
1377         IOHIDDeviceRef device = get_device_ref(This->id);
1378         LPDIPROPGUIDANDPATH pd = (LPDIPROPGUIDANDPATH)pdiph;
1379         WORD vid = get_device_property_long(device, CFSTR(kIOHIDVendorIDKey));
1380         WORD pid = get_device_property_long(device, CFSTR(kIOHIDProductIDKey));
1381 
1382         if (!pid || !vid)
1383             return DIERR_UNSUPPORTED;
1384 
1385         is_gamepad = is_xinput_device(&This->generic.devcaps, vid, pid);
1386         pd->guidClass = GUID_DEVCLASS_HIDCLASS;
1387         sprintfW(pd->wszPath, formatW, vid, pid, is_gamepad ? igW : miW, This->id);
1388 
1389         TRACE("DIPROP_GUIDANDPATH(%s, %s): returning fake path\n", debugstr_guid(&pd->guidClass), debugstr_w(pd->wszPath));
1390         break;
1391     }
1392 
1393     default:
1394         return JoystickWGenericImpl_GetProperty(iface, rguid, pdiph);
1395     }
1396 
1397     return DI_OK;
1398 }
1399 
1400 static HRESULT WINAPI JoystickAImpl_GetProperty(LPDIRECTINPUTDEVICE8A iface, REFGUID rguid, LPDIPROPHEADER pdiph)
1401 {
1402     JoystickImpl *This = impl_from_IDirectInputDevice8A(iface);
1403     return JoystickWImpl_GetProperty(IDirectInputDevice8W_from_impl(This), rguid, pdiph);
1404 }
1405 
1406 static HRESULT osx_set_autocenter(JoystickImpl *This,
1407         const DIPROPDWORD *header)
1408 {
1409     UInt32 v;
1410     HRESULT hr;
1411     if(!This->ff)
1412         return DIERR_UNSUPPORTED;
1413     v = header->dwData;
1414     hr = osx_to_win32_hresult(FFDeviceSetForceFeedbackProperty(This->ff, FFPROP_AUTOCENTER, &v));
1415     TRACE("returning: %08x\n", hr);
1416     return hr;
1417 }
1418 
1419 static HRESULT osx_set_ffgain(JoystickImpl *This, const DIPROPDWORD *header)
1420 {
1421     UInt32 v;
1422     HRESULT hr;
1423     if(!This->ff)
1424         return DIERR_UNSUPPORTED;
1425     v = header->dwData;
1426     hr = osx_to_win32_hresult(FFDeviceSetForceFeedbackProperty(This->ff, FFPROP_FFGAIN, &v));
1427     TRACE("returning: %08x\n", hr);
1428     return hr;
1429 }
1430 
1431 static HRESULT WINAPI JoystickWImpl_SetProperty(IDirectInputDevice8W *iface,
1432         const GUID *prop, const DIPROPHEADER *header)
1433 {
1434     JoystickImpl *This = impl_from_IDirectInputDevice8W(iface);
1435 
1436     TRACE("%p %s %p\n", This, debugstr_guid(prop), header);
1437 
1438     switch(LOWORD(prop))
1439     {
1440     case (DWORD_PTR)DIPROP_AUTOCENTER:
1441         return osx_set_autocenter(This, (const DIPROPDWORD *)header);
1442     case (DWORD_PTR)DIPROP_FFGAIN:
1443         return osx_set_ffgain(This, (const DIPROPDWORD *)header);
1444     }
1445 
1446     return JoystickWGenericImpl_SetProperty(iface, prop, header);
1447 }
1448 
1449 static HRESULT WINAPI JoystickAImpl_SetProperty(IDirectInputDevice8A *iface,
1450         const GUID *prop, const DIPROPHEADER *header)
1451 {
1452     JoystickImpl *This = impl_from_IDirectInputDevice8A(iface);
1453 
1454     TRACE("%p %s %p\n", This, debugstr_guid(prop), header);
1455 
1456     switch(LOWORD(prop))
1457     {
1458     case (DWORD_PTR)DIPROP_AUTOCENTER:
1459         return osx_set_autocenter(This, (const DIPROPDWORD *)header);
1460     case (DWORD_PTR)DIPROP_FFGAIN:
1461         return osx_set_ffgain(This, (const DIPROPDWORD *)header);
1462     }
1463 
1464     return JoystickAGenericImpl_SetProperty(iface, prop, header);
1465 }
1466 
1467 static CFUUIDRef effect_win_to_mac(const GUID *effect)
1468 {
1469 #define DO_MAP(X) \
1470     if(IsEqualGUID(&GUID_##X, effect)) \
1471         return kFFEffectType_##X##_ID;
1472     DO_MAP(ConstantForce)
1473     DO_MAP(RampForce)
1474     DO_MAP(Square)
1475     DO_MAP(Sine)
1476     DO_MAP(Triangle)
1477     DO_MAP(SawtoothUp)
1478     DO_MAP(SawtoothDown)
1479     DO_MAP(Spring)
1480     DO_MAP(Damper)
1481     DO_MAP(Inertia)
1482     DO_MAP(Friction)
1483     DO_MAP(CustomForce)
1484 #undef DO_MAP
1485     WARN("Unknown effect GUID! %s\n", debugstr_guid(effect));
1486     return 0;
1487 }
1488 
1489 static HRESULT WINAPI JoystickWImpl_CreateEffect(IDirectInputDevice8W *iface,
1490         const GUID *type, const DIEFFECT *params, IDirectInputEffect **out,
1491         IUnknown *outer)
1492 {
1493     JoystickImpl *This = impl_from_IDirectInputDevice8W(iface);
1494     EffectImpl *effect;
1495     HRESULT hr;
1496 
1497     TRACE("(%p)->(%s %p %p %p)\n", This, debugstr_guid(type), params, out, outer);
1498     dump_DIEFFECT(params, type, 0);
1499 
1500     if(!This->ff){
1501         TRACE("No force feedback support\n");
1502         *out = NULL;
1503         return DIERR_UNSUPPORTED;
1504     }
1505 
1506     if(outer)
1507         WARN("aggregation not implemented\n");
1508 
1509     effect = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*This));
1510     effect->IDirectInputEffect_iface.lpVtbl = &EffectVtbl;
1511     effect->ref = 1;
1512     effect->guid = *type;
1513     effect->device = This;
1514 
1515     /* Mac's FFEFFECT and Win's DIEFFECT are binary identical. */
1516     hr = osx_to_win32_hresult(FFDeviceCreateEffect(This->ff,
1517                 effect_win_to_mac(type), (FFEFFECT*)params, &effect->effect));
1518     if(FAILED(hr)){
1519         WARN("FFDeviceCreateEffect failed: %08x\n", hr);
1520         HeapFree(GetProcessHeap(), 0, effect);
1521         return hr;
1522     }
1523 
1524     list_add_tail(&This->effects, &effect->entry);
1525     *out = &effect->IDirectInputEffect_iface;
1526 
1527     TRACE("allocated effect: %p\n", effect);
1528 
1529     return S_OK;
1530 }
1531 
1532 static HRESULT WINAPI JoystickAImpl_CreateEffect(IDirectInputDevice8A *iface,
1533         const GUID *type, const DIEFFECT *params, IDirectInputEffect **out,
1534         IUnknown *outer)
1535 {
1536     JoystickImpl *This = impl_from_IDirectInputDevice8A(iface);
1537 
1538     TRACE("(%p)->(%s %p %p %p)\n", This, debugstr_guid(type), params, out, outer);
1539 
1540     return JoystickWImpl_CreateEffect(&This->generic.base.IDirectInputDevice8W_iface,
1541             type, params, out, outer);
1542 }
1543 
1544 static HRESULT WINAPI JoystickWImpl_SendForceFeedbackCommand(IDirectInputDevice8W *iface,
1545         DWORD flags)
1546 {
1547     JoystickImpl *This = impl_from_IDirectInputDevice8W(iface);
1548     HRESULT hr;
1549 
1550     TRACE("%p 0x%x\n", This, flags);
1551 
1552     if(!This->ff)
1553         return DI_NOEFFECT;
1554 
1555     hr = osx_to_win32_hresult(FFDeviceSendForceFeedbackCommand(This->ff, flags));
1556     if(FAILED(hr)){
1557         WARN("FFDeviceSendForceFeedbackCommand failed: %08x\n", hr);
1558         return hr;
1559     }
1560 
1561     return S_OK;
1562 }
1563 
1564 static HRESULT WINAPI JoystickAImpl_SendForceFeedbackCommand(IDirectInputDevice8A *iface,
1565         DWORD flags)
1566 {
1567     JoystickImpl *This = impl_from_IDirectInputDevice8A(iface);
1568 
1569     TRACE("%p 0x%x\n", This, flags);
1570 
1571     return JoystickWImpl_SendForceFeedbackCommand(&This->generic.base.IDirectInputDevice8W_iface, flags);
1572 }
1573 
1574 const struct dinput_device joystick_osx_device = {
1575   "Wine OS X joystick driver",
1576   joydev_enum_deviceA,
1577   joydev_enum_deviceW,
1578   joydev_create_device
1579 };
1580 
1581 static const IDirectInputDevice8AVtbl JoystickAvt =
1582 {
1583     IDirectInputDevice2AImpl_QueryInterface,
1584     IDirectInputDevice2AImpl_AddRef,
1585     IDirectInputDevice2AImpl_Release,
1586     JoystickAGenericImpl_GetCapabilities,
1587     IDirectInputDevice2AImpl_EnumObjects,
1588     JoystickAImpl_GetProperty,
1589     JoystickAImpl_SetProperty,
1590     IDirectInputDevice2AImpl_Acquire,
1591     IDirectInputDevice2AImpl_Unacquire,
1592     JoystickAGenericImpl_GetDeviceState,
1593     IDirectInputDevice2AImpl_GetDeviceData,
1594     IDirectInputDevice2AImpl_SetDataFormat,
1595     IDirectInputDevice2AImpl_SetEventNotification,
1596     IDirectInputDevice2AImpl_SetCooperativeLevel,
1597     JoystickAGenericImpl_GetObjectInfo,
1598     JoystickAGenericImpl_GetDeviceInfo,
1599     IDirectInputDevice2AImpl_RunControlPanel,
1600     IDirectInputDevice2AImpl_Initialize,
1601     JoystickAImpl_CreateEffect,
1602     IDirectInputDevice2AImpl_EnumEffects,
1603     IDirectInputDevice2AImpl_GetEffectInfo,
1604     IDirectInputDevice2AImpl_GetForceFeedbackState,
1605     JoystickAImpl_SendForceFeedbackCommand,
1606     IDirectInputDevice2AImpl_EnumCreatedEffectObjects,
1607     IDirectInputDevice2AImpl_Escape,
1608     JoystickAGenericImpl_Poll,
1609     IDirectInputDevice2AImpl_SendDeviceData,
1610     IDirectInputDevice7AImpl_EnumEffectsInFile,
1611     IDirectInputDevice7AImpl_WriteEffectToFile,
1612     JoystickAGenericImpl_BuildActionMap,
1613     JoystickAGenericImpl_SetActionMap,
1614     IDirectInputDevice8AImpl_GetImageInfo
1615 };
1616 
1617 static const IDirectInputDevice8WVtbl JoystickWvt =
1618 {
1619     IDirectInputDevice2WImpl_QueryInterface,
1620     IDirectInputDevice2WImpl_AddRef,
1621     IDirectInputDevice2WImpl_Release,
1622     JoystickWGenericImpl_GetCapabilities,
1623     IDirectInputDevice2WImpl_EnumObjects,
1624     JoystickWImpl_GetProperty,
1625     JoystickWImpl_SetProperty,
1626     IDirectInputDevice2WImpl_Acquire,
1627     IDirectInputDevice2WImpl_Unacquire,
1628     JoystickWGenericImpl_GetDeviceState,
1629     IDirectInputDevice2WImpl_GetDeviceData,
1630     IDirectInputDevice2WImpl_SetDataFormat,
1631     IDirectInputDevice2WImpl_SetEventNotification,
1632     IDirectInputDevice2WImpl_SetCooperativeLevel,
1633     JoystickWGenericImpl_GetObjectInfo,
1634     JoystickWGenericImpl_GetDeviceInfo,
1635     IDirectInputDevice2WImpl_RunControlPanel,
1636     IDirectInputDevice2WImpl_Initialize,
1637     JoystickWImpl_CreateEffect,
1638     IDirectInputDevice2WImpl_EnumEffects,
1639     IDirectInputDevice2WImpl_GetEffectInfo,
1640     IDirectInputDevice2WImpl_GetForceFeedbackState,
1641     JoystickWImpl_SendForceFeedbackCommand,
1642     IDirectInputDevice2WImpl_EnumCreatedEffectObjects,
1643     IDirectInputDevice2WImpl_Escape,
1644     JoystickWGenericImpl_Poll,
1645     IDirectInputDevice2WImpl_SendDeviceData,
1646     IDirectInputDevice7WImpl_EnumEffectsInFile,
1647     IDirectInputDevice7WImpl_WriteEffectToFile,
1648     JoystickWGenericImpl_BuildActionMap,
1649     JoystickWGenericImpl_SetActionMap,
1650     IDirectInputDevice8WImpl_GetImageInfo
1651 };
1652 
1653 static HRESULT WINAPI effect_QueryInterface(IDirectInputEffect *iface,
1654         const GUID *guid, void **out)
1655 {
1656     EffectImpl *This = impl_from_IDirectInputEffect(iface);
1657 
1658     TRACE("%p %s %p\n", This, debugstr_guid(guid), out);
1659 
1660     if(IsEqualIID(guid, &IID_IUnknown) || IsEqualIID(guid, &IID_IDirectInputEffect)){
1661         *out = iface;
1662         IDirectInputEffect_AddRef(iface);
1663         return S_OK;
1664     }
1665 
1666     return E_NOINTERFACE;
1667 }
1668 
1669 static ULONG WINAPI effect_AddRef(IDirectInputEffect *iface)
1670 {
1671     EffectImpl *This = impl_from_IDirectInputEffect(iface);
1672     ULONG ref = InterlockedIncrement(&This->ref);
1673     TRACE("%p, ref is now: %u\n", This, ref);
1674     return ref;
1675 }
1676 
1677 static ULONG WINAPI effect_Release(IDirectInputEffect *iface)
1678 {
1679     EffectImpl *This = impl_from_IDirectInputEffect(iface);
1680     ULONG ref = InterlockedDecrement(&This->ref);
1681     TRACE("%p, ref is now: %u\n", This, ref);
1682 
1683     if(!ref){
1684         list_remove(&This->entry);
1685         FFDeviceReleaseEffect(This->device->ff, This->effect);
1686         HeapFree(GetProcessHeap(), 0, This);
1687     }
1688 
1689     return ref;
1690 }
1691 
1692 static HRESULT WINAPI effect_Initialize(IDirectInputEffect *iface, HINSTANCE hinst,
1693         DWORD version, const GUID *guid)
1694 {
1695     EffectImpl *This = impl_from_IDirectInputEffect(iface);
1696     TRACE("%p %p 0x%x, %s\n", This, hinst, version, debugstr_guid(guid));
1697     return S_OK;
1698 }
1699 
1700 static HRESULT WINAPI effect_GetEffectGuid(IDirectInputEffect *iface, GUID *out)
1701 {
1702     EffectImpl *This = impl_from_IDirectInputEffect(iface);
1703     TRACE("%p %p\n", This, out);
1704     *out = This->guid;
1705     return S_OK;
1706 }
1707 
1708 static HRESULT WINAPI effect_GetParameters(IDirectInputEffect *iface,
1709         DIEFFECT *effect, DWORD flags)
1710 {
1711     EffectImpl *This = impl_from_IDirectInputEffect(iface);
1712     TRACE("%p %p 0x%x\n", This, effect, flags);
1713     return osx_to_win32_hresult(FFEffectGetParameters(This->effect, (FFEFFECT*)effect, flags));
1714 }
1715 
1716 static HRESULT WINAPI effect_SetParameters(IDirectInputEffect *iface,
1717         const DIEFFECT *effect, DWORD flags)
1718 {
1719     EffectImpl *This = impl_from_IDirectInputEffect(iface);
1720     TRACE("%p %p 0x%x\n", This, effect, flags);
1721     dump_DIEFFECT(effect, &This->guid, flags);
1722     return osx_to_win32_hresult(FFEffectSetParameters(This->effect, (FFEFFECT*)effect, flags));
1723 }
1724 
1725 static HRESULT WINAPI effect_Start(IDirectInputEffect *iface, DWORD iterations,
1726         DWORD flags)
1727 {
1728     EffectImpl *This = impl_from_IDirectInputEffect(iface);
1729     TRACE("%p 0x%x 0x%x\n", This, iterations, flags);
1730     return osx_to_win32_hresult(FFEffectStart(This->effect, iterations, flags));
1731 }
1732 
1733 static HRESULT WINAPI effect_Stop(IDirectInputEffect *iface)
1734 {
1735     EffectImpl *This = impl_from_IDirectInputEffect(iface);
1736     TRACE("%p\n", This);
1737     return osx_to_win32_hresult(FFEffectStop(This->effect));
1738 }
1739 
1740 static HRESULT WINAPI effect_GetEffectStatus(IDirectInputEffect *iface, DWORD *flags)
1741 {
1742     EffectImpl *This = impl_from_IDirectInputEffect(iface);
1743     TRACE("%p %p\n", This, flags);
1744     return osx_to_win32_hresult(FFEffectGetEffectStatus(This->effect, (UInt32*)flags));
1745 }
1746 
1747 static HRESULT WINAPI effect_Download(IDirectInputEffect *iface)
1748 {
1749     EffectImpl *This = impl_from_IDirectInputEffect(iface);
1750     TRACE("%p\n", This);
1751     return osx_to_win32_hresult(FFEffectDownload(This->effect));
1752 }
1753 
1754 static HRESULT WINAPI effect_Unload(IDirectInputEffect *iface)
1755 {
1756     EffectImpl *This = impl_from_IDirectInputEffect(iface);
1757     TRACE("%p\n", This);
1758     return osx_to_win32_hresult(FFEffectUnload(This->effect));
1759 }
1760 
1761 static HRESULT WINAPI effect_Escape(IDirectInputEffect *iface, DIEFFESCAPE *escape)
1762 {
1763     EffectImpl *This = impl_from_IDirectInputEffect(iface);
1764     TRACE("%p %p\n", This, escape);
1765     return osx_to_win32_hresult(FFEffectEscape(This->effect, (FFEFFESCAPE*)escape));
1766 }
1767 
1768 static const IDirectInputEffectVtbl EffectVtbl = {
1769     effect_QueryInterface,
1770     effect_AddRef,
1771     effect_Release,
1772     effect_Initialize,
1773     effect_GetEffectGuid,
1774     effect_GetParameters,
1775     effect_SetParameters,
1776     effect_Start,
1777     effect_Stop,
1778     effect_GetEffectStatus,
1779     effect_Download,
1780     effect_Unload,
1781     effect_Escape
1782 };
1783 
1784 #else /* HAVE_IOHIDMANAGERCREATE */
1785 
1786 const struct dinput_device joystick_osx_device = {
1787   "Wine OS X joystick driver",
1788   NULL,
1789   NULL,
1790   NULL
1791 };
1792 
1793 #endif /* HAVE_IOHIDMANAGERCREATE */
1794