ntdll: Add ARM64 cpu info.
[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 #ifdef HAVE_IOHIDMANAGERCREATE
83
84 WINE_DEFAULT_DEBUG_CHANNEL(dinput);
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 controller */
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         int inst_id;
520         CFIndex idx, cnt = CFArrayGetCount( gElementCFArrayRef );
521
522         for ( idx = 0; idx < cnt; idx++ )
523         {
524             IOHIDValueRef valueRef;
525             int val, oldVal, newVal;
526             IOHIDElementRef tIOHIDElementRef = ( IOHIDElementRef ) CFArrayGetValueAtIndex( gElementCFArrayRef, idx );
527             int eleType = IOHIDElementGetType( tIOHIDElementRef );
528
529             switch(eleType)
530             {
531                 case kIOHIDElementTypeInput_Button:
532                     if(button_idx < 128)
533                     {
534                         IOHIDDeviceGetValue(tIOHIDDeviceRef, tIOHIDElementRef, &valueRef);
535                         val = IOHIDValueGetIntegerValue(valueRef);
536                         newVal = val ? 0x80 : 0x0;
537                         oldVal = device->generic.js.rgbButtons[button_idx];
538                         device->generic.js.rgbButtons[button_idx] = newVal;
539                         if (oldVal != newVal)
540                         {
541                             inst_id = DIDFT_MAKEINSTANCE(button_idx) | DIDFT_PSHBUTTON;
542                             queue_event(iface,inst_id,newVal,GetCurrentTime(),device->generic.base.dinput->evsequence++);
543                         }
544                         button_idx ++;
545                     }
546                     break;
547                 case kIOHIDElementTypeInput_Misc:
548                 {
549                     uint32_t usage = IOHIDElementGetUsage( tIOHIDElementRef );
550                     switch(usage)
551                     {
552                         case kHIDUsage_GD_Hatswitch:
553                         {
554                             IOHIDDeviceGetValue(tIOHIDDeviceRef, tIOHIDElementRef, &valueRef);
555                             val = IOHIDValueGetIntegerValue(valueRef);
556                             oldVal = device->generic.js.rgdwPOV[pov_idx];
557                             if (val >= 8)
558                                 newVal = -1;
559                             else
560                                 newVal = val * 4500;
561                             device->generic.js.rgdwPOV[pov_idx] = newVal;
562                             if (oldVal != newVal)
563                             {
564                                 inst_id = DIDFT_MAKEINSTANCE(pov_idx) | DIDFT_POV;
565                                 queue_event(iface,inst_id,newVal,GetCurrentTime(),device->generic.base.dinput->evsequence++);
566                             }
567                             pov_idx ++;
568                             break;
569                         }
570                         case kHIDUsage_GD_X:
571                         case kHIDUsage_GD_Y:
572                         case kHIDUsage_GD_Z:
573                         case kHIDUsage_GD_Rx:
574                         case kHIDUsage_GD_Ry:
575                         case kHIDUsage_GD_Rz:
576                         case kHIDUsage_GD_Slider:
577                         {
578                             int wine_obj = -1;
579
580                             IOHIDDeviceGetValue(tIOHIDDeviceRef, tIOHIDElementRef, &valueRef);
581                             val = IOHIDValueGetIntegerValue(valueRef);
582                             newVal = joystick_map_axis(&device->generic.props[idx], val);
583                             switch (usage)
584                             {
585                             case kHIDUsage_GD_X:
586                                 wine_obj = 0;
587                                 oldVal = device->generic.js.lX;
588                                 device->generic.js.lX = newVal;
589                                 break;
590                             case kHIDUsage_GD_Y:
591                                 wine_obj = 1;
592                                 oldVal = device->generic.js.lY;
593                                 device->generic.js.lY = newVal;
594                                 break;
595                             case kHIDUsage_GD_Z:
596                                 wine_obj = 2;
597                                 oldVal = device->generic.js.lZ;
598                                 device->generic.js.lZ = newVal;
599                                 break;
600                             case kHIDUsage_GD_Rx:
601                                 wine_obj = 3;
602                                 oldVal = device->generic.js.lRx;
603                                 device->generic.js.lRx = newVal;
604                                 break;
605                             case kHIDUsage_GD_Ry:
606                                 wine_obj = 4;
607                                 oldVal = device->generic.js.lRy;
608                                 device->generic.js.lRy = newVal;
609                                 break;
610                             case kHIDUsage_GD_Rz:
611                                 wine_obj = 5;
612                                 oldVal = device->generic.js.lRz;
613                                 device->generic.js.lRz = newVal;
614                                 break;
615                             case kHIDUsage_GD_Slider:
616                                 wine_obj = 6 + slider_idx;
617                                 oldVal = device->generic.js.rglSlider[slider_idx];
618                                 device->generic.js.rglSlider[slider_idx] = newVal;
619                                 slider_idx ++;
620                                 break;
621                             }
622                             if ((wine_obj != -1) &&
623                                  (oldVal != newVal))
624                             {
625                                 inst_id = DIDFT_MAKEINSTANCE(wine_obj) | DIDFT_ABSAXIS;
626                                 queue_event(iface,inst_id,newVal,GetCurrentTime(),device->generic.base.dinput->evsequence++);
627                             }
628
629                             break;
630                         }
631                         default:
632                             FIXME("unhandled usage %i\n",usage);
633                     }
634                     break;
635                 }
636                 default:
637                     FIXME("Unhandled type %i\n",eleType);
638             }
639         }
640     }
641 }
642
643 static INT find_joystick_devices(void)
644 {
645     static INT joystick_devices_count = -1;
646
647     if (joystick_devices_count != -1) return joystick_devices_count;
648
649     joystick_devices_count = find_osx_devices();
650
651     return  joystick_devices_count;
652 }
653
654 static BOOL joydev_enum_deviceA(DWORD dwDevType, DWORD dwFlags, LPDIDEVICEINSTANCEA lpddi, DWORD version, int id)
655 {
656     if (id >= find_joystick_devices()) return FALSE;
657
658     if (dwFlags & DIEDFL_FORCEFEEDBACK) {
659         WARN("force feedback not supported\n");
660         return FALSE;
661     }
662
663     if ((dwDevType == 0) ||
664     ((dwDevType == DIDEVTYPE_JOYSTICK) && (version > 0x0300 && version < 0x0800)) ||
665     (((dwDevType == DI8DEVCLASS_GAMECTRL) || (dwDevType == DI8DEVTYPE_JOYSTICK)) && (version >= 0x0800)))
666     {
667         /* Return joystick */
668         lpddi->guidInstance = DInput_Wine_OsX_Joystick_GUID;
669         lpddi->guidInstance.Data3 = id;
670         lpddi->guidProduct = DInput_Wine_OsX_Joystick_GUID;
671         /* we only support traditional joysticks for now */
672         if (version >= 0x0800)
673             lpddi->dwDevType = DI8DEVTYPE_JOYSTICK | (DI8DEVTYPEJOYSTICK_STANDARD << 8);
674         else
675             lpddi->dwDevType = DIDEVTYPE_JOYSTICK | (DIDEVTYPEJOYSTICK_TRADITIONAL << 8);
676         sprintf(lpddi->tszInstanceName, "Joystick %d", id);
677
678         /* get the device name */
679         get_osx_device_name(id, lpddi->tszProductName, MAX_PATH);
680
681         lpddi->guidFFDriver = GUID_NULL;
682         return TRUE;
683     }
684
685     return FALSE;
686 }
687
688 static BOOL joydev_enum_deviceW(DWORD dwDevType, DWORD dwFlags, LPDIDEVICEINSTANCEW lpddi, DWORD version, int id)
689 {
690     char name[MAX_PATH];
691     char friendly[32];
692
693     if (id >= find_joystick_devices()) return FALSE;
694
695     if (dwFlags & DIEDFL_FORCEFEEDBACK) {
696         WARN("force feedback not supported\n");
697         return FALSE;
698     }
699
700     if ((dwDevType == 0) ||
701     ((dwDevType == DIDEVTYPE_JOYSTICK) && (version > 0x0300 && version < 0x0800)) ||
702     (((dwDevType == DI8DEVCLASS_GAMECTRL) || (dwDevType == DI8DEVTYPE_JOYSTICK)) && (version >= 0x0800))) {
703         /* Return joystick */
704         lpddi->guidInstance = DInput_Wine_OsX_Joystick_GUID;
705         lpddi->guidInstance.Data3 = id;
706         lpddi->guidProduct = DInput_Wine_OsX_Joystick_GUID;
707         /* we only support traditional joysticks for now */
708         if (version >= 0x0800)
709             lpddi->dwDevType = DI8DEVTYPE_JOYSTICK | (DI8DEVTYPEJOYSTICK_STANDARD << 8);
710         else
711             lpddi->dwDevType = DIDEVTYPE_JOYSTICK | (DIDEVTYPEJOYSTICK_TRADITIONAL << 8);
712         sprintf(friendly, "Joystick %d", id);
713         MultiByteToWideChar(CP_ACP, 0, friendly, -1, lpddi->tszInstanceName, MAX_PATH);
714         /* get the device name */
715         get_osx_device_name(id, name, MAX_PATH);
716
717         MultiByteToWideChar(CP_ACP, 0, name, -1, lpddi->tszProductName, MAX_PATH);
718         lpddi->guidFFDriver = GUID_NULL;
719         return TRUE;
720     }
721
722     return FALSE;
723 }
724
725 static HRESULT alloc_device(REFGUID rguid, IDirectInputImpl *dinput,
726                             JoystickImpl **pdev, unsigned short index)
727 {
728     DWORD i;
729     JoystickImpl* newDevice;
730     char name[MAX_PATH];
731     HRESULT hr;
732     LPDIDATAFORMAT df = NULL;
733     int idx = 0;
734     int axis_map[8]; /* max axes */
735     int slider_count = 0;
736
737     TRACE("%s %p %p %hu\n", debugstr_guid(rguid), dinput, pdev, index);
738
739     newDevice = HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(JoystickImpl));
740     if (newDevice == 0) {
741         WARN("out of memory\n");
742         *pdev = 0;
743         return DIERR_OUTOFMEMORY;
744     }
745
746     newDevice->id = index;
747
748     newDevice->generic.guidInstance = DInput_Wine_OsX_Joystick_GUID;
749     newDevice->generic.guidInstance.Data3 = index;
750     newDevice->generic.guidProduct = DInput_Wine_OsX_Joystick_GUID;
751     newDevice->generic.joy_polldev = poll_osx_device_state;
752
753     /* get the device name */
754     get_osx_device_name(index, name, MAX_PATH);
755     TRACE("Name %s\n",name);
756
757     /* copy the device name */
758     newDevice->generic.name = HeapAlloc(GetProcessHeap(),0,strlen(name) + 1);
759     strcpy(newDevice->generic.name, name);
760
761     memset(axis_map, 0, sizeof(axis_map));
762     get_osx_device_elements(newDevice, axis_map);
763
764     TRACE("%i axes %i buttons %i povs\n",newDevice->generic.devcaps.dwAxes,newDevice->generic.devcaps.dwButtons,newDevice->generic.devcaps.dwPOVs);
765
766     if (newDevice->generic.devcaps.dwButtons > 128)
767     {
768         WARN("Can't support %d buttons. Clamping down to 128\n", newDevice->generic.devcaps.dwButtons);
769         newDevice->generic.devcaps.dwButtons = 128;
770     }
771
772     newDevice->generic.base.IDirectInputDevice8A_iface.lpVtbl = &JoystickAvt;
773     newDevice->generic.base.IDirectInputDevice8W_iface.lpVtbl = &JoystickWvt;
774     newDevice->generic.base.ref = 1;
775     newDevice->generic.base.dinput = dinput;
776     newDevice->generic.base.guid = *rguid;
777     InitializeCriticalSection(&newDevice->generic.base.crit);
778     newDevice->generic.base.crit.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": JoystickImpl*->generic.base.crit");
779
780     /* Create copy of default data format */
781     if (!(df = HeapAlloc(GetProcessHeap(), 0, c_dfDIJoystick2.dwSize))) goto FAILED;
782     memcpy(df, &c_dfDIJoystick2, c_dfDIJoystick2.dwSize);
783
784     df->dwNumObjs = newDevice->generic.devcaps.dwAxes + newDevice->generic.devcaps.dwPOVs + newDevice->generic.devcaps.dwButtons;
785     if (!(df->rgodf = HeapAlloc(GetProcessHeap(), 0, df->dwNumObjs * df->dwObjSize))) goto FAILED;
786
787     for (i = 0; i < newDevice->generic.devcaps.dwAxes; i++)
788     {
789         int wine_obj = -1;
790         switch (axis_map[i])
791         {
792             case kHIDUsage_GD_X: wine_obj = 0; break;
793             case kHIDUsage_GD_Y: wine_obj = 1; break;
794             case kHIDUsage_GD_Z: wine_obj = 2; break;
795             case kHIDUsage_GD_Rx: wine_obj = 3; break;
796             case kHIDUsage_GD_Ry: wine_obj = 4; break;
797             case kHIDUsage_GD_Rz: wine_obj = 5; break;
798             case kHIDUsage_GD_Slider:
799                 wine_obj = 6 + slider_count;
800                 slider_count++;
801                 break;
802         }
803         if (wine_obj < 0 ) continue;
804
805         memcpy(&df->rgodf[idx], &c_dfDIJoystick2.rgodf[wine_obj], df->dwObjSize);
806         df->rgodf[idx++].dwType = DIDFT_MAKEINSTANCE(wine_obj) | DIDFT_ABSAXIS;
807     }
808
809     for (i = 0; i < newDevice->generic.devcaps.dwPOVs; i++)
810     {
811         memcpy(&df->rgodf[idx], &c_dfDIJoystick2.rgodf[i + 8], df->dwObjSize);
812         df->rgodf[idx++].dwType = DIDFT_MAKEINSTANCE(i) | DIDFT_POV;
813     }
814
815     for (i = 0; i < newDevice->generic.devcaps.dwButtons; i++)
816     {
817         memcpy(&df->rgodf[idx], &c_dfDIJoystick2.rgodf[i + 12], df->dwObjSize);
818         df->rgodf[idx  ].pguid = &GUID_Button;
819         df->rgodf[idx++].dwType = DIDFT_MAKEINSTANCE(i) | DIDFT_PSHBUTTON;
820     }
821     newDevice->generic.base.data_format.wine_df = df;
822
823     /* initialize default properties */
824     get_osx_device_elements_props(newDevice);
825
826     IDirectInput_AddRef(&newDevice->generic.base.dinput->IDirectInput7A_iface);
827
828     EnterCriticalSection(&dinput->crit);
829     list_add_tail(&dinput->devices_list, &newDevice->generic.base.entry);
830     LeaveCriticalSection(&dinput->crit);
831
832     newDevice->generic.devcaps.dwSize = sizeof(newDevice->generic.devcaps);
833     newDevice->generic.devcaps.dwFlags = DIDC_ATTACHED;
834     if (newDevice->generic.base.dinput->dwVersion >= 0x0800)
835         newDevice->generic.devcaps.dwDevType = DI8DEVTYPE_JOYSTICK | (DI8DEVTYPEJOYSTICK_STANDARD << 8);
836     else
837         newDevice->generic.devcaps.dwDevType = DIDEVTYPE_JOYSTICK | (DIDEVTYPEJOYSTICK_TRADITIONAL << 8);
838     newDevice->generic.devcaps.dwFFSamplePeriod = 0;
839     newDevice->generic.devcaps.dwFFMinTimeResolution = 0;
840     newDevice->generic.devcaps.dwFirmwareRevision = 0;
841     newDevice->generic.devcaps.dwHardwareRevision = 0;
842     newDevice->generic.devcaps.dwFFDriverVersion = 0;
843
844     if (TRACE_ON(dinput)) {
845         _dump_DIDATAFORMAT(newDevice->generic.base.data_format.wine_df);
846         _dump_DIDEVCAPS(&newDevice->generic.devcaps);
847     }
848
849     *pdev = newDevice;
850
851     return DI_OK;
852
853 FAILED:
854     hr = DIERR_OUTOFMEMORY;
855     if (df) HeapFree(GetProcessHeap(), 0, df->rgodf);
856     HeapFree(GetProcessHeap(), 0, df);
857     release_DataFormat(&newDevice->generic.base.data_format);
858     HeapFree(GetProcessHeap(),0,newDevice->generic.name);
859     HeapFree(GetProcessHeap(),0,newDevice);
860     *pdev = 0;
861
862     return hr;
863 }
864
865 /******************************************************************************
866   *     get_joystick_index : Get the joystick index from a given GUID
867   */
868 static unsigned short get_joystick_index(REFGUID guid)
869 {
870     GUID wine_joystick = DInput_Wine_OsX_Joystick_GUID;
871     GUID dev_guid = *guid;
872
873     wine_joystick.Data3 = 0;
874     dev_guid.Data3 = 0;
875
876     /* for the standard joystick GUID use index 0 */
877     if(IsEqualGUID(&GUID_Joystick,guid)) return 0;
878
879     /* for the wine joystick GUIDs use the index stored in Data3 */
880     if(IsEqualGUID(&wine_joystick, &dev_guid)) return guid->Data3;
881
882     return 0xffff;
883 }
884
885 static HRESULT joydev_create_device(IDirectInputImpl *dinput, REFGUID rguid, REFIID riid, LPVOID *pdev, int unicode)
886 {
887     unsigned short index;
888     int joystick_devices_count;
889
890     TRACE("%p %s %s %p %i\n", dinput, debugstr_guid(rguid), debugstr_guid(riid), pdev, unicode);
891     *pdev = NULL;
892
893     if ((joystick_devices_count = find_joystick_devices()) == 0)
894         return DIERR_DEVICENOTREG;
895
896     if ((index = get_joystick_index(rguid)) < 0xffff &&
897         joystick_devices_count && index < joystick_devices_count)
898     {
899         JoystickImpl *This;
900         HRESULT hr;
901
902         if (riid == NULL)
903             ;/* nothing */
904         else if (IsEqualGUID(&IID_IDirectInputDeviceA,  riid) ||
905                  IsEqualGUID(&IID_IDirectInputDevice2A, riid) ||
906                  IsEqualGUID(&IID_IDirectInputDevice7A, riid) ||
907                  IsEqualGUID(&IID_IDirectInputDevice8A, riid))
908         {
909             unicode = 0;
910         }
911         else if (IsEqualGUID(&IID_IDirectInputDeviceW,  riid) ||
912                  IsEqualGUID(&IID_IDirectInputDevice2W, riid) ||
913                  IsEqualGUID(&IID_IDirectInputDevice7W, riid) ||
914                  IsEqualGUID(&IID_IDirectInputDevice8W, riid))
915         {
916             unicode = 1;
917         }
918         else
919         {
920             WARN("no interface\n");
921             return DIERR_NOINTERFACE;
922         }
923
924         hr = alloc_device(rguid, dinput, &This, index);
925         if (!This) return hr;
926
927         if (unicode)
928             *pdev = &This->generic.base.IDirectInputDevice8W_iface;
929         else
930             *pdev = &This->generic.base.IDirectInputDevice8A_iface;
931         return hr;
932     }
933
934     return DIERR_DEVICENOTREG;
935 }
936
937 const struct dinput_device joystick_osx_device = {
938   "Wine OS X joystick driver",
939   joydev_enum_deviceA,
940   joydev_enum_deviceW,
941   joydev_create_device
942 };
943
944 static const IDirectInputDevice8AVtbl JoystickAvt =
945 {
946     IDirectInputDevice2AImpl_QueryInterface,
947     IDirectInputDevice2AImpl_AddRef,
948     IDirectInputDevice2AImpl_Release,
949     JoystickAGenericImpl_GetCapabilities,
950     IDirectInputDevice2AImpl_EnumObjects,
951     JoystickAGenericImpl_GetProperty,
952     JoystickAGenericImpl_SetProperty,
953     IDirectInputDevice2AImpl_Acquire,
954     IDirectInputDevice2AImpl_Unacquire,
955     JoystickAGenericImpl_GetDeviceState,
956     IDirectInputDevice2AImpl_GetDeviceData,
957     IDirectInputDevice2AImpl_SetDataFormat,
958     IDirectInputDevice2AImpl_SetEventNotification,
959     IDirectInputDevice2AImpl_SetCooperativeLevel,
960     JoystickAGenericImpl_GetObjectInfo,
961     JoystickAGenericImpl_GetDeviceInfo,
962     IDirectInputDevice2AImpl_RunControlPanel,
963     IDirectInputDevice2AImpl_Initialize,
964     IDirectInputDevice2AImpl_CreateEffect,
965     IDirectInputDevice2AImpl_EnumEffects,
966     IDirectInputDevice2AImpl_GetEffectInfo,
967     IDirectInputDevice2AImpl_GetForceFeedbackState,
968     IDirectInputDevice2AImpl_SendForceFeedbackCommand,
969     IDirectInputDevice2AImpl_EnumCreatedEffectObjects,
970     IDirectInputDevice2AImpl_Escape,
971     JoystickAGenericImpl_Poll,
972     IDirectInputDevice2AImpl_SendDeviceData,
973     IDirectInputDevice7AImpl_EnumEffectsInFile,
974     IDirectInputDevice7AImpl_WriteEffectToFile,
975     JoystickAGenericImpl_BuildActionMap,
976     JoystickAGenericImpl_SetActionMap,
977     IDirectInputDevice8AImpl_GetImageInfo
978 };
979
980 static const IDirectInputDevice8WVtbl JoystickWvt =
981 {
982     IDirectInputDevice2WImpl_QueryInterface,
983     IDirectInputDevice2WImpl_AddRef,
984     IDirectInputDevice2WImpl_Release,
985     JoystickWGenericImpl_GetCapabilities,
986     IDirectInputDevice2WImpl_EnumObjects,
987     JoystickWGenericImpl_GetProperty,
988     JoystickWGenericImpl_SetProperty,
989     IDirectInputDevice2WImpl_Acquire,
990     IDirectInputDevice2WImpl_Unacquire,
991     JoystickWGenericImpl_GetDeviceState,
992     IDirectInputDevice2WImpl_GetDeviceData,
993     IDirectInputDevice2WImpl_SetDataFormat,
994     IDirectInputDevice2WImpl_SetEventNotification,
995     IDirectInputDevice2WImpl_SetCooperativeLevel,
996     JoystickWGenericImpl_GetObjectInfo,
997     JoystickWGenericImpl_GetDeviceInfo,
998     IDirectInputDevice2WImpl_RunControlPanel,
999     IDirectInputDevice2WImpl_Initialize,
1000     IDirectInputDevice2WImpl_CreateEffect,
1001     IDirectInputDevice2WImpl_EnumEffects,
1002     IDirectInputDevice2WImpl_GetEffectInfo,
1003     IDirectInputDevice2WImpl_GetForceFeedbackState,
1004     IDirectInputDevice2WImpl_SendForceFeedbackCommand,
1005     IDirectInputDevice2WImpl_EnumCreatedEffectObjects,
1006     IDirectInputDevice2WImpl_Escape,
1007     JoystickWGenericImpl_Poll,
1008     IDirectInputDevice2WImpl_SendDeviceData,
1009     IDirectInputDevice7WImpl_EnumEffectsInFile,
1010     IDirectInputDevice7WImpl_WriteEffectToFile,
1011     JoystickWGenericImpl_BuildActionMap,
1012     JoystickWGenericImpl_SetActionMap,
1013     IDirectInputDevice8WImpl_GetImageInfo
1014 };
1015
1016 #else /* HAVE_IOHIDMANAGERCREATE */
1017
1018 const struct dinput_device joystick_osx_device = {
1019   "Wine OS X joystick driver",
1020   NULL,
1021   NULL,
1022   NULL
1023 };
1024
1025 #endif /* HAVE_IOHIDMANAGERCREATE */