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