msxml3: COM cleanup in domdoc.c.
[wine] / dlls / dinput / joystick_osx.c
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 ULONG __carbon_ULONG
28 #define E_INVALIDARG __carbon_E_INVALIDARG
29 #define E_OUTOFMEMORY __carbon_E_OUTOFMEMORY
30 #define E_HANDLE __carbon_E_HANDLE
31 #define E_ACCESSDENIED __carbon_E_ACCESSDENIED
32 #define E_UNEXPECTED __carbon_E_UNEXPECTED
33 #define E_FAIL __carbon_E_FAIL
34 #define E_ABORT __carbon_E_ABORT
35 #define E_POINTER __carbon_E_POINTER
36 #define E_NOINTERFACE __carbon_E_NOINTERFACE
37 #define E_NOTIMPL __carbon_E_NOTIMPL
38 #define S_FALSE __carbon_S_FALSE
39 #define S_OK __carbon_S_OK
40 #define HRESULT_FACILITY __carbon_HRESULT_FACILITY
41 #define IS_ERROR __carbon_IS_ERROR
42 #define FAILED __carbon_FAILED
43 #define SUCCEEDED __carbon_SUCCEEDED
44 #define MAKE_HRESULT __carbon_MAKE_HRESULT
45 #define HRESULT __carbon_HRESULT
46 #define STDMETHODCALLTYPE __carbon_STDMETHODCALLTYPE
47 #include <IOKit/hid/IOHIDLib.h>
48 #undef ULONG
49 #undef E_INVALIDARG
50 #undef E_OUTOFMEMORY
51 #undef E_HANDLE
52 #undef E_ACCESSDENIED
53 #undef E_UNEXPECTED
54 #undef E_FAIL
55 #undef E_ABORT
56 #undef E_POINTER
57 #undef E_NOINTERFACE
58 #undef E_NOTIMPL
59 #undef S_FALSE
60 #undef S_OK
61 #undef HRESULT_FACILITY
62 #undef IS_ERROR
63 #undef FAILED
64 #undef SUCCEEDED
65 #undef MAKE_HRESULT
66 #undef HRESULT
67 #undef STDMETHODCALLTYPE
68 #endif /* HAVE_IOKIT_HID_IOHIDLIB_H */
69
70 #include "wine/debug.h"
71 #include "wine/unicode.h"
72 #include "windef.h"
73 #include "winbase.h"
74 #include "winerror.h"
75 #include "winreg.h"
76 #include "dinput.h"
77
78 #include "dinput_private.h"
79 #include "device_private.h"
80 #include "joystick_private.h"
81
82 WINE_DEFAULT_DEBUG_CHANNEL(dinput);
83
84 #ifdef HAVE_IOHIDMANAGERCREATE
85
86 static IOHIDManagerRef gIOHIDManagerRef = NULL;
87 static CFArrayRef gCollections = NULL;
88
89 typedef struct JoystickImpl JoystickImpl;
90 static const IDirectInputDevice8AVtbl JoystickAvt;
91 static const IDirectInputDevice8WVtbl JoystickWvt;
92
93 struct JoystickImpl
94 {
95     struct JoystickGenericImpl generic;
96
97     /* osx private */
98     int                    id;
99     CFMutableArrayRef      elementCFArrayRef;
100     ObjProps               **propmap;
101 };
102
103 static inline JoystickImpl *impl_from_IDirectInputDevice8A(IDirectInputDevice8A *iface)
104 {
105     return CONTAINING_RECORD(CONTAINING_RECORD(CONTAINING_RECORD(iface, IDirectInputDeviceImpl, IDirectInputDevice8A_iface),
106            JoystickGenericImpl, base), JoystickImpl, generic);
107 }
108 static inline JoystickImpl *impl_from_IDirectInputDevice8W(IDirectInputDevice8W *iface)
109 {
110     return CONTAINING_RECORD(CONTAINING_RECORD(CONTAINING_RECORD(iface, IDirectInputDeviceImpl, IDirectInputDevice8W_iface),
111            JoystickGenericImpl, base), JoystickImpl, generic);
112 }
113
114 static const GUID DInput_Wine_OsX_Joystick_GUID = { /* 59CAD8F6-E617-41E2-8EB7-47B23EEEDC5A */
115   0x59CAD8F6, 0xE617, 0x41E2, {0x8E, 0xB7, 0x47, 0xB2, 0x3E, 0xEE, 0xDC, 0x5A}
116 };
117
118 static void CFSetApplierFunctionCopyToCFArray(const void *value, void *context)
119 {
120     CFArrayAppendValue( ( CFMutableArrayRef ) context, value );
121 }
122
123 static CFMutableDictionaryRef creates_osx_device_match(int usage)
124 {
125     CFMutableDictionaryRef result;
126
127     result = CFDictionaryCreateMutable( kCFAllocatorDefault, 0,
128             &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks );
129
130     if ( result )
131     {
132         int number = kHIDPage_GenericDesktop;
133         CFNumberRef pageCFNumberRef = CFNumberCreate( kCFAllocatorDefault,
134                           kCFNumberIntType, &number);
135
136         if ( pageCFNumberRef )
137         {
138             CFNumberRef usageCFNumberRef;
139
140             CFDictionarySetValue( result, CFSTR( kIOHIDDeviceUsagePageKey ),
141                 pageCFNumberRef );
142             CFRelease( pageCFNumberRef );
143
144             usageCFNumberRef = CFNumberCreate( kCFAllocatorDefault,
145                         kCFNumberIntType, &usage);
146             if ( usageCFNumberRef )
147             {
148                 CFDictionarySetValue( result, CFSTR( kIOHIDDeviceUsageKey ),
149                     usageCFNumberRef );
150                 CFRelease( usageCFNumberRef );
151             }
152             else
153             {
154                 ERR("CFNumberCreate() failed.\n");
155                 return NULL;
156             }
157         }
158         else
159         {
160             ERR("CFNumberCreate failed.\n");
161             return NULL;
162         }
163     }
164     else
165     {
166         ERR("CFDictionaryCreateMutable failed.\n");
167         return NULL;
168     }
169
170     return result;
171 }
172
173 static CFIndex find_top_level(IOHIDDeviceRef tIOHIDDeviceRef, CFArrayRef topLevels)
174 {
175     CFArrayRef      gElementCFArrayRef;
176     CFIndex         numTops = 0;
177
178     if (!tIOHIDDeviceRef)
179         return 0;
180
181     gElementCFArrayRef = IOHIDDeviceCopyMatchingElements(tIOHIDDeviceRef, NULL, 0);
182
183     if (gElementCFArrayRef)
184     {
185         CFIndex idx, cnt = CFArrayGetCount(gElementCFArrayRef);
186         for (idx=0; idx<cnt; idx++)
187         {
188             IOHIDElementRef tIOHIDElementRef = (IOHIDElementRef)CFArrayGetValueAtIndex(gElementCFArrayRef, idx);
189             int eleType = IOHIDElementGetType(tIOHIDElementRef);
190
191             /* Check for top-level gaming device collections */
192             if (eleType == kIOHIDElementTypeCollection && IOHIDElementGetParent(tIOHIDElementRef) == 0)
193             {
194                 int tUsagePage = IOHIDElementGetUsagePage(tIOHIDElementRef);
195                 int tUsage = IOHIDElementGetUsage(tIOHIDElementRef);
196
197                 if (tUsagePage == kHIDPage_GenericDesktop &&
198                      (tUsage == kHIDUsage_GD_Joystick || tUsage == kHIDUsage_GD_GamePad))
199                 {
200                     CFArrayAppendValue((CFMutableArrayRef)topLevels, tIOHIDElementRef);
201                     numTops++;
202                 }
203             }
204         }
205     }
206     return numTops;
207 }
208
209 static void get_element_children(IOHIDElementRef tElement, CFArrayRef childElements)
210 {
211     CFIndex    idx, cnt;
212     CFArrayRef tElementChildrenArray = IOHIDElementGetChildren(tElement);
213
214     cnt = CFArrayGetCount(tElementChildrenArray);
215     if (cnt < 1)
216         return;
217
218     /* Either add the element to the array or grab its children */
219     for (idx=0; idx<cnt; idx++)
220     {
221         IOHIDElementRef tChildElementRef;
222
223         tChildElementRef = (IOHIDElementRef)CFArrayGetValueAtIndex(tElementChildrenArray, idx);
224         if (IOHIDElementGetType(tChildElementRef) == kIOHIDElementTypeCollection)
225             get_element_children(tChildElementRef, childElements);
226         else
227             CFArrayAppendValue((CFMutableArrayRef)childElements, tChildElementRef);
228     }
229 }
230
231 static int find_osx_devices(void)
232 {
233     IOReturn tIOReturn;
234     CFMutableDictionaryRef result;
235     CFSetRef devset;
236     CFArrayRef matching;
237
238     gIOHIDManagerRef = IOHIDManagerCreate( kCFAllocatorDefault, 0L );
239     tIOReturn = IOHIDManagerOpen( gIOHIDManagerRef, 0L);
240     if ( kIOReturnSuccess != tIOReturn )
241     {
242         ERR("Couldn't open IOHIDManager.\n");
243         return 0;
244     }
245
246      matching = CFArrayCreateMutable( kCFAllocatorDefault, 0,
247                         &kCFTypeArrayCallBacks );
248
249     /* build matching dictionary */
250     result = creates_osx_device_match(kHIDUsage_GD_Joystick);
251     if (!result)
252     {
253         CFRelease(matching);
254         return 0;
255     }
256     CFArrayAppendValue( ( CFMutableArrayRef )matching, result );
257     result = creates_osx_device_match(kHIDUsage_GD_GamePad);
258     if (!result)
259     {
260         CFRelease(matching);
261         return 0;
262     }
263     CFArrayAppendValue( ( CFMutableArrayRef )matching, result );
264
265     IOHIDManagerSetDeviceMatchingMultiple( gIOHIDManagerRef, matching);
266     devset = IOHIDManagerCopyDevices( gIOHIDManagerRef );
267     if (devset)
268     {
269         CFIndex countDevices, countCollections, idx;
270         CFArrayRef gDevices = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
271         CFSetApplyFunction(devset, CFSetApplierFunctionCopyToCFArray, (void*)gDevices);
272         CFRelease( devset);
273         countDevices = CFArrayGetCount(gDevices);
274
275         gCollections = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
276         if (!gCollections)
277             return 0;
278
279         countCollections = 0;
280         for (idx = 0; idx < countDevices; idx++)
281         {
282             CFIndex tTop;
283             IOHIDDeviceRef tDevice;
284
285             tDevice = (IOHIDDeviceRef) CFArrayGetValueAtIndex(gDevices, idx);
286             tTop = find_top_level(tDevice, gCollections);
287             countCollections += tTop;
288         }
289
290         CFRelease(gDevices);
291
292         TRACE("found %i device(s), %i collection(s)\n",(int)countDevices,(int)countCollections);
293         return (int)countCollections;
294     }
295     return 0;
296 }
297
298 static int get_osx_device_name(int id, char *name, int length)
299 {
300     CFStringRef str;
301     IOHIDElementRef tIOHIDElementRef;
302     IOHIDDeviceRef tIOHIDDeviceRef;
303
304     if (!gCollections)
305         return 0;
306
307     tIOHIDElementRef = (IOHIDElementRef)CFArrayGetValueAtIndex(gCollections, id);
308
309     if (!tIOHIDElementRef)
310     {
311         ERR("Invalid Element requested %i\n",id);
312         return 0;
313     }
314
315     tIOHIDDeviceRef = IOHIDElementGetDevice(tIOHIDElementRef);
316
317     if (name)
318         name[0] = 0;
319
320     if (!tIOHIDDeviceRef)
321     {
322         ERR("Invalid Device requested %i\n",id);
323         return 0;
324     }
325
326     str = IOHIDDeviceGetProperty(tIOHIDDeviceRef, CFSTR( kIOHIDProductKey ));
327     if (str)
328     {
329         CFIndex len = CFStringGetLength(str);
330         if (length >= len)
331         {
332             CFStringGetCString(str,name,length,kCFStringEncodingASCII);
333             return len;
334         }
335         else
336             return (len+1);
337     }
338     return 0;
339 }
340
341 static void insert_sort_button(int header, IOHIDElementRef tIOHIDElementRef,
342                                 CFMutableArrayRef elementCFArrayRef, int index,
343                                 int target)
344 {
345     IOHIDElementRef targetElement;
346     int usage;
347
348     CFArraySetValueAtIndex(elementCFArrayRef, header+index, NULL);
349     targetElement = ( IOHIDElementRef ) CFArrayGetValueAtIndex( elementCFArrayRef, header+target);
350     if (targetElement == NULL)
351     {
352         CFArraySetValueAtIndex(elementCFArrayRef, header+target,tIOHIDElementRef);
353         return;
354     }
355     usage = IOHIDElementGetUsage( targetElement );
356     usage --; /* usage 1 based index */
357
358     insert_sort_button(header, targetElement, elementCFArrayRef, target, usage);
359     CFArraySetValueAtIndex(elementCFArrayRef, header+target,tIOHIDElementRef);
360 }
361
362 static void get_osx_device_elements(JoystickImpl *device, int axis_map[8])
363 {
364     IOHIDElementRef tIOHIDElementRef;
365     CFArrayRef      gElementCFArrayRef;
366     DWORD           axes = 0;
367     DWORD           sliders = 0;
368     DWORD           buttons = 0;
369     DWORD           povs = 0;
370
371     device->elementCFArrayRef = NULL;
372
373     if (!gCollections)
374         return;
375
376     tIOHIDElementRef = (IOHIDElementRef)CFArrayGetValueAtIndex(gCollections, device->id);
377
378     if (!tIOHIDElementRef)
379         return;
380
381     gElementCFArrayRef = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
382     get_element_children(tIOHIDElementRef, gElementCFArrayRef);
383
384     if (gElementCFArrayRef)
385     {
386         CFIndex idx, cnt = CFArrayGetCount( gElementCFArrayRef );
387         /* build our element array in the order that dinput expects */
388         device->elementCFArrayRef = CFArrayCreateMutable(NULL,0,NULL);
389
390         for ( idx = 0; idx < cnt; idx++ )
391         {
392             IOHIDElementRef tIOHIDElementRef = ( IOHIDElementRef ) CFArrayGetValueAtIndex( gElementCFArrayRef, idx );
393             int eleType = IOHIDElementGetType( tIOHIDElementRef );
394             switch(eleType)
395             {
396                 case kIOHIDElementTypeInput_Button:
397                 {
398                     int usagePage = IOHIDElementGetUsagePage( tIOHIDElementRef );
399                     if (usagePage != kHIDPage_Button)
400                     {
401                         /* avoid strange elements found on the 360 controler */
402                         continue;
403                     }
404
405                     if (buttons < 128)
406                     {
407                         CFArrayInsertValueAtIndex(device->elementCFArrayRef, (axes+povs+buttons), tIOHIDElementRef);
408                         buttons++;
409                     }
410                     break;
411                 }
412                 case kIOHIDElementTypeInput_Axis:
413                 {
414                     CFArrayInsertValueAtIndex(device->elementCFArrayRef, axes, tIOHIDElementRef);
415                     axes++;
416                     break;
417                 }
418                 case kIOHIDElementTypeInput_Misc:
419                 {
420                     uint32_t usage = IOHIDElementGetUsage( tIOHIDElementRef );
421                     switch(usage)
422                     {
423                         case kHIDUsage_GD_Hatswitch:
424                         {
425                             CFArrayInsertValueAtIndex(device->elementCFArrayRef, (axes+povs), tIOHIDElementRef);
426                             povs++;
427                             break;
428                         }
429                         case kHIDUsage_GD_Slider:
430                             sliders ++;
431                             if (sliders > 2)
432                                 break;
433                             /* fallthrough, sliders are axis */
434                         case kHIDUsage_GD_X:
435                         case kHIDUsage_GD_Y:
436                         case kHIDUsage_GD_Z:
437                         case kHIDUsage_GD_Rx:
438                         case kHIDUsage_GD_Ry:
439                         case kHIDUsage_GD_Rz:
440                         {
441                             CFArrayInsertValueAtIndex(device->elementCFArrayRef, axes, tIOHIDElementRef);
442                             axis_map[axes]=usage;
443                             axes++;
444                             break;
445                         }
446                         default:
447                             FIXME("Unhandled usage %i\n",usage);
448                     }
449                     break;
450                 }
451                 default:
452                     FIXME("Unhandled type %i\n",eleType);
453             }
454         }
455     }
456
457     device->generic.devcaps.dwAxes = axes;
458     device->generic.devcaps.dwButtons = buttons;
459     device->generic.devcaps.dwPOVs = povs;
460
461     /* Sort buttons into correct order */
462     for (buttons = 0; buttons < device->generic.devcaps.dwButtons; buttons++)
463     {
464         IOHIDElementRef tIOHIDElementRef = ( IOHIDElementRef ) CFArrayGetValueAtIndex( device->elementCFArrayRef, axes+povs+buttons);
465         uint32_t usage = IOHIDElementGetUsage( tIOHIDElementRef );
466         usage --; /* usage is 1 indexed we need 0 indexed */
467         if (usage == buttons)
468             continue;
469
470         insert_sort_button(axes+povs, tIOHIDElementRef, device->elementCFArrayRef,buttons,usage);
471     }
472 }
473
474 static void get_osx_device_elements_props(JoystickImpl *device)
475 {
476     CFArrayRef gElementCFArrayRef = device->elementCFArrayRef;
477
478     if (gElementCFArrayRef)
479     {
480         CFIndex idx, cnt = CFArrayGetCount( gElementCFArrayRef );
481
482         for ( idx = 0; idx < cnt; idx++ )
483         {
484             IOHIDElementRef tIOHIDElementRef = ( IOHIDElementRef ) CFArrayGetValueAtIndex( gElementCFArrayRef, idx );
485
486             device->generic.props[idx].lDevMin = IOHIDElementGetLogicalMin(tIOHIDElementRef);
487             device->generic.props[idx].lDevMax = IOHIDElementGetLogicalMax(tIOHIDElementRef);
488             device->generic.props[idx].lMin =  0;
489             device->generic.props[idx].lMax =  0xffff;
490             device->generic.props[idx].lDeadZone = 0;
491             device->generic.props[idx].lSaturation = 0;
492         }
493     }
494 }
495
496 static void poll_osx_device_state(LPDIRECTINPUTDEVICE8A iface)
497 {
498     JoystickImpl *device = impl_from_IDirectInputDevice8A(iface);
499     IOHIDElementRef tIOHIDTopElementRef;
500     IOHIDDeviceRef tIOHIDDeviceRef;
501     CFArrayRef gElementCFArrayRef = device->elementCFArrayRef;
502
503     TRACE("polling device %i\n",device->id);
504
505     if (!gCollections)
506         return;
507
508     tIOHIDTopElementRef = (IOHIDElementRef) CFArrayGetValueAtIndex(gCollections, device->id);
509     tIOHIDDeviceRef = IOHIDElementGetDevice(tIOHIDTopElementRef);
510
511     if (!tIOHIDDeviceRef)
512         return;
513
514     if (gElementCFArrayRef)
515     {
516         int button_idx = 0;
517         int pov_idx = 0;
518         int slider_idx = 0;
519         CFIndex idx, cnt = CFArrayGetCount( gElementCFArrayRef );
520
521         for ( idx = 0; idx < cnt; idx++ )
522         {
523             IOHIDValueRef valueRef;
524             int val;
525             IOHIDElementRef tIOHIDElementRef = ( IOHIDElementRef ) CFArrayGetValueAtIndex( gElementCFArrayRef, idx );
526             int eleType = IOHIDElementGetType( tIOHIDElementRef );
527
528             switch(eleType)
529             {
530                 case kIOHIDElementTypeInput_Button:
531                     if(button_idx < 128)
532                     {
533                         IOHIDDeviceGetValue(tIOHIDDeviceRef, tIOHIDElementRef, &valueRef);
534                         val = IOHIDValueGetIntegerValue(valueRef);
535                         device->generic.js.rgbButtons[button_idx] = val ? 0x80 : 0x00;
536                         button_idx ++;
537                     }
538                     break;
539                 case kIOHIDElementTypeInput_Misc:
540                 {
541                     uint32_t usage = IOHIDElementGetUsage( tIOHIDElementRef );
542                     switch(usage)
543                     {
544                         case kHIDUsage_GD_Hatswitch:
545                         {
546                             IOHIDDeviceGetValue(tIOHIDDeviceRef, tIOHIDElementRef, &valueRef);
547                             val = IOHIDValueGetIntegerValue(valueRef);
548                             if (val >= 8)
549                                 device->generic.js.rgdwPOV[pov_idx] = -1;
550                             else
551                                 device->generic.js.rgdwPOV[pov_idx] = val * 4500;
552                             pov_idx ++;
553                             break;
554                         }
555                         case kHIDUsage_GD_X:
556                         case kHIDUsage_GD_Y:
557                         case kHIDUsage_GD_Z:
558                         case kHIDUsage_GD_Rx:
559                         case kHIDUsage_GD_Ry:
560                         case kHIDUsage_GD_Rz:
561                         case kHIDUsage_GD_Slider:
562                         {
563                             IOHIDDeviceGetValue(tIOHIDDeviceRef, tIOHIDElementRef, &valueRef);
564                             val = IOHIDValueGetIntegerValue(valueRef);
565                             switch (usage)
566                             {
567                             case kHIDUsage_GD_X:
568                                 device->generic.js.lX = joystick_map_axis(&device->generic.props[idx], val);
569                                 break;
570                             case kHIDUsage_GD_Y:
571                                 device->generic.js.lY = joystick_map_axis(&device->generic.props[idx], val);
572                                 break;
573                             case kHIDUsage_GD_Z:
574                                 device->generic.js.lZ = joystick_map_axis(&device->generic.props[idx], val);
575                                 break;
576                             case kHIDUsage_GD_Rx:
577                                 device->generic.js.lRx = joystick_map_axis(&device->generic.props[idx], val);
578                                 break;
579                             case kHIDUsage_GD_Ry:
580                                 device->generic.js.lRy = joystick_map_axis(&device->generic.props[idx], val);
581                                 break;
582                             case kHIDUsage_GD_Rz:
583                                 device->generic.js.lRz = joystick_map_axis(&device->generic.props[idx], val);
584                                 break;
585                             case kHIDUsage_GD_Slider:
586                                 device->generic.js.rglSlider[slider_idx] = joystick_map_axis(&device->generic.props[idx], val);
587                                 slider_idx ++;
588                                 break;
589                             }
590                             break;
591                         }
592                         default:
593                             FIXME("unhandled usage %i\n",usage);
594                     }
595                     break;
596                 }
597                 default:
598                     FIXME("Unhandled type %i\n",eleType);
599             }
600         }
601     }
602 }
603
604 static INT find_joystick_devices(void)
605 {
606     static INT joystick_devices_count = -1;
607
608     if (joystick_devices_count != -1) return joystick_devices_count;
609
610     joystick_devices_count = find_osx_devices();
611
612     return  joystick_devices_count;
613 }
614
615 static BOOL joydev_enum_deviceA(DWORD dwDevType, DWORD dwFlags, LPDIDEVICEINSTANCEA lpddi, DWORD version, int id)
616 {
617     if (id >= find_joystick_devices()) return FALSE;
618
619     if (dwFlags & DIEDFL_FORCEFEEDBACK) {
620         WARN("force feedback not supported\n");
621         return FALSE;
622     }
623
624     if ((dwDevType == 0) ||
625     ((dwDevType == DIDEVTYPE_JOYSTICK) && (version > 0x0300 && version < 0x0800)) ||
626     (((dwDevType == DI8DEVCLASS_GAMECTRL) || (dwDevType == DI8DEVTYPE_JOYSTICK)) && (version >= 0x0800)))
627     {
628         /* Return joystick */
629         lpddi->guidInstance = DInput_Wine_OsX_Joystick_GUID;
630         lpddi->guidInstance.Data3 = id;
631         lpddi->guidProduct = DInput_Wine_OsX_Joystick_GUID;
632         /* we only support traditional joysticks for now */
633         if (version >= 0x0800)
634             lpddi->dwDevType = DI8DEVTYPE_JOYSTICK | (DI8DEVTYPEJOYSTICK_STANDARD << 8);
635         else
636             lpddi->dwDevType = DIDEVTYPE_JOYSTICK | (DIDEVTYPEJOYSTICK_TRADITIONAL << 8);
637         sprintf(lpddi->tszInstanceName, "Joystick %d", id);
638
639         /* get the device name */
640         get_osx_device_name(id, lpddi->tszProductName, MAX_PATH);
641
642         lpddi->guidFFDriver = GUID_NULL;
643         return TRUE;
644     }
645
646     return FALSE;
647 }
648
649 static BOOL joydev_enum_deviceW(DWORD dwDevType, DWORD dwFlags, LPDIDEVICEINSTANCEW lpddi, DWORD version, int id)
650 {
651     char name[MAX_PATH];
652     char friendly[32];
653
654     if (id >= find_joystick_devices()) return FALSE;
655
656     if (dwFlags & DIEDFL_FORCEFEEDBACK) {
657         WARN("force feedback not supported\n");
658         return FALSE;
659     }
660
661     if ((dwDevType == 0) ||
662     ((dwDevType == DIDEVTYPE_JOYSTICK) && (version > 0x0300 && version < 0x0800)) ||
663     (((dwDevType == DI8DEVCLASS_GAMECTRL) || (dwDevType == DI8DEVTYPE_JOYSTICK)) && (version >= 0x0800))) {
664         /* Return joystick */
665         lpddi->guidInstance = DInput_Wine_OsX_Joystick_GUID;
666         lpddi->guidInstance.Data3 = id;
667         lpddi->guidProduct = DInput_Wine_OsX_Joystick_GUID;
668         /* we only support traditional joysticks for now */
669         if (version >= 0x0800)
670             lpddi->dwDevType = DI8DEVTYPE_JOYSTICK | (DI8DEVTYPEJOYSTICK_STANDARD << 8);
671         else
672             lpddi->dwDevType = DIDEVTYPE_JOYSTICK | (DIDEVTYPEJOYSTICK_TRADITIONAL << 8);
673         sprintf(friendly, "Joystick %d", id);
674         MultiByteToWideChar(CP_ACP, 0, friendly, -1, lpddi->tszInstanceName, MAX_PATH);
675         /* get the device name */
676         get_osx_device_name(id, name, MAX_PATH);
677
678         MultiByteToWideChar(CP_ACP, 0, name, -1, lpddi->tszProductName, MAX_PATH);
679         lpddi->guidFFDriver = GUID_NULL;
680         return TRUE;
681     }
682
683     return FALSE;
684 }
685
686 static HRESULT alloc_device(REFGUID rguid, IDirectInputImpl *dinput,
687                             JoystickImpl **pdev, unsigned short index)
688 {
689     DWORD i;
690     JoystickImpl* newDevice;
691     char name[MAX_PATH];
692     HRESULT hr;
693     LPDIDATAFORMAT df = NULL;
694     int idx = 0;
695     int axis_map[8]; /* max axes */
696     int slider_count = 0;
697
698     TRACE("%s %p %p %hu\n", debugstr_guid(rguid), dinput, pdev, index);
699
700     newDevice = HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(JoystickImpl));
701     if (newDevice == 0) {
702         WARN("out of memory\n");
703         *pdev = 0;
704         return DIERR_OUTOFMEMORY;
705     }
706
707     newDevice->id = index;
708
709     newDevice->generic.guidInstance = DInput_Wine_OsX_Joystick_GUID;
710     newDevice->generic.guidInstance.Data3 = index;
711     newDevice->generic.guidProduct = DInput_Wine_OsX_Joystick_GUID;
712     newDevice->generic.joy_polldev = poll_osx_device_state;
713
714     /* get the device name */
715     get_osx_device_name(index, name, MAX_PATH);
716     TRACE("Name %s\n",name);
717
718     /* copy the device name */
719     newDevice->generic.name = HeapAlloc(GetProcessHeap(),0,strlen(name) + 1);
720     strcpy(newDevice->generic.name, name);
721
722     memset(axis_map, 0, sizeof(axis_map));
723     get_osx_device_elements(newDevice, axis_map);
724
725     TRACE("%i axes %i buttons %i povs\n",newDevice->generic.devcaps.dwAxes,newDevice->generic.devcaps.dwButtons,newDevice->generic.devcaps.dwPOVs);
726
727     if (newDevice->generic.devcaps.dwButtons > 128)
728     {
729         WARN("Can't support %d buttons. Clamping down to 128\n", newDevice->generic.devcaps.dwButtons);
730         newDevice->generic.devcaps.dwButtons = 128;
731     }
732
733     newDevice->generic.base.IDirectInputDevice8A_iface.lpVtbl = &JoystickAvt;
734     newDevice->generic.base.IDirectInputDevice8W_iface.lpVtbl = &JoystickWvt;
735     newDevice->generic.base.ref = 1;
736     newDevice->generic.base.dinput = dinput;
737     newDevice->generic.base.guid = *rguid;
738     InitializeCriticalSection(&newDevice->generic.base.crit);
739     newDevice->generic.base.crit.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": JoystickImpl*->generic.base.crit");
740
741     /* Create copy of default data format */
742     if (!(df = HeapAlloc(GetProcessHeap(), 0, c_dfDIJoystick2.dwSize))) goto FAILED;
743     memcpy(df, &c_dfDIJoystick2, c_dfDIJoystick2.dwSize);
744
745     df->dwNumObjs = newDevice->generic.devcaps.dwAxes + newDevice->generic.devcaps.dwPOVs + newDevice->generic.devcaps.dwButtons;
746     if (!(df->rgodf = HeapAlloc(GetProcessHeap(), 0, df->dwNumObjs * df->dwObjSize))) goto FAILED;
747
748     for (i = 0; i < newDevice->generic.devcaps.dwAxes; i++)
749     {
750         int wine_obj = -1;
751         switch (axis_map[i])
752         {
753             case kHIDUsage_GD_X: wine_obj = 0; break;
754             case kHIDUsage_GD_Y: wine_obj = 1; break;
755             case kHIDUsage_GD_Z: wine_obj = 2; break;
756             case kHIDUsage_GD_Rx: wine_obj = 3; break;
757             case kHIDUsage_GD_Ry: wine_obj = 4; break;
758             case kHIDUsage_GD_Rz: wine_obj = 5; break;
759             case kHIDUsage_GD_Slider:
760                 wine_obj = 6 + slider_count;
761                 slider_count++;
762                 break;
763         }
764         if (wine_obj < 0 ) continue;
765
766         memcpy(&df->rgodf[idx], &c_dfDIJoystick2.rgodf[wine_obj], df->dwObjSize);
767         df->rgodf[idx++].dwType = DIDFT_MAKEINSTANCE(wine_obj) | DIDFT_ABSAXIS;
768     }
769
770     for (i = 0; i < newDevice->generic.devcaps.dwPOVs; i++)
771     {
772         memcpy(&df->rgodf[idx], &c_dfDIJoystick2.rgodf[i + 8], df->dwObjSize);
773         df->rgodf[idx++].dwType = DIDFT_MAKEINSTANCE(i) | DIDFT_POV;
774     }
775
776     for (i = 0; i < newDevice->generic.devcaps.dwButtons; i++)
777     {
778         memcpy(&df->rgodf[idx], &c_dfDIJoystick2.rgodf[i + 12], df->dwObjSize);
779         df->rgodf[idx  ].pguid = &GUID_Button;
780         df->rgodf[idx++].dwType = DIDFT_MAKEINSTANCE(i) | DIDFT_PSHBUTTON;
781     }
782     newDevice->generic.base.data_format.wine_df = df;
783
784     /* initialize default properties */
785     get_osx_device_elements_props(newDevice);
786
787     IDirectInput_AddRef(&newDevice->generic.base.dinput->IDirectInput7A_iface);
788
789     EnterCriticalSection(&dinput->crit);
790     list_add_tail(&dinput->devices_list, &newDevice->generic.base.entry);
791     LeaveCriticalSection(&dinput->crit);
792
793     newDevice->generic.devcaps.dwSize = sizeof(newDevice->generic.devcaps);
794     newDevice->generic.devcaps.dwFlags = DIDC_ATTACHED;
795     if (newDevice->generic.base.dinput->dwVersion >= 0x0800)
796         newDevice->generic.devcaps.dwDevType = DI8DEVTYPE_JOYSTICK | (DI8DEVTYPEJOYSTICK_STANDARD << 8);
797     else
798         newDevice->generic.devcaps.dwDevType = DIDEVTYPE_JOYSTICK | (DIDEVTYPEJOYSTICK_TRADITIONAL << 8);
799     newDevice->generic.devcaps.dwFFSamplePeriod = 0;
800     newDevice->generic.devcaps.dwFFMinTimeResolution = 0;
801     newDevice->generic.devcaps.dwFirmwareRevision = 0;
802     newDevice->generic.devcaps.dwHardwareRevision = 0;
803     newDevice->generic.devcaps.dwFFDriverVersion = 0;
804
805     if (TRACE_ON(dinput)) {
806         _dump_DIDATAFORMAT(newDevice->generic.base.data_format.wine_df);
807         _dump_DIDEVCAPS(&newDevice->generic.devcaps);
808     }
809
810     *pdev = newDevice;
811
812     return DI_OK;
813
814 FAILED:
815     hr = DIERR_OUTOFMEMORY;
816     if (df) HeapFree(GetProcessHeap(), 0, df->rgodf);
817     HeapFree(GetProcessHeap(), 0, df);
818     release_DataFormat(&newDevice->generic.base.data_format);
819     HeapFree(GetProcessHeap(),0,newDevice->generic.name);
820     HeapFree(GetProcessHeap(),0,newDevice);
821     *pdev = 0;
822
823     return hr;
824 }
825
826 /******************************************************************************
827   *     get_joystick_index : Get the joystick index from a given GUID
828   */
829 static unsigned short get_joystick_index(REFGUID guid)
830 {
831     GUID wine_joystick = DInput_Wine_OsX_Joystick_GUID;
832     GUID dev_guid = *guid;
833
834     wine_joystick.Data3 = 0;
835     dev_guid.Data3 = 0;
836
837     /* for the standard joystick GUID use index 0 */
838     if(IsEqualGUID(&GUID_Joystick,guid)) return 0;
839
840     /* for the wine joystick GUIDs use the index stored in Data3 */
841     if(IsEqualGUID(&wine_joystick, &dev_guid)) return guid->Data3;
842
843     return 0xffff;
844 }
845
846 static HRESULT joydev_create_device(IDirectInputImpl *dinput, REFGUID rguid, REFIID riid, LPVOID *pdev, int unicode)
847 {
848     unsigned short index;
849     int joystick_devices_count;
850
851     TRACE("%p %s %s %p %i\n", dinput, debugstr_guid(rguid), debugstr_guid(riid), pdev, unicode);
852     *pdev = NULL;
853
854     if ((joystick_devices_count = find_joystick_devices()) == 0)
855         return DIERR_DEVICENOTREG;
856
857     if ((index = get_joystick_index(rguid)) < 0xffff &&
858         joystick_devices_count && index < joystick_devices_count)
859     {
860         JoystickImpl *This;
861         HRESULT hr;
862
863         if (riid == NULL)
864             ;/* nothing */
865         else if (IsEqualGUID(&IID_IDirectInputDeviceA,  riid) ||
866                  IsEqualGUID(&IID_IDirectInputDevice2A, riid) ||
867                  IsEqualGUID(&IID_IDirectInputDevice7A, riid) ||
868                  IsEqualGUID(&IID_IDirectInputDevice8A, riid))
869         {
870             unicode = 0;
871         }
872         else if (IsEqualGUID(&IID_IDirectInputDeviceW,  riid) ||
873                  IsEqualGUID(&IID_IDirectInputDevice2W, riid) ||
874                  IsEqualGUID(&IID_IDirectInputDevice7W, riid) ||
875                  IsEqualGUID(&IID_IDirectInputDevice8W, riid))
876         {
877             unicode = 1;
878         }
879         else
880         {
881             WARN("no interface\n");
882             return DIERR_NOINTERFACE;
883         }
884
885         hr = alloc_device(rguid, dinput, &This, index);
886         if (!This) return hr;
887
888         if (unicode)
889             *pdev = &This->generic.base.IDirectInputDevice8W_iface;
890         else
891             *pdev = &This->generic.base.IDirectInputDevice8A_iface;
892         return hr;
893     }
894
895     return DIERR_DEVICENOTREG;
896 }
897
898 const struct dinput_device joystick_osx_device = {
899   "Wine OS X joystick driver",
900   joydev_enum_deviceA,
901   joydev_enum_deviceW,
902   joydev_create_device
903 };
904
905 static const IDirectInputDevice8AVtbl JoystickAvt =
906 {
907     IDirectInputDevice2AImpl_QueryInterface,
908     IDirectInputDevice2AImpl_AddRef,
909     IDirectInputDevice2AImpl_Release,
910     JoystickAGenericImpl_GetCapabilities,
911     IDirectInputDevice2AImpl_EnumObjects,
912     JoystickAGenericImpl_GetProperty,
913     JoystickAGenericImpl_SetProperty,
914     IDirectInputDevice2AImpl_Acquire,
915     IDirectInputDevice2AImpl_Unacquire,
916     JoystickAGenericImpl_GetDeviceState,
917     IDirectInputDevice2AImpl_GetDeviceData,
918     IDirectInputDevice2AImpl_SetDataFormat,
919     IDirectInputDevice2AImpl_SetEventNotification,
920     IDirectInputDevice2AImpl_SetCooperativeLevel,
921     JoystickAGenericImpl_GetObjectInfo,
922     JoystickAGenericImpl_GetDeviceInfo,
923     IDirectInputDevice2AImpl_RunControlPanel,
924     IDirectInputDevice2AImpl_Initialize,
925     IDirectInputDevice2AImpl_CreateEffect,
926     IDirectInputDevice2AImpl_EnumEffects,
927     IDirectInputDevice2AImpl_GetEffectInfo,
928     IDirectInputDevice2AImpl_GetForceFeedbackState,
929     IDirectInputDevice2AImpl_SendForceFeedbackCommand,
930     IDirectInputDevice2AImpl_EnumCreatedEffectObjects,
931     IDirectInputDevice2AImpl_Escape,
932     JoystickAGenericImpl_Poll,
933     IDirectInputDevice2AImpl_SendDeviceData,
934     IDirectInputDevice7AImpl_EnumEffectsInFile,
935     IDirectInputDevice7AImpl_WriteEffectToFile,
936     IDirectInputDevice8AImpl_BuildActionMap,
937     IDirectInputDevice8AImpl_SetActionMap,
938     IDirectInputDevice8AImpl_GetImageInfo
939 };
940
941 static const IDirectInputDevice8WVtbl JoystickWvt =
942 {
943     IDirectInputDevice2WImpl_QueryInterface,
944     IDirectInputDevice2WImpl_AddRef,
945     IDirectInputDevice2WImpl_Release,
946     JoystickWGenericImpl_GetCapabilities,
947     IDirectInputDevice2WImpl_EnumObjects,
948     JoystickWGenericImpl_GetProperty,
949     JoystickWGenericImpl_SetProperty,
950     IDirectInputDevice2WImpl_Acquire,
951     IDirectInputDevice2WImpl_Unacquire,
952     JoystickWGenericImpl_GetDeviceState,
953     IDirectInputDevice2WImpl_GetDeviceData,
954     IDirectInputDevice2WImpl_SetDataFormat,
955     IDirectInputDevice2WImpl_SetEventNotification,
956     IDirectInputDevice2WImpl_SetCooperativeLevel,
957     JoystickWGenericImpl_GetObjectInfo,
958     JoystickWGenericImpl_GetDeviceInfo,
959     IDirectInputDevice2WImpl_RunControlPanel,
960     IDirectInputDevice2WImpl_Initialize,
961     IDirectInputDevice2WImpl_CreateEffect,
962     IDirectInputDevice2WImpl_EnumEffects,
963     IDirectInputDevice2WImpl_GetEffectInfo,
964     IDirectInputDevice2WImpl_GetForceFeedbackState,
965     IDirectInputDevice2WImpl_SendForceFeedbackCommand,
966     IDirectInputDevice2WImpl_EnumCreatedEffectObjects,
967     IDirectInputDevice2WImpl_Escape,
968     JoystickWGenericImpl_Poll,
969     IDirectInputDevice2WImpl_SendDeviceData,
970     IDirectInputDevice7WImpl_EnumEffectsInFile,
971     IDirectInputDevice7WImpl_WriteEffectToFile,
972     IDirectInputDevice8WImpl_BuildActionMap,
973     IDirectInputDevice8WImpl_SetActionMap,
974     IDirectInputDevice8WImpl_GetImageInfo
975 };
976
977 #else /* HAVE_IOHIDMANAGERCREATE */
978
979 const struct dinput_device joystick_osx_device = {
980   "Wine OS X joystick driver",
981   NULL,
982   NULL,
983   NULL
984 };
985
986 #endif /* HAVE_IOHIDMANAGERCREATE */