cabinet: Call FCI function pointers explicitly instead of hiding them inside macros.
[wine.git] / dlls / dinput / joystick_osx.c
blob29c7856b15476110e547c7f4aabd034b0765d6e1
1 /* DirectInput Joystick device for Mac OS/X
3 * Copyright 1998 Marcus Meissner
4 * Copyright 1998,1999 Lionel Ulmer
5 * Copyright 2000-2001 TransGaming Technologies Inc.
6 * Copyright 2009 CodeWeavers, Aric Stewart
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.
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.
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
23 #include "config.h"
24 #include "wine/port.h"
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 */
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"
78 #include "dinput_private.h"
79 #include "device_private.h"
80 #include "joystick_private.h"
82 WINE_DEFAULT_DEBUG_CHANNEL(dinput);
84 #ifdef HAVE_IOHIDMANAGERCREATE
86 static IOHIDManagerRef gIOHIDManagerRef = NULL;
87 static CFArrayRef gCollections = NULL;
89 typedef struct JoystickImpl JoystickImpl;
90 static const IDirectInputDevice8AVtbl JoystickAvt;
91 static const IDirectInputDevice8WVtbl JoystickWvt;
93 struct JoystickImpl
95 struct JoystickGenericImpl generic;
97 /* osx private */
98 int id;
99 CFMutableArrayRef elementCFArrayRef;
100 ObjProps **propmap;
103 static inline JoystickImpl *impl_from_IDirectInputDevice8A(IDirectInputDevice8A *iface)
105 return CONTAINING_RECORD(CONTAINING_RECORD(CONTAINING_RECORD(iface, IDirectInputDeviceImpl, IDirectInputDevice8A_iface),
106 JoystickGenericImpl, base), JoystickImpl, generic);
108 static inline JoystickImpl *impl_from_IDirectInputDevice8W(IDirectInputDevice8W *iface)
110 return CONTAINING_RECORD(CONTAINING_RECORD(CONTAINING_RECORD(iface, IDirectInputDeviceImpl, IDirectInputDevice8W_iface),
111 JoystickGenericImpl, base), JoystickImpl, generic);
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}
118 static void CFSetApplierFunctionCopyToCFArray(const void *value, void *context)
120 CFArrayAppendValue( ( CFMutableArrayRef ) context, value );
123 static CFMutableDictionaryRef creates_osx_device_match(int usage)
125 CFMutableDictionaryRef result;
127 result = CFDictionaryCreateMutable( kCFAllocatorDefault, 0,
128 &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks );
130 if ( result )
132 int number = kHIDPage_GenericDesktop;
133 CFNumberRef pageCFNumberRef = CFNumberCreate( kCFAllocatorDefault,
134 kCFNumberIntType, &number);
136 if ( pageCFNumberRef )
138 CFNumberRef usageCFNumberRef;
140 CFDictionarySetValue( result, CFSTR( kIOHIDDeviceUsagePageKey ),
141 pageCFNumberRef );
142 CFRelease( pageCFNumberRef );
144 usageCFNumberRef = CFNumberCreate( kCFAllocatorDefault,
145 kCFNumberIntType, &usage);
146 if ( usageCFNumberRef )
148 CFDictionarySetValue( result, CFSTR( kIOHIDDeviceUsageKey ),
149 usageCFNumberRef );
150 CFRelease( usageCFNumberRef );
152 else
154 ERR("CFNumberCreate() failed.\n");
155 return NULL;
158 else
160 ERR("CFNumberCreate failed.\n");
161 return NULL;
164 else
166 ERR("CFDictionaryCreateMutable failed.\n");
167 return NULL;
170 return result;
173 static CFIndex find_top_level(IOHIDDeviceRef tIOHIDDeviceRef, CFArrayRef topLevels)
175 CFArrayRef gElementCFArrayRef;
176 CFIndex numTops = 0;
178 if (!tIOHIDDeviceRef)
179 return 0;
181 gElementCFArrayRef = IOHIDDeviceCopyMatchingElements(tIOHIDDeviceRef, NULL, 0);
183 if (gElementCFArrayRef)
185 CFIndex idx, cnt = CFArrayGetCount(gElementCFArrayRef);
186 for (idx=0; idx<cnt; idx++)
188 IOHIDElementRef tIOHIDElementRef = (IOHIDElementRef)CFArrayGetValueAtIndex(gElementCFArrayRef, idx);
189 int eleType = IOHIDElementGetType(tIOHIDElementRef);
191 /* Check for top-level gaming device collections */
192 if (eleType == kIOHIDElementTypeCollection && IOHIDElementGetParent(tIOHIDElementRef) == 0)
194 int tUsagePage = IOHIDElementGetUsagePage(tIOHIDElementRef);
195 int tUsage = IOHIDElementGetUsage(tIOHIDElementRef);
197 if (tUsagePage == kHIDPage_GenericDesktop &&
198 (tUsage == kHIDUsage_GD_Joystick || tUsage == kHIDUsage_GD_GamePad))
200 CFArrayAppendValue((CFMutableArrayRef)topLevels, tIOHIDElementRef);
201 numTops++;
206 return numTops;
209 static void get_element_children(IOHIDElementRef tElement, CFArrayRef childElements)
211 CFIndex idx, cnt;
212 CFArrayRef tElementChildrenArray = IOHIDElementGetChildren(tElement);
214 cnt = CFArrayGetCount(tElementChildrenArray);
215 if (cnt < 1)
216 return;
218 /* Either add the element to the array or grab its children */
219 for (idx=0; idx<cnt; idx++)
221 IOHIDElementRef tChildElementRef;
223 tChildElementRef = (IOHIDElementRef)CFArrayGetValueAtIndex(tElementChildrenArray, idx);
224 if (IOHIDElementGetType(tChildElementRef) == kIOHIDElementTypeCollection)
225 get_element_children(tChildElementRef, childElements);
226 else
227 CFArrayAppendValue((CFMutableArrayRef)childElements, tChildElementRef);
231 static int find_osx_devices(void)
233 IOReturn tIOReturn;
234 CFMutableDictionaryRef result;
235 CFSetRef devset;
236 CFArrayRef matching;
238 gIOHIDManagerRef = IOHIDManagerCreate( kCFAllocatorDefault, 0L );
239 tIOReturn = IOHIDManagerOpen( gIOHIDManagerRef, 0L);
240 if ( kIOReturnSuccess != tIOReturn )
242 ERR("Couldn't open IOHIDManager.\n");
243 return 0;
246 matching = CFArrayCreateMutable( kCFAllocatorDefault, 0,
247 &kCFTypeArrayCallBacks );
249 /* build matching dictionary */
250 result = creates_osx_device_match(kHIDUsage_GD_Joystick);
251 if (!result)
253 CFRelease(matching);
254 return 0;
256 CFArrayAppendValue( ( CFMutableArrayRef )matching, result );
257 result = creates_osx_device_match(kHIDUsage_GD_GamePad);
258 if (!result)
260 CFRelease(matching);
261 return 0;
263 CFArrayAppendValue( ( CFMutableArrayRef )matching, result );
265 IOHIDManagerSetDeviceMatchingMultiple( gIOHIDManagerRef, matching);
266 devset = IOHIDManagerCopyDevices( gIOHIDManagerRef );
267 if (devset)
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);
275 gCollections = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
276 if (!gCollections)
277 return 0;
279 countCollections = 0;
280 for (idx = 0; idx < countDevices; idx++)
282 CFIndex tTop;
283 IOHIDDeviceRef tDevice;
285 tDevice = (IOHIDDeviceRef) CFArrayGetValueAtIndex(gDevices, idx);
286 tTop = find_top_level(tDevice, gCollections);
287 countCollections += tTop;
290 CFRelease(gDevices);
292 TRACE("found %i device(s), %i collection(s)\n",(int)countDevices,(int)countCollections);
293 return (int)countCollections;
295 return 0;
298 static int get_osx_device_name(int id, char *name, int length)
300 CFStringRef str;
301 IOHIDElementRef tIOHIDElementRef;
302 IOHIDDeviceRef tIOHIDDeviceRef;
304 if (!gCollections)
305 return 0;
307 tIOHIDElementRef = (IOHIDElementRef)CFArrayGetValueAtIndex(gCollections, id);
309 if (!tIOHIDElementRef)
311 ERR("Invalid Element requested %i\n",id);
312 return 0;
315 tIOHIDDeviceRef = IOHIDElementGetDevice(tIOHIDElementRef);
317 if (name)
318 name[0] = 0;
320 if (!tIOHIDDeviceRef)
322 ERR("Invalid Device requested %i\n",id);
323 return 0;
326 str = IOHIDDeviceGetProperty(tIOHIDDeviceRef, CFSTR( kIOHIDProductKey ));
327 if (str)
329 CFIndex len = CFStringGetLength(str);
330 if (length >= len)
332 CFStringGetCString(str,name,length,kCFStringEncodingASCII);
333 return len;
335 else
336 return (len+1);
338 return 0;
341 static void insert_sort_button(int header, IOHIDElementRef tIOHIDElementRef,
342 CFMutableArrayRef elementCFArrayRef, int index,
343 int target)
345 IOHIDElementRef targetElement;
346 int usage;
348 CFArraySetValueAtIndex(elementCFArrayRef, header+index, NULL);
349 targetElement = ( IOHIDElementRef ) CFArrayGetValueAtIndex( elementCFArrayRef, header+target);
350 if (targetElement == NULL)
352 CFArraySetValueAtIndex(elementCFArrayRef, header+target,tIOHIDElementRef);
353 return;
355 usage = IOHIDElementGetUsage( targetElement );
356 usage --; /* usage 1 based index */
358 insert_sort_button(header, targetElement, elementCFArrayRef, target, usage);
359 CFArraySetValueAtIndex(elementCFArrayRef, header+target,tIOHIDElementRef);
362 static void get_osx_device_elements(JoystickImpl *device, int axis_map[8])
364 IOHIDElementRef tIOHIDElementRef;
365 CFArrayRef gElementCFArrayRef;
366 DWORD axes = 0;
367 DWORD sliders = 0;
368 DWORD buttons = 0;
369 DWORD povs = 0;
371 device->elementCFArrayRef = NULL;
373 if (!gCollections)
374 return;
376 tIOHIDElementRef = (IOHIDElementRef)CFArrayGetValueAtIndex(gCollections, device->id);
378 if (!tIOHIDElementRef)
379 return;
381 gElementCFArrayRef = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
382 get_element_children(tIOHIDElementRef, gElementCFArrayRef);
384 if (gElementCFArrayRef)
386 CFIndex idx, cnt = CFArrayGetCount( gElementCFArrayRef );
387 /* build our element array in the order that dinput expects */
388 device->elementCFArrayRef = CFArrayCreateMutable(NULL,0,NULL);
390 for ( idx = 0; idx < cnt; idx++ )
392 IOHIDElementRef tIOHIDElementRef = ( IOHIDElementRef ) CFArrayGetValueAtIndex( gElementCFArrayRef, idx );
393 int eleType = IOHIDElementGetType( tIOHIDElementRef );
394 switch(eleType)
396 case kIOHIDElementTypeInput_Button:
398 int usagePage = IOHIDElementGetUsagePage( tIOHIDElementRef );
399 if (usagePage != kHIDPage_Button)
401 /* avoid strange elements found on the 360 controler */
402 continue;
405 if (buttons < 128)
407 CFArrayInsertValueAtIndex(device->elementCFArrayRef, (axes+povs+buttons), tIOHIDElementRef);
408 buttons++;
410 break;
412 case kIOHIDElementTypeInput_Axis:
414 CFArrayInsertValueAtIndex(device->elementCFArrayRef, axes, tIOHIDElementRef);
415 axes++;
416 break;
418 case kIOHIDElementTypeInput_Misc:
420 uint32_t usage = IOHIDElementGetUsage( tIOHIDElementRef );
421 switch(usage)
423 case kHIDUsage_GD_Hatswitch:
425 CFArrayInsertValueAtIndex(device->elementCFArrayRef, (axes+povs), tIOHIDElementRef);
426 povs++;
427 break;
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:
441 CFArrayInsertValueAtIndex(device->elementCFArrayRef, axes, tIOHIDElementRef);
442 axis_map[axes]=usage;
443 axes++;
444 break;
446 default:
447 FIXME("Unhandled usage %i\n",usage);
449 break;
451 default:
452 FIXME("Unhandled type %i\n",eleType);
457 device->generic.devcaps.dwAxes = axes;
458 device->generic.devcaps.dwButtons = buttons;
459 device->generic.devcaps.dwPOVs = povs;
461 /* Sort buttons into correct order */
462 for (buttons = 0; buttons < device->generic.devcaps.dwButtons; buttons++)
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;
470 insert_sort_button(axes+povs, tIOHIDElementRef, device->elementCFArrayRef,buttons,usage);
474 static void get_osx_device_elements_props(JoystickImpl *device)
476 CFArrayRef gElementCFArrayRef = device->elementCFArrayRef;
478 if (gElementCFArrayRef)
480 CFIndex idx, cnt = CFArrayGetCount( gElementCFArrayRef );
482 for ( idx = 0; idx < cnt; idx++ )
484 IOHIDElementRef tIOHIDElementRef = ( IOHIDElementRef ) CFArrayGetValueAtIndex( gElementCFArrayRef, idx );
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;
496 static void poll_osx_device_state(LPDIRECTINPUTDEVICE8A iface)
498 JoystickImpl *device = impl_from_IDirectInputDevice8A(iface);
499 IOHIDElementRef tIOHIDTopElementRef;
500 IOHIDDeviceRef tIOHIDDeviceRef;
501 CFArrayRef gElementCFArrayRef = device->elementCFArrayRef;
503 TRACE("polling device %i\n",device->id);
505 if (!gCollections)
506 return;
508 tIOHIDTopElementRef = (IOHIDElementRef) CFArrayGetValueAtIndex(gCollections, device->id);
509 tIOHIDDeviceRef = IOHIDElementGetDevice(tIOHIDTopElementRef);
511 if (!tIOHIDDeviceRef)
512 return;
514 if (gElementCFArrayRef)
516 int button_idx = 0;
517 int pov_idx = 0;
518 int slider_idx = 0;
519 CFIndex idx, cnt = CFArrayGetCount( gElementCFArrayRef );
521 for ( idx = 0; idx < cnt; idx++ )
523 IOHIDValueRef valueRef;
524 int val;
525 IOHIDElementRef tIOHIDElementRef = ( IOHIDElementRef ) CFArrayGetValueAtIndex( gElementCFArrayRef, idx );
526 int eleType = IOHIDElementGetType( tIOHIDElementRef );
528 switch(eleType)
530 case kIOHIDElementTypeInput_Button:
531 if(button_idx < 128)
533 IOHIDDeviceGetValue(tIOHIDDeviceRef, tIOHIDElementRef, &valueRef);
534 val = IOHIDValueGetIntegerValue(valueRef);
535 device->generic.js.rgbButtons[button_idx] = val ? 0x80 : 0x00;
536 button_idx ++;
538 break;
539 case kIOHIDElementTypeInput_Misc:
541 uint32_t usage = IOHIDElementGetUsage( tIOHIDElementRef );
542 switch(usage)
544 case kHIDUsage_GD_Hatswitch:
546 IOHIDDeviceGetValue(tIOHIDDeviceRef, tIOHIDElementRef, &valueRef);
547 val = IOHIDValueGetIntegerValue(valueRef);
548 if (val >= 8)
549 device->generic.js.rgdwPOV[pov_idx] = -1;
550 else
551 device->generic.js.rgdwPOV[pov_idx] = val * 4500;
552 pov_idx ++;
553 break;
555 case kHIDUsage_GD_X:
556 case kHIDUsage_GD_Y:
557 case kHIDUsage_GD_Z:
558 case kHIDUsage_GD_Rx:
559 case kHIDUsage_GD_Ry:
560 case kHIDUsage_GD_Rz:
561 case kHIDUsage_GD_Slider:
563 IOHIDDeviceGetValue(tIOHIDDeviceRef, tIOHIDElementRef, &valueRef);
564 val = IOHIDValueGetIntegerValue(valueRef);
565 switch (usage)
567 case kHIDUsage_GD_X:
568 device->generic.js.lX = joystick_map_axis(&device->generic.props[idx], val);
569 break;
570 case kHIDUsage_GD_Y:
571 device->generic.js.lY = joystick_map_axis(&device->generic.props[idx], val);
572 break;
573 case kHIDUsage_GD_Z:
574 device->generic.js.lZ = joystick_map_axis(&device->generic.props[idx], val);
575 break;
576 case kHIDUsage_GD_Rx:
577 device->generic.js.lRx = joystick_map_axis(&device->generic.props[idx], val);
578 break;
579 case kHIDUsage_GD_Ry:
580 device->generic.js.lRy = joystick_map_axis(&device->generic.props[idx], val);
581 break;
582 case kHIDUsage_GD_Rz:
583 device->generic.js.lRz = joystick_map_axis(&device->generic.props[idx], val);
584 break;
585 case kHIDUsage_GD_Slider:
586 device->generic.js.rglSlider[slider_idx] = joystick_map_axis(&device->generic.props[idx], val);
587 slider_idx ++;
588 break;
590 break;
592 default:
593 FIXME("unhandled usage %i\n",usage);
595 break;
597 default:
598 FIXME("Unhandled type %i\n",eleType);
604 static INT find_joystick_devices(void)
606 static INT joystick_devices_count = -1;
608 if (joystick_devices_count != -1) return joystick_devices_count;
610 joystick_devices_count = find_osx_devices();
612 return joystick_devices_count;
615 static BOOL joydev_enum_deviceA(DWORD dwDevType, DWORD dwFlags, LPDIDEVICEINSTANCEA lpddi, DWORD version, int id)
617 if (id >= find_joystick_devices()) return FALSE;
619 if (dwFlags & DIEDFL_FORCEFEEDBACK) {
620 WARN("force feedback not supported\n");
621 return FALSE;
624 if ((dwDevType == 0) ||
625 ((dwDevType == DIDEVTYPE_JOYSTICK) && (version > 0x0300 && version < 0x0800)) ||
626 (((dwDevType == DI8DEVCLASS_GAMECTRL) || (dwDevType == DI8DEVTYPE_JOYSTICK)) && (version >= 0x0800)))
628 /* Return joystick */
629 lpddi->guidInstance = DInput_Wine_OsX_Joystick_GUID;
630 lpddi->guidInstance.Data3 = id;
631 lpddi->guidProduct = DInput_Wine_OsX_Joystick_GUID;
632 /* we only support traditional joysticks for now */
633 if (version >= 0x0800)
634 lpddi->dwDevType = DI8DEVTYPE_JOYSTICK | (DI8DEVTYPEJOYSTICK_STANDARD << 8);
635 else
636 lpddi->dwDevType = DIDEVTYPE_JOYSTICK | (DIDEVTYPEJOYSTICK_TRADITIONAL << 8);
637 sprintf(lpddi->tszInstanceName, "Joystick %d", id);
639 /* get the device name */
640 get_osx_device_name(id, lpddi->tszProductName, MAX_PATH);
642 lpddi->guidFFDriver = GUID_NULL;
643 return TRUE;
646 return FALSE;
649 static BOOL joydev_enum_deviceW(DWORD dwDevType, DWORD dwFlags, LPDIDEVICEINSTANCEW lpddi, DWORD version, int id)
651 char name[MAX_PATH];
652 char friendly[32];
654 if (id >= find_joystick_devices()) return FALSE;
656 if (dwFlags & DIEDFL_FORCEFEEDBACK) {
657 WARN("force feedback not supported\n");
658 return FALSE;
661 if ((dwDevType == 0) ||
662 ((dwDevType == DIDEVTYPE_JOYSTICK) && (version > 0x0300 && version < 0x0800)) ||
663 (((dwDevType == DI8DEVCLASS_GAMECTRL) || (dwDevType == DI8DEVTYPE_JOYSTICK)) && (version >= 0x0800))) {
664 /* Return joystick */
665 lpddi->guidInstance = DInput_Wine_OsX_Joystick_GUID;
666 lpddi->guidInstance.Data3 = id;
667 lpddi->guidProduct = DInput_Wine_OsX_Joystick_GUID;
668 /* we only support traditional joysticks for now */
669 if (version >= 0x0800)
670 lpddi->dwDevType = DI8DEVTYPE_JOYSTICK | (DI8DEVTYPEJOYSTICK_STANDARD << 8);
671 else
672 lpddi->dwDevType = DIDEVTYPE_JOYSTICK | (DIDEVTYPEJOYSTICK_TRADITIONAL << 8);
673 sprintf(friendly, "Joystick %d", id);
674 MultiByteToWideChar(CP_ACP, 0, friendly, -1, lpddi->tszInstanceName, MAX_PATH);
675 /* get the device name */
676 get_osx_device_name(id, name, MAX_PATH);
678 MultiByteToWideChar(CP_ACP, 0, name, -1, lpddi->tszProductName, MAX_PATH);
679 lpddi->guidFFDriver = GUID_NULL;
680 return TRUE;
683 return FALSE;
686 static HRESULT alloc_device(REFGUID rguid, IDirectInputImpl *dinput,
687 JoystickImpl **pdev, unsigned short index)
689 DWORD i;
690 JoystickImpl* newDevice;
691 char name[MAX_PATH];
692 HRESULT hr;
693 LPDIDATAFORMAT df = NULL;
694 int idx = 0;
695 int axis_map[8]; /* max axes */
696 int slider_count = 0;
698 TRACE("%s %p %p %hu\n", debugstr_guid(rguid), dinput, pdev, index);
700 newDevice = HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(JoystickImpl));
701 if (newDevice == 0) {
702 WARN("out of memory\n");
703 *pdev = 0;
704 return DIERR_OUTOFMEMORY;
707 newDevice->id = index;
709 newDevice->generic.guidInstance = DInput_Wine_OsX_Joystick_GUID;
710 newDevice->generic.guidInstance.Data3 = index;
711 newDevice->generic.guidProduct = DInput_Wine_OsX_Joystick_GUID;
712 newDevice->generic.joy_polldev = poll_osx_device_state;
714 /* get the device name */
715 get_osx_device_name(index, name, MAX_PATH);
716 TRACE("Name %s\n",name);
718 /* copy the device name */
719 newDevice->generic.name = HeapAlloc(GetProcessHeap(),0,strlen(name) + 1);
720 strcpy(newDevice->generic.name, name);
722 memset(axis_map, 0, sizeof(axis_map));
723 get_osx_device_elements(newDevice, axis_map);
725 TRACE("%i axes %i buttons %i povs\n",newDevice->generic.devcaps.dwAxes,newDevice->generic.devcaps.dwButtons,newDevice->generic.devcaps.dwPOVs);
727 if (newDevice->generic.devcaps.dwButtons > 128)
729 WARN("Can't support %d buttons. Clamping down to 128\n", newDevice->generic.devcaps.dwButtons);
730 newDevice->generic.devcaps.dwButtons = 128;
733 newDevice->generic.base.IDirectInputDevice8A_iface.lpVtbl = &JoystickAvt;
734 newDevice->generic.base.IDirectInputDevice8W_iface.lpVtbl = &JoystickWvt;
735 newDevice->generic.base.ref = 1;
736 newDevice->generic.base.dinput = dinput;
737 newDevice->generic.base.guid = *rguid;
738 InitializeCriticalSection(&newDevice->generic.base.crit);
739 newDevice->generic.base.crit.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": JoystickImpl*->generic.base.crit");
741 /* Create copy of default data format */
742 if (!(df = HeapAlloc(GetProcessHeap(), 0, c_dfDIJoystick2.dwSize))) goto FAILED;
743 memcpy(df, &c_dfDIJoystick2, c_dfDIJoystick2.dwSize);
745 df->dwNumObjs = newDevice->generic.devcaps.dwAxes + newDevice->generic.devcaps.dwPOVs + newDevice->generic.devcaps.dwButtons;
746 if (!(df->rgodf = HeapAlloc(GetProcessHeap(), 0, df->dwNumObjs * df->dwObjSize))) goto FAILED;
748 for (i = 0; i < newDevice->generic.devcaps.dwAxes; i++)
750 int wine_obj = -1;
751 switch (axis_map[i])
753 case kHIDUsage_GD_X: wine_obj = 0; break;
754 case kHIDUsage_GD_Y: wine_obj = 1; break;
755 case kHIDUsage_GD_Z: wine_obj = 2; break;
756 case kHIDUsage_GD_Rx: wine_obj = 3; break;
757 case kHIDUsage_GD_Ry: wine_obj = 4; break;
758 case kHIDUsage_GD_Rz: wine_obj = 5; break;
759 case kHIDUsage_GD_Slider:
760 wine_obj = 6 + slider_count;
761 slider_count++;
762 break;
764 if (wine_obj < 0 ) continue;
766 memcpy(&df->rgodf[idx], &c_dfDIJoystick2.rgodf[wine_obj], df->dwObjSize);
767 df->rgodf[idx++].dwType = DIDFT_MAKEINSTANCE(wine_obj) | DIDFT_ABSAXIS;
770 for (i = 0; i < newDevice->generic.devcaps.dwPOVs; i++)
772 memcpy(&df->rgodf[idx], &c_dfDIJoystick2.rgodf[i + 8], df->dwObjSize);
773 df->rgodf[idx++].dwType = DIDFT_MAKEINSTANCE(i) | DIDFT_POV;
776 for (i = 0; i < newDevice->generic.devcaps.dwButtons; i++)
778 memcpy(&df->rgodf[idx], &c_dfDIJoystick2.rgodf[i + 12], df->dwObjSize);
779 df->rgodf[idx ].pguid = &GUID_Button;
780 df->rgodf[idx++].dwType = DIDFT_MAKEINSTANCE(i) | DIDFT_PSHBUTTON;
782 newDevice->generic.base.data_format.wine_df = df;
784 /* initialize default properties */
785 get_osx_device_elements_props(newDevice);
787 IDirectInput_AddRef(&newDevice->generic.base.dinput->IDirectInput7A_iface);
789 EnterCriticalSection(&dinput->crit);
790 list_add_tail(&dinput->devices_list, &newDevice->generic.base.entry);
791 LeaveCriticalSection(&dinput->crit);
793 newDevice->generic.devcaps.dwSize = sizeof(newDevice->generic.devcaps);
794 newDevice->generic.devcaps.dwFlags = DIDC_ATTACHED;
795 if (newDevice->generic.base.dinput->dwVersion >= 0x0800)
796 newDevice->generic.devcaps.dwDevType = DI8DEVTYPE_JOYSTICK | (DI8DEVTYPEJOYSTICK_STANDARD << 8);
797 else
798 newDevice->generic.devcaps.dwDevType = DIDEVTYPE_JOYSTICK | (DIDEVTYPEJOYSTICK_TRADITIONAL << 8);
799 newDevice->generic.devcaps.dwFFSamplePeriod = 0;
800 newDevice->generic.devcaps.dwFFMinTimeResolution = 0;
801 newDevice->generic.devcaps.dwFirmwareRevision = 0;
802 newDevice->generic.devcaps.dwHardwareRevision = 0;
803 newDevice->generic.devcaps.dwFFDriverVersion = 0;
805 if (TRACE_ON(dinput)) {
806 _dump_DIDATAFORMAT(newDevice->generic.base.data_format.wine_df);
807 _dump_DIDEVCAPS(&newDevice->generic.devcaps);
810 *pdev = newDevice;
812 return DI_OK;
814 FAILED:
815 hr = DIERR_OUTOFMEMORY;
816 if (df) HeapFree(GetProcessHeap(), 0, df->rgodf);
817 HeapFree(GetProcessHeap(), 0, df);
818 release_DataFormat(&newDevice->generic.base.data_format);
819 HeapFree(GetProcessHeap(),0,newDevice->generic.name);
820 HeapFree(GetProcessHeap(),0,newDevice);
821 *pdev = 0;
823 return hr;
826 /******************************************************************************
827 * get_joystick_index : Get the joystick index from a given GUID
829 static unsigned short get_joystick_index(REFGUID guid)
831 GUID wine_joystick = DInput_Wine_OsX_Joystick_GUID;
832 GUID dev_guid = *guid;
834 wine_joystick.Data3 = 0;
835 dev_guid.Data3 = 0;
837 /* for the standard joystick GUID use index 0 */
838 if(IsEqualGUID(&GUID_Joystick,guid)) return 0;
840 /* for the wine joystick GUIDs use the index stored in Data3 */
841 if(IsEqualGUID(&wine_joystick, &dev_guid)) return guid->Data3;
843 return 0xffff;
846 static HRESULT joydev_create_device(IDirectInputImpl *dinput, REFGUID rguid, REFIID riid, LPVOID *pdev, int unicode)
848 unsigned short index;
849 int joystick_devices_count;
851 TRACE("%p %s %s %p %i\n", dinput, debugstr_guid(rguid), debugstr_guid(riid), pdev, unicode);
852 *pdev = NULL;
854 if ((joystick_devices_count = find_joystick_devices()) == 0)
855 return DIERR_DEVICENOTREG;
857 if ((index = get_joystick_index(rguid)) < 0xffff &&
858 joystick_devices_count && index < joystick_devices_count)
860 JoystickImpl *This;
861 HRESULT hr;
863 if (riid == NULL)
864 ;/* nothing */
865 else if (IsEqualGUID(&IID_IDirectInputDeviceA, riid) ||
866 IsEqualGUID(&IID_IDirectInputDevice2A, riid) ||
867 IsEqualGUID(&IID_IDirectInputDevice7A, riid) ||
868 IsEqualGUID(&IID_IDirectInputDevice8A, riid))
870 unicode = 0;
872 else if (IsEqualGUID(&IID_IDirectInputDeviceW, riid) ||
873 IsEqualGUID(&IID_IDirectInputDevice2W, riid) ||
874 IsEqualGUID(&IID_IDirectInputDevice7W, riid) ||
875 IsEqualGUID(&IID_IDirectInputDevice8W, riid))
877 unicode = 1;
879 else
881 WARN("no interface\n");
882 return DIERR_NOINTERFACE;
885 hr = alloc_device(rguid, dinput, &This, index);
886 if (!This) return hr;
888 if (unicode)
889 *pdev = &This->generic.base.IDirectInputDevice8W_iface;
890 else
891 *pdev = &This->generic.base.IDirectInputDevice8A_iface;
892 return hr;
895 return DIERR_DEVICENOTREG;
898 const struct dinput_device joystick_osx_device = {
899 "Wine OS X joystick driver",
900 joydev_enum_deviceA,
901 joydev_enum_deviceW,
902 joydev_create_device
905 static const IDirectInputDevice8AVtbl JoystickAvt =
907 IDirectInputDevice2AImpl_QueryInterface,
908 IDirectInputDevice2AImpl_AddRef,
909 IDirectInputDevice2AImpl_Release,
910 JoystickAGenericImpl_GetCapabilities,
911 IDirectInputDevice2AImpl_EnumObjects,
912 JoystickAGenericImpl_GetProperty,
913 JoystickAGenericImpl_SetProperty,
914 IDirectInputDevice2AImpl_Acquire,
915 IDirectInputDevice2AImpl_Unacquire,
916 JoystickAGenericImpl_GetDeviceState,
917 IDirectInputDevice2AImpl_GetDeviceData,
918 IDirectInputDevice2AImpl_SetDataFormat,
919 IDirectInputDevice2AImpl_SetEventNotification,
920 IDirectInputDevice2AImpl_SetCooperativeLevel,
921 JoystickAGenericImpl_GetObjectInfo,
922 JoystickAGenericImpl_GetDeviceInfo,
923 IDirectInputDevice2AImpl_RunControlPanel,
924 IDirectInputDevice2AImpl_Initialize,
925 IDirectInputDevice2AImpl_CreateEffect,
926 IDirectInputDevice2AImpl_EnumEffects,
927 IDirectInputDevice2AImpl_GetEffectInfo,
928 IDirectInputDevice2AImpl_GetForceFeedbackState,
929 IDirectInputDevice2AImpl_SendForceFeedbackCommand,
930 IDirectInputDevice2AImpl_EnumCreatedEffectObjects,
931 IDirectInputDevice2AImpl_Escape,
932 JoystickAGenericImpl_Poll,
933 IDirectInputDevice2AImpl_SendDeviceData,
934 IDirectInputDevice7AImpl_EnumEffectsInFile,
935 IDirectInputDevice7AImpl_WriteEffectToFile,
936 IDirectInputDevice8AImpl_BuildActionMap,
937 IDirectInputDevice8AImpl_SetActionMap,
938 IDirectInputDevice8AImpl_GetImageInfo
941 static const IDirectInputDevice8WVtbl JoystickWvt =
943 IDirectInputDevice2WImpl_QueryInterface,
944 IDirectInputDevice2WImpl_AddRef,
945 IDirectInputDevice2WImpl_Release,
946 JoystickWGenericImpl_GetCapabilities,
947 IDirectInputDevice2WImpl_EnumObjects,
948 JoystickWGenericImpl_GetProperty,
949 JoystickWGenericImpl_SetProperty,
950 IDirectInputDevice2WImpl_Acquire,
951 IDirectInputDevice2WImpl_Unacquire,
952 JoystickWGenericImpl_GetDeviceState,
953 IDirectInputDevice2WImpl_GetDeviceData,
954 IDirectInputDevice2WImpl_SetDataFormat,
955 IDirectInputDevice2WImpl_SetEventNotification,
956 IDirectInputDevice2WImpl_SetCooperativeLevel,
957 JoystickWGenericImpl_GetObjectInfo,
958 JoystickWGenericImpl_GetDeviceInfo,
959 IDirectInputDevice2WImpl_RunControlPanel,
960 IDirectInputDevice2WImpl_Initialize,
961 IDirectInputDevice2WImpl_CreateEffect,
962 IDirectInputDevice2WImpl_EnumEffects,
963 IDirectInputDevice2WImpl_GetEffectInfo,
964 IDirectInputDevice2WImpl_GetForceFeedbackState,
965 IDirectInputDevice2WImpl_SendForceFeedbackCommand,
966 IDirectInputDevice2WImpl_EnumCreatedEffectObjects,
967 IDirectInputDevice2WImpl_Escape,
968 JoystickWGenericImpl_Poll,
969 IDirectInputDevice2WImpl_SendDeviceData,
970 IDirectInputDevice7WImpl_EnumEffectsInFile,
971 IDirectInputDevice7WImpl_WriteEffectToFile,
972 IDirectInputDevice8WImpl_BuildActionMap,
973 IDirectInputDevice8WImpl_SetActionMap,
974 IDirectInputDevice8WImpl_GetImageInfo
977 #else /* HAVE_IOHIDMANAGERCREATE */
979 const struct dinput_device joystick_osx_device = {
980 "Wine OS X joystick driver",
981 NULL,
982 NULL,
983 NULL
986 #endif /* HAVE_IOHIDMANAGERCREATE */