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