dinput: Factor out device creation interface queries.
[wine.git] / dlls / dinput / joystick_osx.c
blob38a708d4caee9e78f84c7ce9887f60d2f893e5d6
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 DWORD UInt32
28 #define LPDWORD UInt32*
29 #define LONG SInt32
30 #define LPLONG SInt32*
31 #define E_PENDING __carbon_E_PENDING
32 #define ULONG __carbon_ULONG
33 #define E_INVALIDARG __carbon_E_INVALIDARG
34 #define E_OUTOFMEMORY __carbon_E_OUTOFMEMORY
35 #define E_HANDLE __carbon_E_HANDLE
36 #define E_ACCESSDENIED __carbon_E_ACCESSDENIED
37 #define E_UNEXPECTED __carbon_E_UNEXPECTED
38 #define E_FAIL __carbon_E_FAIL
39 #define E_ABORT __carbon_E_ABORT
40 #define E_POINTER __carbon_E_POINTER
41 #define E_NOINTERFACE __carbon_E_NOINTERFACE
42 #define E_NOTIMPL __carbon_E_NOTIMPL
43 #define S_FALSE __carbon_S_FALSE
44 #define S_OK __carbon_S_OK
45 #define HRESULT_FACILITY __carbon_HRESULT_FACILITY
46 #define IS_ERROR __carbon_IS_ERROR
47 #define FAILED __carbon_FAILED
48 #define SUCCEEDED __carbon_SUCCEEDED
49 #define MAKE_HRESULT __carbon_MAKE_HRESULT
50 #define HRESULT __carbon_HRESULT
51 #define STDMETHODCALLTYPE __carbon_STDMETHODCALLTYPE
52 #include <IOKit/IOKitLib.h>
53 #include <IOKit/hid/IOHIDLib.h>
54 #include <ForceFeedback/ForceFeedback.h>
55 #undef ULONG
56 #undef E_INVALIDARG
57 #undef E_OUTOFMEMORY
58 #undef E_HANDLE
59 #undef E_ACCESSDENIED
60 #undef E_UNEXPECTED
61 #undef E_FAIL
62 #undef E_ABORT
63 #undef E_POINTER
64 #undef E_NOINTERFACE
65 #undef E_NOTIMPL
66 #undef S_FALSE
67 #undef S_OK
68 #undef HRESULT_FACILITY
69 #undef IS_ERROR
70 #undef FAILED
71 #undef SUCCEEDED
72 #undef MAKE_HRESULT
73 #undef HRESULT
74 #undef STDMETHODCALLTYPE
75 #undef DWORD
76 #undef LPDWORD
77 #undef LONG
78 #undef LPLONG
79 #undef E_PENDING
80 #endif /* HAVE_IOKIT_HID_IOHIDLIB_H */
82 #include "wine/debug.h"
83 #include "wine/unicode.h"
84 #include "windef.h"
85 #include "winbase.h"
86 #include "winerror.h"
87 #include "winreg.h"
88 #include "devguid.h"
89 #include "dinput.h"
91 #include "dinput_private.h"
92 #include "device_private.h"
93 #include "joystick_private.h"
95 #ifdef HAVE_IOHIDMANAGERCREATE
97 WINE_DEFAULT_DEBUG_CHANNEL(dinput);
99 #define MAKEUINT64(high, low) (((uint64_t)high << 32) | (uint32_t)low)
101 static CFMutableArrayRef device_main_elements = NULL;
103 typedef struct JoystickImpl JoystickImpl;
104 static const IDirectInputDevice8WVtbl JoystickWvt;
106 struct JoystickImpl
108 struct JoystickGenericImpl generic;
110 /* osx private */
111 int id;
112 CFArrayRef elements;
113 ObjProps **propmap;
114 FFDeviceObjectReference ff;
115 struct list effects;
118 static inline JoystickImpl *impl_from_IDirectInputDevice8W(IDirectInputDevice8W *iface)
120 return CONTAINING_RECORD(CONTAINING_RECORD(CONTAINING_RECORD(iface, IDirectInputDeviceImpl, IDirectInputDevice8W_iface),
121 JoystickGenericImpl, base), JoystickImpl, generic);
124 typedef struct _EffectImpl {
125 IDirectInputEffect IDirectInputEffect_iface;
126 LONG ref;
128 JoystickImpl *device;
129 FFEffectObjectReference effect;
130 GUID guid;
132 struct list entry;
133 } EffectImpl;
135 static EffectImpl *impl_from_IDirectInputEffect(IDirectInputEffect *iface)
137 return CONTAINING_RECORD(iface, EffectImpl, IDirectInputEffect_iface);
140 static const IDirectInputEffectVtbl EffectVtbl;
142 static const GUID DInput_Wine_OsX_Joystick_GUID = { /* 59CAD8F6-E617-41E2-8EB7-47B23EEEDC5A */
143 0x59CAD8F6, 0xE617, 0x41E2, {0x8E, 0xB7, 0x47, 0xB2, 0x3E, 0xEE, 0xDC, 0x5A}
146 static HRESULT osx_to_win32_hresult(HRESULT in)
148 /* OSX returns 16-bit COM runtime errors, which we should
149 * convert to win32 */
150 switch(in){
151 case 0x80000001:
152 return E_NOTIMPL;
153 case 0x80000002:
154 return E_OUTOFMEMORY;
155 case 0x80000003:
156 return E_INVALIDARG;
157 case 0x80000004:
158 return E_NOINTERFACE;
159 case 0x80000005:
160 return E_POINTER;
161 case 0x80000006:
162 return E_HANDLE;
163 case 0x80000007:
164 return E_ABORT;
165 case 0x80000008:
166 return E_FAIL;
167 case 0x80000009:
168 return E_ACCESSDENIED;
169 case 0x8000FFFF:
170 return E_UNEXPECTED;
172 return in;
175 static long get_device_property_long(IOHIDDeviceRef device, CFStringRef key)
177 CFTypeRef ref;
178 long result = 0;
180 if (device)
182 assert(IOHIDDeviceGetTypeID() == CFGetTypeID(device));
184 ref = IOHIDDeviceGetProperty(device, key);
186 if (ref && CFNumberGetTypeID() == CFGetTypeID(ref))
187 CFNumberGetValue((CFNumberRef)ref, kCFNumberLongType, &result);
190 return result;
193 static CFStringRef copy_device_name(IOHIDDeviceRef device)
195 CFStringRef name;
197 if (device)
199 CFTypeRef ref_name;
201 assert(IOHIDDeviceGetTypeID() == CFGetTypeID(device));
203 ref_name = IOHIDDeviceGetProperty(device, CFSTR(kIOHIDProductKey));
205 if (ref_name && CFStringGetTypeID() == CFGetTypeID(ref_name))
206 name = CFStringCreateCopy(kCFAllocatorDefault, ref_name);
207 else
209 long vendID, prodID;
211 vendID = get_device_property_long(device, CFSTR(kIOHIDVendorIDKey));
212 prodID = get_device_property_long(device, CFSTR(kIOHIDProductIDKey));
213 name = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("0x%04lx 0x%04lx"), vendID, prodID);
216 else
218 ERR("NULL device\n");
219 name = CFStringCreateCopy(kCFAllocatorDefault, CFSTR(""));
222 return name;
225 static long get_device_location_ID(IOHIDDeviceRef device)
227 return get_device_property_long(device, CFSTR(kIOHIDLocationIDKey));
230 static void copy_set_to_array(const void *value, void *context)
232 CFArrayAppendValue(context, value);
235 static CFComparisonResult device_name_comparator(IOHIDDeviceRef device1, IOHIDDeviceRef device2)
237 CFStringRef name1 = copy_device_name(device1), name2 = copy_device_name(device2);
238 CFComparisonResult result = CFStringCompare(name1, name2, (kCFCompareForcedOrdering | kCFCompareNumerically));
239 CFRelease(name1);
240 CFRelease(name2);
241 return result;
244 static CFComparisonResult device_location_name_comparator(const void *val1, const void *val2, void *context)
246 IOHIDDeviceRef device1 = (IOHIDDeviceRef)val1, device2 = (IOHIDDeviceRef)val2;
247 long loc1 = get_device_location_ID(device1), loc2 = get_device_location_ID(device2);
249 if (loc1 < loc2)
250 return kCFCompareLessThan;
251 else if (loc1 > loc2)
252 return kCFCompareGreaterThan;
253 /* virtual joysticks may not have a kIOHIDLocationIDKey and will default to location ID of 0, this orders virtual joysticks by their name */
254 return device_name_comparator(device1, device2);
257 static const char* debugstr_cf(CFTypeRef t)
259 CFStringRef s;
260 const char* ret;
262 if (!t) return "(null)";
264 if (CFGetTypeID(t) == CFStringGetTypeID())
265 s = t;
266 else
267 s = CFCopyDescription(t);
268 ret = CFStringGetCStringPtr(s, kCFStringEncodingUTF8);
269 if (ret) ret = debugstr_a(ret);
270 if (!ret)
272 const UniChar* u = CFStringGetCharactersPtr(s);
273 if (u)
274 ret = debugstr_wn((const WCHAR*)u, CFStringGetLength(s));
276 if (!ret)
278 UniChar buf[200];
279 int len = min(CFStringGetLength(s), ARRAY_SIZE(buf));
280 CFStringGetCharacters(s, CFRangeMake(0, len), buf);
281 ret = debugstr_wn(buf, len);
283 if (s != t) CFRelease(s);
284 return ret;
287 static const char* debugstr_device(IOHIDDeviceRef device)
289 return wine_dbg_sprintf("<IOHIDDevice %p product %s IOHIDLocationID %lu>", device,
290 debugstr_cf(IOHIDDeviceGetProperty(device, CFSTR(kIOHIDProductKey))),
291 get_device_location_ID(device));
294 static const char* debugstr_element(IOHIDElementRef element)
296 return wine_dbg_sprintf("<IOHIDElement %p type %d usage %u/%u device %p>", element,
297 IOHIDElementGetType(element), IOHIDElementGetUsagePage(element),
298 IOHIDElementGetUsage(element), IOHIDElementGetDevice(element));
301 static IOHIDDeviceRef get_device_ref(int id)
303 IOHIDElementRef device_main_element;
304 IOHIDDeviceRef hid_device;
306 TRACE("id %d\n", id);
308 if (!device_main_elements || id >= CFArrayGetCount(device_main_elements))
309 return 0;
311 device_main_element = (IOHIDElementRef)CFArrayGetValueAtIndex(device_main_elements, id);
312 if (!device_main_element)
314 ERR("Invalid Element requested %i\n",id);
315 return 0;
318 hid_device = IOHIDElementGetDevice(device_main_element);
319 if (!hid_device)
321 ERR("Invalid Device requested %i\n",id);
322 return 0;
325 TRACE("-> %s\n", debugstr_device(hid_device));
326 return hid_device;
329 static HRESULT get_ff(IOHIDDeviceRef device, FFDeviceObjectReference *ret)
331 io_service_t service;
332 CFMutableDictionaryRef matching;
333 CFTypeRef location_id;
334 HRESULT hr;
336 TRACE("device %s\n", debugstr_device(device));
338 matching = IOServiceMatching(kIOHIDDeviceKey);
339 if(!matching){
340 WARN("IOServiceMatching failed, force feedback disabled\n");
341 return DIERR_DEVICENOTREG;
344 location_id = IOHIDDeviceGetProperty(device, CFSTR(kIOHIDLocationIDKey));
345 if(!location_id){
346 CFRelease(matching);
347 WARN("IOHIDDeviceGetProperty failed, force feedback disabled\n");
348 return DIERR_DEVICENOTREG;
351 CFDictionaryAddValue(matching, CFSTR(kIOHIDLocationIDKey), location_id);
353 service = IOServiceGetMatchingService(kIOMasterPortDefault, matching);
355 if (ret)
356 hr = osx_to_win32_hresult(FFCreateDevice(service, ret));
357 else
358 hr = FFIsForceFeedback(service) == FF_OK ? S_OK : S_FALSE;
360 IOObjectRelease(service);
361 TRACE("-> hr 0x%08x *ret %p\n", hr, ret ? *ret : NULL);
362 return hr;
365 static CFMutableDictionaryRef create_osx_device_match(int usage)
367 CFMutableDictionaryRef result;
369 TRACE("usage %d\n", usage);
371 result = CFDictionaryCreateMutable( kCFAllocatorDefault, 0,
372 &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks );
374 if ( result )
376 int number = kHIDPage_GenericDesktop;
377 CFNumberRef page = CFNumberCreate( kCFAllocatorDefault,
378 kCFNumberIntType, &number);
380 if (page)
382 CFNumberRef cf_usage;
384 CFDictionarySetValue( result, CFSTR( kIOHIDDeviceUsagePageKey ), page );
385 CFRelease( page );
387 cf_usage = CFNumberCreate( kCFAllocatorDefault,
388 kCFNumberIntType, &usage);
389 if (cf_usage)
391 CFDictionarySetValue( result, CFSTR( kIOHIDDeviceUsageKey ), cf_usage );
392 CFRelease( cf_usage );
394 else
396 ERR("CFNumberCreate() failed.\n");
397 CFRelease(result);
398 return NULL;
401 else
403 ERR("CFNumberCreate failed.\n");
404 CFRelease(result);
405 return NULL;
408 else
410 ERR("CFDictionaryCreateMutable failed.\n");
411 return NULL;
414 return result;
417 static CFIndex find_top_level(IOHIDDeviceRef hid_device, CFMutableArrayRef main_elements)
419 CFArrayRef elements;
420 CFIndex total = 0;
422 TRACE("hid_device %s\n", debugstr_device(hid_device));
424 if (!hid_device)
425 return 0;
427 elements = IOHIDDeviceCopyMatchingElements(hid_device, NULL, 0);
429 if (elements)
431 CFIndex idx, cnt = CFArrayGetCount(elements);
432 for (idx=0; idx<cnt; idx++)
434 IOHIDElementRef element = (IOHIDElementRef)CFArrayGetValueAtIndex(elements, idx);
435 int type = IOHIDElementGetType(element);
437 TRACE("element %s\n", debugstr_element(element));
439 /* Check for top-level gaming device collections */
440 if (type == kIOHIDElementTypeCollection && IOHIDElementGetParent(element) == 0)
442 int usage_page = IOHIDElementGetUsagePage(element);
443 int usage = IOHIDElementGetUsage(element);
445 if (usage_page == kHIDPage_GenericDesktop &&
446 (usage == kHIDUsage_GD_Joystick || usage == kHIDUsage_GD_GamePad))
448 CFArrayAppendValue(main_elements, element);
449 total++;
453 CFRelease(elements);
456 TRACE("-> total %d\n", (int)total);
457 return total;
460 static void get_element_children(IOHIDElementRef element, CFMutableArrayRef all_children)
462 CFIndex idx, cnt;
463 CFArrayRef element_children = IOHIDElementGetChildren(element);
465 TRACE("element %s\n", debugstr_element(element));
467 cnt = CFArrayGetCount(element_children);
469 /* Either add the element to the array or grab its children */
470 for (idx=0; idx<cnt; idx++)
472 IOHIDElementRef child;
474 child = (IOHIDElementRef)CFArrayGetValueAtIndex(element_children, idx);
475 TRACE("child %s\n", debugstr_element(child));
476 if (IOHIDElementGetType(child) == kIOHIDElementTypeCollection)
477 get_element_children(child, all_children);
478 else
479 CFArrayAppendValue(all_children, child);
483 static int find_osx_devices(void)
485 IOHIDManagerRef hid_manager;
486 CFMutableDictionaryRef result;
487 CFSetRef devset;
488 CFMutableArrayRef matching;
490 TRACE("()\n");
492 hid_manager = IOHIDManagerCreate( kCFAllocatorDefault, 0L );
493 if (IOHIDManagerOpen( hid_manager, 0 ) != kIOReturnSuccess)
495 ERR("Couldn't open IOHIDManager.\n");
496 CFRelease( hid_manager );
497 return 0;
500 matching = CFArrayCreateMutable( kCFAllocatorDefault, 0,
501 &kCFTypeArrayCallBacks );
503 /* build matching dictionary */
504 result = create_osx_device_match(kHIDUsage_GD_Joystick);
505 if (!result)
507 CFRelease(matching);
508 goto fail;
510 CFArrayAppendValue( matching, result );
511 CFRelease( result );
512 result = create_osx_device_match(kHIDUsage_GD_GamePad);
513 if (!result)
515 CFRelease(matching);
516 goto fail;
518 CFArrayAppendValue( matching, result );
519 CFRelease( result );
521 IOHIDManagerSetDeviceMatchingMultiple( hid_manager, matching);
522 CFRelease( matching );
523 devset = IOHIDManagerCopyDevices( hid_manager );
524 if (devset)
526 CFIndex num_devices, num_main_elements, idx;
527 CFMutableArrayRef devices;
529 num_devices = CFSetGetCount(devset);
530 devices = CFArrayCreateMutable(kCFAllocatorDefault, num_devices, &kCFTypeArrayCallBacks);
531 CFSetApplyFunction(devset, copy_set_to_array, devices);
532 CFRelease(devset);
533 CFArraySortValues(devices, CFRangeMake(0, num_devices), device_location_name_comparator, NULL);
535 device_main_elements = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
536 if (!device_main_elements)
538 CFRelease( devices );
539 goto fail;
542 num_main_elements = 0;
543 for (idx = 0; idx < num_devices; idx++)
545 CFIndex top;
546 IOHIDDeviceRef hid_device;
548 hid_device = (IOHIDDeviceRef) CFArrayGetValueAtIndex(devices, idx);
549 TRACE("hid_device %s\n", debugstr_device(hid_device));
550 top = find_top_level(hid_device, device_main_elements);
551 num_main_elements += top;
554 CFRelease(devices);
556 TRACE("found %i device(s), %i collection(s)\n",(int)num_devices,(int)num_main_elements);
557 return (int)num_main_elements;
560 fail:
561 IOHIDManagerClose( hid_manager, 0 );
562 CFRelease( hid_manager );
563 return 0;
566 static int get_osx_device_name(int id, char *name, int length)
568 CFStringRef str;
569 IOHIDDeviceRef hid_device;
571 hid_device = get_device_ref(id);
573 TRACE("id %d hid_device %s\n", id, debugstr_device(hid_device));
575 if (name)
576 name[0] = 0;
578 if (!hid_device)
579 return 0;
581 str = IOHIDDeviceGetProperty(hid_device, CFSTR( kIOHIDProductKey ));
582 if (str)
584 CFIndex len = CFStringGetLength(str);
585 if (length >= len)
587 CFStringGetCString(str,name,length,kCFStringEncodingASCII);
588 return len;
590 else
591 return (len+1);
593 return 0;
596 static CFComparisonResult button_usage_comparator(const void *val1, const void *val2, void *context)
598 IOHIDElementRef element1 = (IOHIDElementRef)val1, element2 = (IOHIDElementRef)val2;
599 int usage1 = IOHIDElementGetUsage(element1), usage2 = IOHIDElementGetUsage(element2);
601 if (usage1 < usage2)
602 return kCFCompareLessThan;
603 if (usage1 > usage2)
604 return kCFCompareGreaterThan;
605 return kCFCompareEqualTo;
608 static void get_osx_device_elements(JoystickImpl *device, uint64_t axis_map[8])
610 IOHIDElementRef device_main_element;
611 CFMutableArrayRef elements;
612 DWORD sliders = 0;
613 BOOL use_accel_brake_for_rx_ry = TRUE;
615 TRACE("device %p device->id %d\n", device, device->id);
617 device->elements = NULL;
619 if (!device_main_elements || device->id >= CFArrayGetCount(device_main_elements))
620 return;
622 device_main_element = (IOHIDElementRef)CFArrayGetValueAtIndex(device_main_elements, device->id);
623 TRACE("device_main_element %s\n", debugstr_element(device_main_element));
624 if (!device_main_element)
625 return;
627 elements = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
628 get_element_children(device_main_element, elements);
630 if (elements)
632 CFIndex idx, cnt = CFArrayGetCount( elements );
633 CFMutableArrayRef axes = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
634 CFMutableArrayRef buttons = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
635 CFMutableArrayRef povs = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
637 /* Scan the elements to see if Rx/Ry is present, if not then Accelerator/Brake can be mapped to it.
638 * (Xbox One controller triggers use accelerator/brake)
640 for ( idx = 0; idx < cnt; idx++ )
642 IOHIDElementRef element = ( IOHIDElementRef ) CFArrayGetValueAtIndex( elements, idx );
643 IOHIDElementType type = IOHIDElementGetType( element );
644 uint32_t usage_page = IOHIDElementGetUsagePage( element );
645 uint32_t usage = IOHIDElementGetUsage( element );
647 if (type == kIOHIDElementTypeInput_Misc && usage_page == kHIDPage_GenericDesktop &&
648 (usage == kHIDUsage_GD_Rx || usage == kHIDUsage_GD_Ry))
650 use_accel_brake_for_rx_ry = FALSE;
654 for ( idx = 0; idx < cnt; idx++ )
656 IOHIDElementRef element = ( IOHIDElementRef ) CFArrayGetValueAtIndex( elements, idx );
657 int type = IOHIDElementGetType( element );
658 int usage_page = IOHIDElementGetUsagePage( element );
660 TRACE("element %s\n", debugstr_element(element));
662 if (usage_page >= kHIDPage_VendorDefinedStart)
664 /* vendor pages can repurpose type ids, resulting in incorrect case matches below (e.g. ds4 controllers) */
665 continue;
668 switch(type)
670 case kIOHIDElementTypeInput_Button:
672 TRACE("kIOHIDElementTypeInput_Button usage_page %d\n", usage_page);
673 if ((usage_page != kHIDPage_Button) && (usage_page != kHIDPage_Consumer))
675 /* avoid strange elements found on the 360 controller */
676 continue;
679 if (CFArrayGetCount(buttons) < 128)
680 CFArrayAppendValue(buttons, element);
681 break;
683 case kIOHIDElementTypeInput_Axis:
685 TRACE("kIOHIDElementTypeInput_Axis\n");
686 CFArrayAppendValue(axes, element);
687 break;
689 case kIOHIDElementTypeInput_Misc:
691 uint32_t usage = IOHIDElementGetUsage( element );
692 switch(MAKEUINT64(usage_page, usage))
694 case MAKEUINT64(kHIDPage_GenericDesktop, kHIDUsage_GD_Hatswitch):
696 TRACE("kIOHIDElementTypeInput_Misc / kHIDUsage_GD_Hatswitch\n");
697 CFArrayAppendValue(povs, element);
698 break;
700 case MAKEUINT64(kHIDPage_GenericDesktop, kHIDUsage_GD_Slider):
701 sliders ++;
702 if (sliders > 2)
703 break;
704 /* fallthrough, sliders are axis */
705 case MAKEUINT64(kHIDPage_GenericDesktop, kHIDUsage_GD_X):
706 case MAKEUINT64(kHIDPage_GenericDesktop, kHIDUsage_GD_Y):
707 case MAKEUINT64(kHIDPage_GenericDesktop, kHIDUsage_GD_Z):
708 case MAKEUINT64(kHIDPage_GenericDesktop, kHIDUsage_GD_Rx):
709 case MAKEUINT64(kHIDPage_GenericDesktop, kHIDUsage_GD_Ry):
710 case MAKEUINT64(kHIDPage_GenericDesktop, kHIDUsage_GD_Rz):
711 case MAKEUINT64(kHIDPage_Simulation, kHIDUsage_Sim_Accelerator):
712 case MAKEUINT64(kHIDPage_Simulation, kHIDUsage_Sim_Brake):
714 if (usage == kHIDUsage_Sim_Accelerator || usage == kHIDUsage_Sim_Brake)
716 if (use_accel_brake_for_rx_ry)
717 TRACE("Using Sim_Accelerator/Brake for GD_Rx/Ry\n");
718 else
719 break;
722 TRACE("kIOHIDElementTypeInput_Misc / kHIDUsage_GD_* (%d)\n", usage);
723 axis_map[CFArrayGetCount(axes)]=MAKEUINT64(usage_page, usage);
724 CFArrayAppendValue(axes, element);
725 break;
727 default:
728 FIXME("kIOHIDElementTypeInput_Misc / Unhandled usage %i/%i\n", usage_page, usage);
730 break;
732 default:
733 FIXME("Unhandled type %i\n",type);
737 /* Sort buttons into correct order */
738 CFArraySortValues(buttons, CFRangeMake(0, CFArrayGetCount(buttons)), button_usage_comparator, NULL);
740 device->generic.devcaps.dwAxes = CFArrayGetCount(axes);
741 device->generic.devcaps.dwButtons = CFArrayGetCount(buttons);
742 device->generic.devcaps.dwPOVs = CFArrayGetCount(povs);
744 TRACE("axes %u povs %u buttons %u\n", device->generic.devcaps.dwAxes, device->generic.devcaps.dwPOVs,
745 device->generic.devcaps.dwButtons);
747 /* build our element array in the order that dinput expects */
748 CFArrayAppendArray(axes, povs, CFRangeMake(0, device->generic.devcaps.dwPOVs));
749 CFArrayAppendArray(axes, buttons, CFRangeMake(0, device->generic.devcaps.dwButtons));
750 device->elements = axes;
751 axes = NULL;
753 CFRelease(povs);
754 CFRelease(buttons);
755 CFRelease(elements);
757 else
759 device->generic.devcaps.dwAxes = 0;
760 device->generic.devcaps.dwButtons = 0;
761 device->generic.devcaps.dwPOVs = 0;
765 static void get_osx_device_elements_props(JoystickImpl *device)
767 TRACE("device %p\n", device);
769 if (device->elements)
771 CFIndex idx, cnt = CFArrayGetCount( device->elements );
773 for ( idx = 0; idx < cnt; idx++ )
775 IOHIDElementRef element = ( IOHIDElementRef ) CFArrayGetValueAtIndex( device->elements, idx );
777 TRACE("element %s\n", debugstr_element(element));
779 device->generic.props[idx].lDevMin = IOHIDElementGetLogicalMin(element);
780 device->generic.props[idx].lDevMax = IOHIDElementGetLogicalMax(element);
781 device->generic.props[idx].lMin = 0;
782 device->generic.props[idx].lMax = 0xffff;
783 device->generic.props[idx].lDeadZone = 0;
784 device->generic.props[idx].lSaturation = 0;
789 static void poll_osx_device_state( IDirectInputDevice8W *iface )
791 JoystickImpl *device = impl_from_IDirectInputDevice8W( iface );
792 IOHIDElementRef device_main_element;
793 IOHIDDeviceRef hid_device;
795 TRACE("device %p device->id %i\n", device, device->id);
797 if (!device_main_elements || device->id >= CFArrayGetCount(device_main_elements))
798 return;
800 device_main_element = (IOHIDElementRef) CFArrayGetValueAtIndex(device_main_elements, device->id);
801 hid_device = IOHIDElementGetDevice(device_main_element);
802 TRACE("main element %s hid_device %s\n", debugstr_element(device_main_element), debugstr_device(hid_device));
803 if (!hid_device)
804 return;
806 if (device->elements)
808 int button_idx = 0;
809 int pov_idx = 0;
810 int slider_idx = 0;
811 int inst_id;
812 CFIndex idx, cnt = CFArrayGetCount( device->elements );
814 for ( idx = 0; idx < cnt; idx++ )
816 IOHIDValueRef valueRef;
817 int val, oldVal, newVal;
818 IOHIDElementRef element = ( IOHIDElementRef ) CFArrayGetValueAtIndex( device->elements, idx );
819 int type = IOHIDElementGetType( element );
821 TRACE("element %s\n", debugstr_element(element));
823 switch(type)
825 case kIOHIDElementTypeInput_Button:
826 TRACE("kIOHIDElementTypeInput_Button\n");
827 if(button_idx < 128)
829 valueRef = NULL;
830 if (IOHIDDeviceGetValue(hid_device, element, &valueRef) != kIOReturnSuccess)
831 return;
832 if (valueRef == NULL)
833 return;
834 val = IOHIDValueGetIntegerValue(valueRef);
835 newVal = val ? 0x80 : 0x0;
836 oldVal = device->generic.js.rgbButtons[button_idx];
837 device->generic.js.rgbButtons[button_idx] = newVal;
838 TRACE("valueRef %s val %d oldVal %d newVal %d\n", debugstr_cf(valueRef), val, oldVal, newVal);
839 if (oldVal != newVal)
841 inst_id = DIDFT_MAKEINSTANCE(button_idx) | DIDFT_PSHBUTTON;
842 queue_event(iface,inst_id,newVal,GetCurrentTime(),device->generic.base.dinput->evsequence++);
844 button_idx ++;
846 break;
847 case kIOHIDElementTypeInput_Misc:
849 uint32_t usage_page = IOHIDElementGetUsagePage( element );
850 uint32_t usage = IOHIDElementGetUsage( element );
851 switch(MAKEUINT64(usage_page, usage))
853 case MAKEUINT64(kHIDPage_GenericDesktop, kHIDUsage_GD_Hatswitch):
855 TRACE("kIOHIDElementTypeInput_Misc / kHIDUsage_GD_Hatswitch\n");
856 valueRef = NULL;
857 if (IOHIDDeviceGetValue(hid_device, element, &valueRef) != kIOReturnSuccess)
858 return;
859 if (valueRef == NULL)
860 return;
861 val = IOHIDValueGetIntegerValue(valueRef);
862 oldVal = device->generic.js.rgdwPOV[pov_idx];
863 if ((val > device->generic.props[idx].lDevMax) || (val < device->generic.props[idx].lDevMin))
864 newVal = -1;
865 else
866 newVal = (val - device->generic.props[idx].lDevMin) * 4500;
867 device->generic.js.rgdwPOV[pov_idx] = newVal;
868 TRACE("valueRef %s val %d oldVal %d newVal %d\n", debugstr_cf(valueRef), val, oldVal, newVal);
869 if (oldVal != newVal)
871 inst_id = DIDFT_MAKEINSTANCE(pov_idx) | DIDFT_POV;
872 queue_event(iface,inst_id,newVal,GetCurrentTime(),device->generic.base.dinput->evsequence++);
874 pov_idx ++;
875 break;
877 case MAKEUINT64(kHIDPage_GenericDesktop, kHIDUsage_GD_X):
878 case MAKEUINT64(kHIDPage_GenericDesktop, kHIDUsage_GD_Y):
879 case MAKEUINT64(kHIDPage_GenericDesktop, kHIDUsage_GD_Z):
880 case MAKEUINT64(kHIDPage_GenericDesktop, kHIDUsage_GD_Rx):
881 case MAKEUINT64(kHIDPage_GenericDesktop, kHIDUsage_GD_Ry):
882 case MAKEUINT64(kHIDPage_GenericDesktop, kHIDUsage_GD_Rz):
883 case MAKEUINT64(kHIDPage_GenericDesktop, kHIDUsage_GD_Slider):
884 case MAKEUINT64(kHIDPage_Simulation, kHIDUsage_Sim_Accelerator):
885 case MAKEUINT64(kHIDPage_Simulation, kHIDUsage_Sim_Brake):
887 int wine_obj = -1;
889 valueRef = NULL;
890 if (IOHIDDeviceGetValue(hid_device, element, &valueRef) != kIOReturnSuccess)
891 return;
892 if (valueRef == NULL)
893 return;
894 val = IOHIDValueGetIntegerValue(valueRef);
895 newVal = joystick_map_axis(&device->generic.props[idx], val);
896 switch (MAKEUINT64(usage_page, usage))
898 case MAKEUINT64(kHIDPage_GenericDesktop, kHIDUsage_GD_X):
899 TRACE("kIOHIDElementTypeInput_Misc / kHIDUsage_GD_X\n");
900 wine_obj = 0;
901 oldVal = device->generic.js.lX;
902 device->generic.js.lX = newVal;
903 break;
904 case MAKEUINT64(kHIDPage_GenericDesktop, kHIDUsage_GD_Y):
905 TRACE("kIOHIDElementTypeInput_Misc / kHIDUsage_GD_Y\n");
906 wine_obj = 1;
907 oldVal = device->generic.js.lY;
908 device->generic.js.lY = newVal;
909 break;
910 case MAKEUINT64(kHIDPage_GenericDesktop, kHIDUsage_GD_Z):
911 TRACE("kIOHIDElementTypeInput_Misc / kHIDUsage_GD_Z\n");
912 wine_obj = 2;
913 oldVal = device->generic.js.lZ;
914 device->generic.js.lZ = newVal;
915 break;
916 case MAKEUINT64(kHIDPage_GenericDesktop, kHIDUsage_GD_Rx):
917 case MAKEUINT64(kHIDPage_Simulation, kHIDUsage_Sim_Accelerator):
918 TRACE("kIOHIDElementTypeInput_Misc / kHIDUsage_GD_Rx\n");
919 wine_obj = 3;
920 oldVal = device->generic.js.lRx;
921 device->generic.js.lRx = newVal;
922 break;
923 case MAKEUINT64(kHIDPage_GenericDesktop, kHIDUsage_GD_Ry):
924 case MAKEUINT64(kHIDPage_Simulation, kHIDUsage_Sim_Brake):
925 TRACE("kIOHIDElementTypeInput_Misc / kHIDUsage_GD_Ry\n");
926 wine_obj = 4;
927 oldVal = device->generic.js.lRy;
928 device->generic.js.lRy = newVal;
929 break;
930 case MAKEUINT64(kHIDPage_GenericDesktop, kHIDUsage_GD_Rz):
931 TRACE("kIOHIDElementTypeInput_Misc / kHIDUsage_GD_Rz\n");
932 wine_obj = 5;
933 oldVal = device->generic.js.lRz;
934 device->generic.js.lRz = newVal;
935 break;
936 case MAKEUINT64(kHIDPage_GenericDesktop, kHIDUsage_GD_Slider):
937 TRACE("kIOHIDElementTypeInput_Misc / kHIDUsage_GD_Slider\n");
938 wine_obj = 6 + slider_idx;
939 oldVal = device->generic.js.rglSlider[slider_idx];
940 device->generic.js.rglSlider[slider_idx] = newVal;
941 slider_idx ++;
942 break;
944 TRACE("valueRef %s val %d oldVal %d newVal %d\n", debugstr_cf(valueRef), val, oldVal, newVal);
945 if ((wine_obj != -1) &&
946 (oldVal != newVal))
948 inst_id = DIDFT_MAKEINSTANCE(wine_obj) | DIDFT_ABSAXIS;
949 queue_event(iface,inst_id,newVal,GetCurrentTime(),device->generic.base.dinput->evsequence++);
952 break;
954 default:
955 FIXME("kIOHIDElementTypeInput_Misc / unhandled usage %i\n", usage);
957 break;
959 default:
960 FIXME("Unhandled type %i\n",type);
966 static INT find_joystick_devices(void)
968 static INT joystick_devices_count = -1;
970 if (joystick_devices_count != -1) return joystick_devices_count;
972 joystick_devices_count = find_osx_devices();
974 return joystick_devices_count;
977 static DWORD make_vid_pid(IOHIDDeviceRef device)
979 long vendID, prodID;
981 vendID = get_device_property_long(device, CFSTR(kIOHIDVendorIDKey));
982 prodID = get_device_property_long(device, CFSTR(kIOHIDProductIDKey));
984 return MAKELONG(vendID, prodID);
987 static HRESULT joydev_enum_deviceA(DWORD dwDevType, DWORD dwFlags, LPDIDEVICEINSTANCEA lpddi, DWORD version, int id)
989 IOHIDDeviceRef device;
990 BOOL is_joystick;
992 TRACE("dwDevType %u dwFlags 0x%08x version 0x%04x id %d\n", dwDevType, dwFlags, version, id);
994 if (id >= find_joystick_devices()) return E_FAIL;
996 device = get_device_ref(id);
998 if ((dwDevType == 0) ||
999 ((dwDevType == DIDEVTYPE_JOYSTICK) && (version >= 0x0300 && version < 0x0800)) ||
1000 (((dwDevType == DI8DEVCLASS_GAMECTRL) || (dwDevType == DI8DEVTYPE_JOYSTICK)) && (version >= 0x0800)))
1002 if (dwFlags & DIEDFL_FORCEFEEDBACK) {
1003 if(!device)
1004 return S_FALSE;
1005 if(get_ff(device, NULL) != S_OK)
1006 return S_FALSE;
1008 is_joystick = get_device_property_long(device, CFSTR(kIOHIDDeviceUsageKey)) == kHIDUsage_GD_Joystick;
1009 /* Return joystick */
1010 lpddi->guidInstance = DInput_Wine_OsX_Joystick_GUID;
1011 lpddi->guidInstance.Data3 = id;
1012 lpddi->guidProduct = DInput_PIDVID_Product_GUID;
1013 lpddi->guidProduct.Data1 = make_vid_pid(device);
1014 lpddi->dwDevType = get_device_type(version, is_joystick);
1015 lpddi->dwDevType |= DIDEVTYPE_HID;
1016 lpddi->wUsagePage = 0x01; /* Desktop */
1017 if (is_joystick)
1018 lpddi->wUsage = 0x04; /* Joystick */
1019 else
1020 lpddi->wUsage = 0x05; /* Game Pad */
1021 sprintf(lpddi->tszInstanceName, "Joystick %d", id);
1023 /* get the device name */
1024 get_osx_device_name(id, lpddi->tszProductName, MAX_PATH);
1026 lpddi->guidFFDriver = GUID_NULL;
1027 return S_OK;
1030 return S_FALSE;
1033 static HRESULT joydev_enum_deviceW(DWORD dwDevType, DWORD dwFlags, LPDIDEVICEINSTANCEW lpddi, DWORD version, int id)
1035 char name[MAX_PATH];
1036 char friendly[32];
1037 IOHIDDeviceRef device;
1038 BOOL is_joystick;
1040 TRACE("dwDevType %u dwFlags 0x%08x version 0x%04x id %d\n", dwDevType, dwFlags, version, id);
1042 if (id >= find_joystick_devices()) return E_FAIL;
1044 device = get_device_ref(id);
1046 if ((dwDevType == 0) ||
1047 ((dwDevType == DIDEVTYPE_JOYSTICK) && (version >= 0x0300 && version < 0x0800)) ||
1048 (((dwDevType == DI8DEVCLASS_GAMECTRL) || (dwDevType == DI8DEVTYPE_JOYSTICK)) && (version >= 0x0800))) {
1050 if (dwFlags & DIEDFL_FORCEFEEDBACK) {
1051 if(!device)
1052 return S_FALSE;
1053 if(get_ff(device, NULL) != S_OK)
1054 return S_FALSE;
1056 is_joystick = get_device_property_long(device, CFSTR(kIOHIDDeviceUsageKey)) == kHIDUsage_GD_Joystick;
1057 /* Return joystick */
1058 lpddi->guidInstance = DInput_Wine_OsX_Joystick_GUID;
1059 lpddi->guidInstance.Data3 = id;
1060 lpddi->guidProduct = DInput_PIDVID_Product_GUID;
1061 lpddi->guidProduct.Data1 = make_vid_pid(device);
1062 lpddi->dwDevType = get_device_type(version, is_joystick);
1063 lpddi->dwDevType |= DIDEVTYPE_HID;
1064 lpddi->wUsagePage = 0x01; /* Desktop */
1065 if (is_joystick)
1066 lpddi->wUsage = 0x04; /* Joystick */
1067 else
1068 lpddi->wUsage = 0x05; /* Game Pad */
1069 sprintf(friendly, "Joystick %d", id);
1070 MultiByteToWideChar(CP_ACP, 0, friendly, -1, lpddi->tszInstanceName, MAX_PATH);
1072 /* get the device name */
1073 get_osx_device_name(id, name, MAX_PATH);
1075 MultiByteToWideChar(CP_ACP, 0, name, -1, lpddi->tszProductName, MAX_PATH);
1076 lpddi->guidFFDriver = GUID_NULL;
1077 return S_OK;
1080 return S_FALSE;
1083 static const char *osx_ff_axis_name(UInt8 axis)
1085 static char ret[6];
1086 switch(axis){
1087 case FFJOFS_X:
1088 return "FFJOFS_X";
1089 case FFJOFS_Y:
1090 return "FFJOFS_Y";
1091 case FFJOFS_Z:
1092 return "FFJOFS_Z";
1094 sprintf(ret, "%u", (unsigned int)axis);
1095 return ret;
1098 static BOOL osx_axis_has_ff(FFCAPABILITIES *ffcaps, UInt8 axis)
1100 int i;
1101 for(i = 0; i < ffcaps->numFfAxes; ++i)
1102 if(ffcaps->ffAxes[i] == axis)
1103 return TRUE;
1104 return FALSE;
1107 static HRESULT alloc_device( REFGUID rguid, IDirectInputImpl *dinput, JoystickImpl **out, unsigned short index )
1109 DWORD i;
1110 IOHIDDeviceRef device;
1111 JoystickImpl* newDevice;
1112 char name[MAX_PATH];
1113 HRESULT hr;
1114 LPDIDATAFORMAT df = NULL;
1115 int idx = 0;
1116 uint64_t axis_map[8]; /* max axes */
1117 int slider_count = 0;
1118 FFCAPABILITIES ffcaps;
1120 TRACE( "%s %p %p %hu\n", debugstr_guid( rguid ), dinput, out, index );
1122 if (FAILED(hr = direct_input_device_alloc( sizeof(JoystickImpl), &JoystickWvt, rguid, dinput, (void **)&newDevice )))
1123 return hr;
1124 newDevice->generic.base.crit.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": JoystickImpl*->generic.base.crit");
1126 newDevice->id = index;
1128 device = get_device_ref(index);
1130 newDevice->generic.guidInstance = DInput_Wine_OsX_Joystick_GUID;
1131 newDevice->generic.guidInstance.Data3 = index;
1132 newDevice->generic.guidProduct = DInput_PIDVID_Product_GUID;
1133 newDevice->generic.guidProduct.Data1 = make_vid_pid(device);
1134 newDevice->generic.joy_polldev = poll_osx_device_state;
1136 /* get the device name */
1137 get_osx_device_name(index, name, MAX_PATH);
1138 TRACE("Name %s\n",name);
1140 /* copy the device name */
1141 newDevice->generic.name = HeapAlloc(GetProcessHeap(),0,strlen(name) + 1);
1142 strcpy(newDevice->generic.name, name);
1144 list_init(&newDevice->effects);
1145 if(get_ff(device, &newDevice->ff) == S_OK){
1146 newDevice->generic.devcaps.dwFlags |= DIDC_FORCEFEEDBACK;
1148 hr = FFDeviceGetForceFeedbackCapabilities(newDevice->ff, &ffcaps);
1149 if(SUCCEEDED(hr)){
1150 TRACE("FF Capabilities:\n");
1151 TRACE("\tsupportedEffects: 0x%x\n", (unsigned int)ffcaps.supportedEffects);
1152 TRACE("\temulatedEffects: 0x%x\n", (unsigned int)ffcaps.emulatedEffects);
1153 TRACE("\tsubType: 0x%x\n", (unsigned int)ffcaps.subType);
1154 TRACE("\tnumFfAxes: %u\n", (unsigned int)ffcaps.numFfAxes);
1155 TRACE("\tffAxes: [");
1156 for(i = 0; i < ffcaps.numFfAxes; ++i){
1157 TRACE("%s", osx_ff_axis_name(ffcaps.ffAxes[i]));
1158 if(i < ffcaps.numFfAxes - 1)
1159 TRACE(", ");
1161 TRACE("]\n");
1162 TRACE("\tstorageCapacity: %u\n", (unsigned int)ffcaps.storageCapacity);
1163 TRACE("\tplaybackCapacity: %u\n", (unsigned int)ffcaps.playbackCapacity);
1166 hr = FFDeviceSendForceFeedbackCommand(newDevice->ff, FFSFFC_RESET);
1167 if(FAILED(hr))
1168 WARN("FFDeviceSendForceFeedbackCommand(FFSFFC_RESET) failed: %08x\n", hr);
1170 hr = FFDeviceSendForceFeedbackCommand(newDevice->ff, FFSFFC_SETACTUATORSON);
1171 if(FAILED(hr))
1172 WARN("FFDeviceSendForceFeedbackCommand(FFSFFC_SETACTUATORSON) failed: %08x\n", hr);
1175 memset(axis_map, 0, sizeof(axis_map));
1176 get_osx_device_elements(newDevice, axis_map);
1178 TRACE("%i axes %i buttons %i povs\n",newDevice->generic.devcaps.dwAxes,newDevice->generic.devcaps.dwButtons,newDevice->generic.devcaps.dwPOVs);
1180 if (newDevice->generic.devcaps.dwButtons > 128)
1182 WARN("Can't support %d buttons. Clamping down to 128\n", newDevice->generic.devcaps.dwButtons);
1183 newDevice->generic.devcaps.dwButtons = 128;
1186 /* Create copy of default data format */
1187 if (!(df = HeapAlloc(GetProcessHeap(), 0, c_dfDIJoystick2.dwSize))) goto FAILED;
1188 memcpy(df, &c_dfDIJoystick2, c_dfDIJoystick2.dwSize);
1190 df->dwNumObjs = newDevice->generic.devcaps.dwAxes + newDevice->generic.devcaps.dwPOVs + newDevice->generic.devcaps.dwButtons;
1191 if (!(df->rgodf = HeapAlloc(GetProcessHeap(), 0, df->dwNumObjs * df->dwObjSize))) goto FAILED;
1193 for (i = 0; i < newDevice->generic.devcaps.dwAxes; i++)
1195 int wine_obj = -1;
1196 BOOL has_ff = FALSE;
1197 switch (axis_map[i])
1199 case MAKEUINT64(kHIDPage_GenericDesktop, kHIDUsage_GD_X):
1200 wine_obj = 0;
1201 has_ff = (newDevice->ff != 0) && osx_axis_has_ff(&ffcaps, FFJOFS_X);
1202 break;
1203 case MAKEUINT64(kHIDPage_GenericDesktop, kHIDUsage_GD_Y):
1204 wine_obj = 1;
1205 has_ff = (newDevice->ff != 0) && osx_axis_has_ff(&ffcaps, FFJOFS_Y);
1206 break;
1207 case MAKEUINT64(kHIDPage_GenericDesktop, kHIDUsage_GD_Z):
1208 wine_obj = 2;
1209 has_ff = (newDevice->ff != 0) && osx_axis_has_ff(&ffcaps, FFJOFS_Z);
1210 break;
1211 case MAKEUINT64(kHIDPage_GenericDesktop, kHIDUsage_GD_Rx):
1212 case MAKEUINT64(kHIDPage_Simulation, kHIDUsage_Sim_Accelerator):
1213 wine_obj = 3;
1214 has_ff = (newDevice->ff != 0) && osx_axis_has_ff(&ffcaps, FFJOFS_RX);
1215 break;
1216 case MAKEUINT64(kHIDPage_GenericDesktop, kHIDUsage_GD_Ry):
1217 case MAKEUINT64(kHIDPage_Simulation, kHIDUsage_Sim_Brake):
1218 wine_obj = 4;
1219 has_ff = (newDevice->ff != 0) && osx_axis_has_ff(&ffcaps, FFJOFS_RY);
1220 break;
1221 case MAKEUINT64(kHIDPage_GenericDesktop, kHIDUsage_GD_Rz):
1222 wine_obj = 5;
1223 has_ff = (newDevice->ff != 0) && osx_axis_has_ff(&ffcaps, FFJOFS_RZ);
1224 break;
1225 case MAKEUINT64(kHIDPage_GenericDesktop, kHIDUsage_GD_Slider):
1226 wine_obj = 6 + slider_count;
1227 has_ff = (newDevice->ff != 0) && osx_axis_has_ff(&ffcaps, FFJOFS_SLIDER(slider_count));
1228 slider_count++;
1229 break;
1231 if (wine_obj < 0 ) continue;
1233 memcpy(&df->rgodf[idx], &c_dfDIJoystick2.rgodf[wine_obj], df->dwObjSize);
1234 df->rgodf[idx].dwType = DIDFT_MAKEINSTANCE(wine_obj) | DIDFT_ABSAXIS;
1235 if(has_ff)
1236 df->rgodf[idx].dwFlags |= DIDOI_FFACTUATOR;
1237 ++idx;
1240 for (i = 0; i < newDevice->generic.devcaps.dwPOVs; i++)
1242 memcpy(&df->rgodf[idx], &c_dfDIJoystick2.rgodf[i + 8], df->dwObjSize);
1243 df->rgodf[idx++].dwType = DIDFT_MAKEINSTANCE(i) | DIDFT_POV;
1246 for (i = 0; i < newDevice->generic.devcaps.dwButtons; i++)
1248 memcpy(&df->rgodf[idx], &c_dfDIJoystick2.rgodf[i + 12], df->dwObjSize);
1249 df->rgodf[idx ].pguid = &GUID_Button;
1250 df->rgodf[idx++].dwType = DIDFT_MAKEINSTANCE(i) | DIDFT_PSHBUTTON;
1252 newDevice->generic.base.data_format.wine_df = df;
1254 /* initialize default properties */
1255 get_osx_device_elements_props(newDevice);
1257 newDevice->generic.devcaps.dwSize = sizeof(newDevice->generic.devcaps);
1258 newDevice->generic.devcaps.dwFlags |= DIDC_ATTACHED;
1259 if (newDevice->generic.base.dinput->dwVersion >= 0x0800)
1260 newDevice->generic.devcaps.dwDevType = DI8DEVTYPE_JOYSTICK | (DI8DEVTYPEJOYSTICK_STANDARD << 8);
1261 else
1262 newDevice->generic.devcaps.dwDevType = DIDEVTYPE_JOYSTICK | (DIDEVTYPEJOYSTICK_TRADITIONAL << 8);
1263 newDevice->generic.devcaps.dwFFSamplePeriod = 0;
1264 newDevice->generic.devcaps.dwFFMinTimeResolution = 0;
1265 newDevice->generic.devcaps.dwFirmwareRevision = 0;
1266 newDevice->generic.devcaps.dwHardwareRevision = 0;
1267 newDevice->generic.devcaps.dwFFDriverVersion = 0;
1269 if (TRACE_ON(dinput)) {
1270 TRACE("allocated device %p\n", newDevice);
1271 _dump_DIDATAFORMAT(newDevice->generic.base.data_format.wine_df);
1272 _dump_DIDEVCAPS(&newDevice->generic.devcaps);
1275 *out = newDevice;
1276 return DI_OK;
1278 FAILED:
1279 hr = DIERR_OUTOFMEMORY;
1280 if (newDevice->ff) FFReleaseDevice(newDevice->ff);
1281 if (newDevice->elements) CFRelease(newDevice->elements);
1282 if (df) HeapFree(GetProcessHeap(), 0, df->rgodf);
1283 HeapFree(GetProcessHeap(), 0, df);
1284 release_DataFormat(&newDevice->generic.base.data_format);
1285 HeapFree(GetProcessHeap(),0,newDevice->generic.name);
1286 HeapFree(GetProcessHeap(),0,newDevice);
1287 return hr;
1290 /******************************************************************************
1291 * get_joystick_index : Get the joystick index from a given GUID
1293 static unsigned short get_joystick_index(REFGUID guid)
1295 GUID wine_joystick = DInput_Wine_OsX_Joystick_GUID;
1296 GUID dev_guid = *guid;
1297 GUID prod_guid = *guid;
1298 IOHIDDeviceRef device;
1299 int joystick_devices_count;
1300 INT i;
1302 wine_joystick.Data3 = 0;
1303 dev_guid.Data3 = 0;
1305 /* for the standard joystick GUID use index 0 */
1306 if(IsEqualGUID(&GUID_Joystick,guid)) return 0;
1308 /* for the wine joystick GUIDs use the index stored in Data3 */
1309 if(IsEqualGUID(&wine_joystick, &dev_guid)) return guid->Data3;
1311 prod_guid.Data1 = 0;
1312 if(IsEqualGUID(&DInput_PIDVID_Product_GUID, &prod_guid))
1314 joystick_devices_count = find_joystick_devices();
1315 for(i = 0; i < joystick_devices_count; i++)
1317 device = get_device_ref(i);
1318 if(guid->Data1 == make_vid_pid(device))
1319 return i;
1323 return 0xffff;
1326 static HRESULT joydev_create_device( IDirectInputImpl *dinput, REFGUID rguid, IDirectInputDevice8W **out )
1328 unsigned short index;
1329 int joystick_devices_count;
1331 TRACE( "%p %s %p\n", dinput, debugstr_guid( rguid ), out );
1332 *out = NULL;
1334 if ((joystick_devices_count = find_joystick_devices()) == 0)
1335 return DIERR_DEVICENOTREG;
1337 if ((index = get_joystick_index(rguid)) < 0xffff &&
1338 joystick_devices_count && index < joystick_devices_count)
1340 JoystickImpl *This;
1341 HRESULT hr;
1343 if (FAILED(hr = alloc_device( rguid, dinput, &This, index ))) return hr;
1345 TRACE( "Created a Joystick device (%p)\n", This );
1347 *out = &This->generic.base.IDirectInputDevice8W_iface;
1348 return hr;
1351 return DIERR_DEVICENOTREG;
1354 static HRESULT WINAPI JoystickWImpl_GetProperty(LPDIRECTINPUTDEVICE8W iface, REFGUID rguid, LPDIPROPHEADER pdiph)
1356 JoystickImpl *This = impl_from_IDirectInputDevice8W(iface);
1358 TRACE("(%p)->(%s,%p)\n", This, debugstr_guid(rguid), pdiph);
1359 _dump_DIPROPHEADER(pdiph);
1361 if (!IS_DIPROP(rguid)) return DI_OK;
1363 switch (LOWORD(rguid)) {
1364 case (DWORD_PTR) DIPROP_GUIDANDPATH:
1366 static const WCHAR formatW[] = {'\\','\\','?','\\','h','i','d','#','v','i','d','_','%','0','4','x','&',
1367 'p','i','d','_','%','0','4','x','&','%','s','_','%','i',0};
1368 static const WCHAR miW[] = {'m','i',0};
1369 static const WCHAR igW[] = {'i','g',0};
1371 BOOL is_gamepad;
1372 IOHIDDeviceRef device = get_device_ref(This->id);
1373 LPDIPROPGUIDANDPATH pd = (LPDIPROPGUIDANDPATH)pdiph;
1374 WORD vid = get_device_property_long(device, CFSTR(kIOHIDVendorIDKey));
1375 WORD pid = get_device_property_long(device, CFSTR(kIOHIDProductIDKey));
1377 if (!pid || !vid)
1378 return DIERR_UNSUPPORTED;
1380 is_gamepad = is_xinput_device(&This->generic.devcaps, vid, pid);
1381 pd->guidClass = GUID_DEVCLASS_HIDCLASS;
1382 sprintfW(pd->wszPath, formatW, vid, pid, is_gamepad ? igW : miW, This->id);
1384 TRACE("DIPROP_GUIDANDPATH(%s, %s): returning fake path\n", debugstr_guid(&pd->guidClass), debugstr_w(pd->wszPath));
1385 break;
1388 default:
1389 return JoystickWGenericImpl_GetProperty(iface, rguid, pdiph);
1392 return DI_OK;
1395 static HRESULT osx_set_autocenter(JoystickImpl *This,
1396 const DIPROPDWORD *header)
1398 UInt32 v;
1399 HRESULT hr;
1400 if(!This->ff)
1401 return DIERR_UNSUPPORTED;
1402 v = header->dwData;
1403 hr = osx_to_win32_hresult(FFDeviceSetForceFeedbackProperty(This->ff, FFPROP_AUTOCENTER, &v));
1404 TRACE("returning: %08x\n", hr);
1405 return hr;
1408 static HRESULT osx_set_ffgain(JoystickImpl *This, const DIPROPDWORD *header)
1410 UInt32 v;
1411 HRESULT hr;
1412 if(!This->ff)
1413 return DIERR_UNSUPPORTED;
1414 v = header->dwData;
1415 hr = osx_to_win32_hresult(FFDeviceSetForceFeedbackProperty(This->ff, FFPROP_FFGAIN, &v));
1416 TRACE("returning: %08x\n", hr);
1417 return hr;
1420 static HRESULT WINAPI JoystickWImpl_SetProperty(IDirectInputDevice8W *iface,
1421 const GUID *prop, const DIPROPHEADER *header)
1423 JoystickImpl *This = impl_from_IDirectInputDevice8W(iface);
1425 TRACE("%p %s %p\n", This, debugstr_guid(prop), header);
1427 switch(LOWORD(prop))
1429 case (DWORD_PTR)DIPROP_AUTOCENTER:
1430 return osx_set_autocenter(This, (const DIPROPDWORD *)header);
1431 case (DWORD_PTR)DIPROP_FFGAIN:
1432 return osx_set_ffgain(This, (const DIPROPDWORD *)header);
1435 return JoystickWGenericImpl_SetProperty(iface, prop, header);
1438 static CFUUIDRef effect_win_to_mac(const GUID *effect)
1440 #define DO_MAP(X) \
1441 if(IsEqualGUID(&GUID_##X, effect)) \
1442 return kFFEffectType_##X##_ID;
1443 DO_MAP(ConstantForce)
1444 DO_MAP(RampForce)
1445 DO_MAP(Square)
1446 DO_MAP(Sine)
1447 DO_MAP(Triangle)
1448 DO_MAP(SawtoothUp)
1449 DO_MAP(SawtoothDown)
1450 DO_MAP(Spring)
1451 DO_MAP(Damper)
1452 DO_MAP(Inertia)
1453 DO_MAP(Friction)
1454 DO_MAP(CustomForce)
1455 #undef DO_MAP
1456 WARN("Unknown effect GUID! %s\n", debugstr_guid(effect));
1457 return 0;
1460 static HRESULT WINAPI JoystickWImpl_CreateEffect(IDirectInputDevice8W *iface,
1461 const GUID *type, const DIEFFECT *params, IDirectInputEffect **out,
1462 IUnknown *outer)
1464 JoystickImpl *This = impl_from_IDirectInputDevice8W(iface);
1465 EffectImpl *effect;
1466 HRESULT hr;
1468 TRACE("(%p)->(%s %p %p %p)\n", This, debugstr_guid(type), params, out, outer);
1469 dump_DIEFFECT(params, type, 0);
1471 if(!This->ff){
1472 TRACE("No force feedback support\n");
1473 *out = NULL;
1474 return DIERR_UNSUPPORTED;
1477 if(outer)
1478 WARN("aggregation not implemented\n");
1480 effect = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*This));
1481 effect->IDirectInputEffect_iface.lpVtbl = &EffectVtbl;
1482 effect->ref = 1;
1483 effect->guid = *type;
1484 effect->device = This;
1486 /* Mac's FFEFFECT and Win's DIEFFECT are binary identical. */
1487 hr = osx_to_win32_hresult(FFDeviceCreateEffect(This->ff,
1488 effect_win_to_mac(type), (FFEFFECT*)params, &effect->effect));
1489 if(FAILED(hr)){
1490 WARN("FFDeviceCreateEffect failed: %08x\n", hr);
1491 HeapFree(GetProcessHeap(), 0, effect);
1492 return hr;
1495 list_add_tail(&This->effects, &effect->entry);
1496 *out = &effect->IDirectInputEffect_iface;
1498 TRACE("allocated effect: %p\n", effect);
1500 return S_OK;
1503 static HRESULT WINAPI JoystickWImpl_SendForceFeedbackCommand(IDirectInputDevice8W *iface,
1504 DWORD flags)
1506 JoystickImpl *This = impl_from_IDirectInputDevice8W(iface);
1507 HRESULT hr;
1509 TRACE("%p 0x%x\n", This, flags);
1511 if(!This->ff)
1512 return DI_NOEFFECT;
1514 hr = osx_to_win32_hresult(FFDeviceSendForceFeedbackCommand(This->ff, flags));
1515 if(FAILED(hr)){
1516 WARN("FFDeviceSendForceFeedbackCommand failed: %08x\n", hr);
1517 return hr;
1520 return S_OK;
1523 const struct dinput_device joystick_osx_device = {
1524 "Wine OS X joystick driver",
1525 joydev_enum_deviceA,
1526 joydev_enum_deviceW,
1527 joydev_create_device
1530 static const IDirectInputDevice8WVtbl JoystickWvt =
1532 IDirectInputDevice2WImpl_QueryInterface,
1533 IDirectInputDevice2WImpl_AddRef,
1534 IDirectInputDevice2WImpl_Release,
1535 JoystickWGenericImpl_GetCapabilities,
1536 IDirectInputDevice2WImpl_EnumObjects,
1537 JoystickWImpl_GetProperty,
1538 JoystickWImpl_SetProperty,
1539 IDirectInputDevice2WImpl_Acquire,
1540 IDirectInputDevice2WImpl_Unacquire,
1541 JoystickWGenericImpl_GetDeviceState,
1542 IDirectInputDevice2WImpl_GetDeviceData,
1543 IDirectInputDevice2WImpl_SetDataFormat,
1544 IDirectInputDevice2WImpl_SetEventNotification,
1545 IDirectInputDevice2WImpl_SetCooperativeLevel,
1546 JoystickWGenericImpl_GetObjectInfo,
1547 JoystickWGenericImpl_GetDeviceInfo,
1548 IDirectInputDevice2WImpl_RunControlPanel,
1549 IDirectInputDevice2WImpl_Initialize,
1550 JoystickWImpl_CreateEffect,
1551 IDirectInputDevice2WImpl_EnumEffects,
1552 IDirectInputDevice2WImpl_GetEffectInfo,
1553 IDirectInputDevice2WImpl_GetForceFeedbackState,
1554 JoystickWImpl_SendForceFeedbackCommand,
1555 IDirectInputDevice2WImpl_EnumCreatedEffectObjects,
1556 IDirectInputDevice2WImpl_Escape,
1557 JoystickWGenericImpl_Poll,
1558 IDirectInputDevice2WImpl_SendDeviceData,
1559 IDirectInputDevice7WImpl_EnumEffectsInFile,
1560 IDirectInputDevice7WImpl_WriteEffectToFile,
1561 JoystickWGenericImpl_BuildActionMap,
1562 JoystickWGenericImpl_SetActionMap,
1563 IDirectInputDevice8WImpl_GetImageInfo
1566 static HRESULT WINAPI effect_QueryInterface(IDirectInputEffect *iface,
1567 const GUID *guid, void **out)
1569 EffectImpl *This = impl_from_IDirectInputEffect(iface);
1571 TRACE("%p %s %p\n", This, debugstr_guid(guid), out);
1573 if(IsEqualIID(guid, &IID_IUnknown) || IsEqualIID(guid, &IID_IDirectInputEffect)){
1574 *out = iface;
1575 IDirectInputEffect_AddRef(iface);
1576 return S_OK;
1579 return E_NOINTERFACE;
1582 static ULONG WINAPI effect_AddRef(IDirectInputEffect *iface)
1584 EffectImpl *This = impl_from_IDirectInputEffect(iface);
1585 ULONG ref = InterlockedIncrement(&This->ref);
1586 TRACE("%p, ref is now: %u\n", This, ref);
1587 return ref;
1590 static ULONG WINAPI effect_Release(IDirectInputEffect *iface)
1592 EffectImpl *This = impl_from_IDirectInputEffect(iface);
1593 ULONG ref = InterlockedDecrement(&This->ref);
1594 TRACE("%p, ref is now: %u\n", This, ref);
1596 if(!ref){
1597 list_remove(&This->entry);
1598 FFDeviceReleaseEffect(This->device->ff, This->effect);
1599 HeapFree(GetProcessHeap(), 0, This);
1602 return ref;
1605 static HRESULT WINAPI effect_Initialize(IDirectInputEffect *iface, HINSTANCE hinst,
1606 DWORD version, const GUID *guid)
1608 EffectImpl *This = impl_from_IDirectInputEffect(iface);
1609 TRACE("%p %p 0x%x, %s\n", This, hinst, version, debugstr_guid(guid));
1610 return S_OK;
1613 static HRESULT WINAPI effect_GetEffectGuid(IDirectInputEffect *iface, GUID *out)
1615 EffectImpl *This = impl_from_IDirectInputEffect(iface);
1616 TRACE("%p %p\n", This, out);
1617 *out = This->guid;
1618 return S_OK;
1621 static HRESULT WINAPI effect_GetParameters(IDirectInputEffect *iface,
1622 DIEFFECT *effect, DWORD flags)
1624 EffectImpl *This = impl_from_IDirectInputEffect(iface);
1625 TRACE("%p %p 0x%x\n", This, effect, flags);
1626 return osx_to_win32_hresult(FFEffectGetParameters(This->effect, (FFEFFECT*)effect, flags));
1629 static HRESULT WINAPI effect_SetParameters(IDirectInputEffect *iface,
1630 const DIEFFECT *effect, DWORD flags)
1632 EffectImpl *This = impl_from_IDirectInputEffect(iface);
1633 TRACE("%p %p 0x%x\n", This, effect, flags);
1634 dump_DIEFFECT(effect, &This->guid, flags);
1635 return osx_to_win32_hresult(FFEffectSetParameters(This->effect, (FFEFFECT*)effect, flags));
1638 static HRESULT WINAPI effect_Start(IDirectInputEffect *iface, DWORD iterations,
1639 DWORD flags)
1641 EffectImpl *This = impl_from_IDirectInputEffect(iface);
1642 TRACE("%p 0x%x 0x%x\n", This, iterations, flags);
1643 return osx_to_win32_hresult(FFEffectStart(This->effect, iterations, flags));
1646 static HRESULT WINAPI effect_Stop(IDirectInputEffect *iface)
1648 EffectImpl *This = impl_from_IDirectInputEffect(iface);
1649 TRACE("%p\n", This);
1650 return osx_to_win32_hresult(FFEffectStop(This->effect));
1653 static HRESULT WINAPI effect_GetEffectStatus(IDirectInputEffect *iface, DWORD *flags)
1655 EffectImpl *This = impl_from_IDirectInputEffect(iface);
1656 TRACE("%p %p\n", This, flags);
1657 return osx_to_win32_hresult(FFEffectGetEffectStatus(This->effect, (UInt32*)flags));
1660 static HRESULT WINAPI effect_Download(IDirectInputEffect *iface)
1662 EffectImpl *This = impl_from_IDirectInputEffect(iface);
1663 TRACE("%p\n", This);
1664 return osx_to_win32_hresult(FFEffectDownload(This->effect));
1667 static HRESULT WINAPI effect_Unload(IDirectInputEffect *iface)
1669 EffectImpl *This = impl_from_IDirectInputEffect(iface);
1670 TRACE("%p\n", This);
1671 return osx_to_win32_hresult(FFEffectUnload(This->effect));
1674 static HRESULT WINAPI effect_Escape(IDirectInputEffect *iface, DIEFFESCAPE *escape)
1676 EffectImpl *This = impl_from_IDirectInputEffect(iface);
1677 TRACE("%p %p\n", This, escape);
1678 return osx_to_win32_hresult(FFEffectEscape(This->effect, (FFEFFESCAPE*)escape));
1681 static const IDirectInputEffectVtbl EffectVtbl = {
1682 effect_QueryInterface,
1683 effect_AddRef,
1684 effect_Release,
1685 effect_Initialize,
1686 effect_GetEffectGuid,
1687 effect_GetParameters,
1688 effect_SetParameters,
1689 effect_Start,
1690 effect_Stop,
1691 effect_GetEffectStatus,
1692 effect_Download,
1693 effect_Unload,
1694 effect_Escape
1697 #else /* HAVE_IOHIDMANAGERCREATE */
1699 const struct dinput_device joystick_osx_device = {
1700 "Wine OS X joystick driver",
1701 NULL,
1702 NULL,
1703 NULL
1706 #endif /* HAVE_IOHIDMANAGERCREATE */