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