msado15: Implement Dispatch functions in Fields.
[wine.git] / dlls / dinput / joystick_osx.c
blob805b75639fe6c51b86b4c98402d35a67547d72e5
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 IDirectInputDevice8AVtbl JoystickAvt;
105 static const IDirectInputDevice8WVtbl JoystickWvt;
107 struct JoystickImpl
109 struct JoystickGenericImpl generic;
111 /* osx private */
112 int id;
113 CFArrayRef elements;
114 ObjProps **propmap;
115 FFDeviceObjectReference ff;
116 struct list effects;
119 static inline JoystickImpl *impl_from_IDirectInputDevice8A(IDirectInputDevice8A *iface)
121 return CONTAINING_RECORD(CONTAINING_RECORD(CONTAINING_RECORD(iface, IDirectInputDeviceImpl, IDirectInputDevice8A_iface),
122 JoystickGenericImpl, base), JoystickImpl, generic);
124 static inline JoystickImpl *impl_from_IDirectInputDevice8W(IDirectInputDevice8W *iface)
126 return CONTAINING_RECORD(CONTAINING_RECORD(CONTAINING_RECORD(iface, IDirectInputDeviceImpl, IDirectInputDevice8W_iface),
127 JoystickGenericImpl, base), JoystickImpl, generic);
130 static inline IDirectInputDevice8W *IDirectInputDevice8W_from_impl(JoystickImpl *This)
132 return &This->generic.base.IDirectInputDevice8W_iface;
135 typedef struct _EffectImpl {
136 IDirectInputEffect IDirectInputEffect_iface;
137 LONG ref;
139 JoystickImpl *device;
140 FFEffectObjectReference effect;
141 GUID guid;
143 struct list entry;
144 } EffectImpl;
146 static EffectImpl *impl_from_IDirectInputEffect(IDirectInputEffect *iface)
148 return CONTAINING_RECORD(iface, EffectImpl, IDirectInputEffect_iface);
151 static const IDirectInputEffectVtbl EffectVtbl;
153 static const GUID DInput_Wine_OsX_Joystick_GUID = { /* 59CAD8F6-E617-41E2-8EB7-47B23EEEDC5A */
154 0x59CAD8F6, 0xE617, 0x41E2, {0x8E, 0xB7, 0x47, 0xB2, 0x3E, 0xEE, 0xDC, 0x5A}
157 static HRESULT osx_to_win32_hresult(HRESULT in)
159 /* OSX returns 16-bit COM runtime errors, which we should
160 * convert to win32 */
161 switch(in){
162 case 0x80000001:
163 return E_NOTIMPL;
164 case 0x80000002:
165 return E_OUTOFMEMORY;
166 case 0x80000003:
167 return E_INVALIDARG;
168 case 0x80000004:
169 return E_NOINTERFACE;
170 case 0x80000005:
171 return E_POINTER;
172 case 0x80000006:
173 return E_HANDLE;
174 case 0x80000007:
175 return E_ABORT;
176 case 0x80000008:
177 return E_FAIL;
178 case 0x80000009:
179 return E_ACCESSDENIED;
180 case 0x8000FFFF:
181 return E_UNEXPECTED;
183 return in;
186 static long get_device_property_long(IOHIDDeviceRef device, CFStringRef key)
188 CFTypeRef ref;
189 long result = 0;
191 if (device)
193 assert(IOHIDDeviceGetTypeID() == CFGetTypeID(device));
195 ref = IOHIDDeviceGetProperty(device, key);
197 if (ref && CFNumberGetTypeID() == CFGetTypeID(ref))
198 CFNumberGetValue((CFNumberRef)ref, kCFNumberLongType, &result);
201 return result;
204 static CFStringRef copy_device_name(IOHIDDeviceRef device)
206 CFStringRef name;
208 if (device)
210 CFTypeRef ref_name;
212 assert(IOHIDDeviceGetTypeID() == CFGetTypeID(device));
214 ref_name = IOHIDDeviceGetProperty(device, CFSTR(kIOHIDProductKey));
216 if (ref_name && CFStringGetTypeID() == CFGetTypeID(ref_name))
217 name = CFStringCreateCopy(kCFAllocatorDefault, ref_name);
218 else
220 long vendID, prodID;
222 vendID = get_device_property_long(device, CFSTR(kIOHIDVendorIDKey));
223 prodID = get_device_property_long(device, CFSTR(kIOHIDProductIDKey));
224 name = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("0x%04lx 0x%04lx"), vendID, prodID);
227 else
229 ERR("NULL device\n");
230 name = CFStringCreateCopy(kCFAllocatorDefault, CFSTR(""));
233 return name;
236 static long get_device_location_ID(IOHIDDeviceRef device)
238 return get_device_property_long(device, CFSTR(kIOHIDLocationIDKey));
241 static void copy_set_to_array(const void *value, void *context)
243 CFArrayAppendValue(context, value);
246 static CFComparisonResult device_name_comparator(IOHIDDeviceRef device1, IOHIDDeviceRef device2)
248 CFStringRef name1 = copy_device_name(device1), name2 = copy_device_name(device2);
249 CFComparisonResult result = CFStringCompare(name1, name2, (kCFCompareForcedOrdering | kCFCompareNumerically));
250 CFRelease(name1);
251 CFRelease(name2);
252 return result;
255 static CFComparisonResult device_location_name_comparator(const void *val1, const void *val2, void *context)
257 IOHIDDeviceRef device1 = (IOHIDDeviceRef)val1, device2 = (IOHIDDeviceRef)val2;
258 long loc1 = get_device_location_ID(device1), loc2 = get_device_location_ID(device2);
260 if (loc1 < loc2)
261 return kCFCompareLessThan;
262 else if (loc1 > loc2)
263 return kCFCompareGreaterThan;
264 /* virtual joysticks may not have a kIOHIDLocationIDKey and will default to location ID of 0, this orders virtual joysticks by their name */
265 return device_name_comparator(device1, device2);
268 static const char* debugstr_cf(CFTypeRef t)
270 CFStringRef s;
271 const char* ret;
273 if (!t) return "(null)";
275 if (CFGetTypeID(t) == CFStringGetTypeID())
276 s = t;
277 else
278 s = CFCopyDescription(t);
279 ret = CFStringGetCStringPtr(s, kCFStringEncodingUTF8);
280 if (ret) ret = debugstr_a(ret);
281 if (!ret)
283 const UniChar* u = CFStringGetCharactersPtr(s);
284 if (u)
285 ret = debugstr_wn((const WCHAR*)u, CFStringGetLength(s));
287 if (!ret)
289 UniChar buf[200];
290 int len = min(CFStringGetLength(s), ARRAY_SIZE(buf));
291 CFStringGetCharacters(s, CFRangeMake(0, len), buf);
292 ret = debugstr_wn(buf, len);
294 if (s != t) CFRelease(s);
295 return ret;
298 static const char* debugstr_device(IOHIDDeviceRef device)
300 return wine_dbg_sprintf("<IOHIDDevice %p product %s IOHIDLocationID %lu>", device,
301 debugstr_cf(IOHIDDeviceGetProperty(device, CFSTR(kIOHIDProductKey))),
302 get_device_location_ID(device));
305 static const char* debugstr_element(IOHIDElementRef element)
307 return wine_dbg_sprintf("<IOHIDElement %p type %d usage %u/%u device %p>", element,
308 IOHIDElementGetType(element), IOHIDElementGetUsagePage(element),
309 IOHIDElementGetUsage(element), IOHIDElementGetDevice(element));
312 static IOHIDDeviceRef get_device_ref(int id)
314 IOHIDElementRef device_main_element;
315 IOHIDDeviceRef hid_device;
317 TRACE("id %d\n", id);
319 if (!device_main_elements || id >= CFArrayGetCount(device_main_elements))
320 return 0;
322 device_main_element = (IOHIDElementRef)CFArrayGetValueAtIndex(device_main_elements, id);
323 if (!device_main_element)
325 ERR("Invalid Element requested %i\n",id);
326 return 0;
329 hid_device = IOHIDElementGetDevice(device_main_element);
330 if (!hid_device)
332 ERR("Invalid Device requested %i\n",id);
333 return 0;
336 TRACE("-> %s\n", debugstr_device(hid_device));
337 return hid_device;
340 static HRESULT get_ff(IOHIDDeviceRef device, FFDeviceObjectReference *ret)
342 io_service_t service;
343 CFMutableDictionaryRef matching;
344 CFTypeRef location_id;
345 HRESULT hr;
347 TRACE("device %s\n", debugstr_device(device));
349 matching = IOServiceMatching(kIOHIDDeviceKey);
350 if(!matching){
351 WARN("IOServiceMatching failed, force feedback disabled\n");
352 return DIERR_DEVICENOTREG;
355 location_id = IOHIDDeviceGetProperty(device, CFSTR(kIOHIDLocationIDKey));
356 if(!location_id){
357 CFRelease(matching);
358 WARN("IOHIDDeviceGetProperty failed, force feedback disabled\n");
359 return DIERR_DEVICENOTREG;
362 CFDictionaryAddValue(matching, CFSTR(kIOHIDLocationIDKey), location_id);
364 service = IOServiceGetMatchingService(kIOMasterPortDefault, matching);
366 if (ret)
367 hr = osx_to_win32_hresult(FFCreateDevice(service, ret));
368 else
369 hr = FFIsForceFeedback(service) == FF_OK ? S_OK : S_FALSE;
371 IOObjectRelease(service);
372 TRACE("-> hr 0x%08x *ret %p\n", hr, ret ? *ret : NULL);
373 return hr;
376 static CFMutableDictionaryRef create_osx_device_match(int usage)
378 CFMutableDictionaryRef result;
380 TRACE("usage %d\n", usage);
382 result = CFDictionaryCreateMutable( kCFAllocatorDefault, 0,
383 &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks );
385 if ( result )
387 int number = kHIDPage_GenericDesktop;
388 CFNumberRef page = CFNumberCreate( kCFAllocatorDefault,
389 kCFNumberIntType, &number);
391 if (page)
393 CFNumberRef cf_usage;
395 CFDictionarySetValue( result, CFSTR( kIOHIDDeviceUsagePageKey ), page );
396 CFRelease( page );
398 cf_usage = CFNumberCreate( kCFAllocatorDefault,
399 kCFNumberIntType, &usage);
400 if (cf_usage)
402 CFDictionarySetValue( result, CFSTR( kIOHIDDeviceUsageKey ), cf_usage );
403 CFRelease( cf_usage );
405 else
407 ERR("CFNumberCreate() failed.\n");
408 CFRelease(result);
409 return NULL;
412 else
414 ERR("CFNumberCreate failed.\n");
415 CFRelease(result);
416 return NULL;
419 else
421 ERR("CFDictionaryCreateMutable failed.\n");
422 return NULL;
425 return result;
428 static CFIndex find_top_level(IOHIDDeviceRef hid_device, CFMutableArrayRef main_elements)
430 CFArrayRef elements;
431 CFIndex total = 0;
433 TRACE("hid_device %s\n", debugstr_device(hid_device));
435 if (!hid_device)
436 return 0;
438 elements = IOHIDDeviceCopyMatchingElements(hid_device, NULL, 0);
440 if (elements)
442 CFIndex idx, cnt = CFArrayGetCount(elements);
443 for (idx=0; idx<cnt; idx++)
445 IOHIDElementRef element = (IOHIDElementRef)CFArrayGetValueAtIndex(elements, idx);
446 int type = IOHIDElementGetType(element);
448 TRACE("element %s\n", debugstr_element(element));
450 /* Check for top-level gaming device collections */
451 if (type == kIOHIDElementTypeCollection && IOHIDElementGetParent(element) == 0)
453 int usage_page = IOHIDElementGetUsagePage(element);
454 int usage = IOHIDElementGetUsage(element);
456 if (usage_page == kHIDPage_GenericDesktop &&
457 (usage == kHIDUsage_GD_Joystick || usage == kHIDUsage_GD_GamePad))
459 CFArrayAppendValue(main_elements, element);
460 total++;
464 CFRelease(elements);
467 TRACE("-> total %d\n", (int)total);
468 return total;
471 static void get_element_children(IOHIDElementRef element, CFMutableArrayRef all_children)
473 CFIndex idx, cnt;
474 CFArrayRef element_children = IOHIDElementGetChildren(element);
476 TRACE("element %s\n", debugstr_element(element));
478 cnt = CFArrayGetCount(element_children);
480 /* Either add the element to the array or grab its children */
481 for (idx=0; idx<cnt; idx++)
483 IOHIDElementRef child;
485 child = (IOHIDElementRef)CFArrayGetValueAtIndex(element_children, idx);
486 TRACE("child %s\n", debugstr_element(child));
487 if (IOHIDElementGetType(child) == kIOHIDElementTypeCollection)
488 get_element_children(child, all_children);
489 else
490 CFArrayAppendValue(all_children, child);
494 static int find_osx_devices(void)
496 IOHIDManagerRef hid_manager;
497 CFMutableDictionaryRef result;
498 CFSetRef devset;
499 CFMutableArrayRef matching;
501 TRACE("()\n");
503 hid_manager = IOHIDManagerCreate( kCFAllocatorDefault, 0L );
504 if (IOHIDManagerOpen( hid_manager, 0 ) != kIOReturnSuccess)
506 ERR("Couldn't open IOHIDManager.\n");
507 CFRelease( hid_manager );
508 return 0;
511 matching = CFArrayCreateMutable( kCFAllocatorDefault, 0,
512 &kCFTypeArrayCallBacks );
514 /* build matching dictionary */
515 result = create_osx_device_match(kHIDUsage_GD_Joystick);
516 if (!result)
518 CFRelease(matching);
519 goto fail;
521 CFArrayAppendValue( matching, result );
522 CFRelease( result );
523 result = create_osx_device_match(kHIDUsage_GD_GamePad);
524 if (!result)
526 CFRelease(matching);
527 goto fail;
529 CFArrayAppendValue( matching, result );
530 CFRelease( result );
532 IOHIDManagerSetDeviceMatchingMultiple( hid_manager, matching);
533 CFRelease( matching );
534 devset = IOHIDManagerCopyDevices( hid_manager );
535 if (devset)
537 CFIndex num_devices, num_main_elements, idx;
538 CFMutableArrayRef devices;
540 num_devices = CFSetGetCount(devset);
541 devices = CFArrayCreateMutable(kCFAllocatorDefault, num_devices, &kCFTypeArrayCallBacks);
542 CFSetApplyFunction(devset, copy_set_to_array, devices);
543 CFRelease(devset);
544 CFArraySortValues(devices, CFRangeMake(0, num_devices), device_location_name_comparator, NULL);
546 device_main_elements = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
547 if (!device_main_elements)
549 CFRelease( devices );
550 goto fail;
553 num_main_elements = 0;
554 for (idx = 0; idx < num_devices; idx++)
556 CFIndex top;
557 IOHIDDeviceRef hid_device;
559 hid_device = (IOHIDDeviceRef) CFArrayGetValueAtIndex(devices, idx);
560 TRACE("hid_device %s\n", debugstr_device(hid_device));
561 top = find_top_level(hid_device, device_main_elements);
562 num_main_elements += top;
565 CFRelease(devices);
567 TRACE("found %i device(s), %i collection(s)\n",(int)num_devices,(int)num_main_elements);
568 return (int)num_main_elements;
571 fail:
572 IOHIDManagerClose( hid_manager, 0 );
573 CFRelease( hid_manager );
574 return 0;
577 static int get_osx_device_name(int id, char *name, int length)
579 CFStringRef str;
580 IOHIDDeviceRef hid_device;
582 hid_device = get_device_ref(id);
584 TRACE("id %d hid_device %s\n", id, debugstr_device(hid_device));
586 if (name)
587 name[0] = 0;
589 if (!hid_device)
590 return 0;
592 str = IOHIDDeviceGetProperty(hid_device, CFSTR( kIOHIDProductKey ));
593 if (str)
595 CFIndex len = CFStringGetLength(str);
596 if (length >= len)
598 CFStringGetCString(str,name,length,kCFStringEncodingASCII);
599 return len;
601 else
602 return (len+1);
604 return 0;
607 static CFComparisonResult button_usage_comparator(const void *val1, const void *val2, void *context)
609 IOHIDElementRef element1 = (IOHIDElementRef)val1, element2 = (IOHIDElementRef)val2;
610 int usage1 = IOHIDElementGetUsage(element1), usage2 = IOHIDElementGetUsage(element2);
612 if (usage1 < usage2)
613 return kCFCompareLessThan;
614 if (usage1 > usage2)
615 return kCFCompareGreaterThan;
616 return kCFCompareEqualTo;
619 static void get_osx_device_elements(JoystickImpl *device, uint64_t axis_map[8])
621 IOHIDElementRef device_main_element;
622 CFMutableArrayRef elements;
623 DWORD sliders = 0;
624 BOOL use_accel_brake_for_rx_ry = TRUE;
626 TRACE("device %p device->id %d\n", device, device->id);
628 device->elements = NULL;
630 if (!device_main_elements || device->id >= CFArrayGetCount(device_main_elements))
631 return;
633 device_main_element = (IOHIDElementRef)CFArrayGetValueAtIndex(device_main_elements, device->id);
634 TRACE("device_main_element %s\n", debugstr_element(device_main_element));
635 if (!device_main_element)
636 return;
638 elements = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
639 get_element_children(device_main_element, elements);
641 if (elements)
643 CFIndex idx, cnt = CFArrayGetCount( elements );
644 CFMutableArrayRef axes = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
645 CFMutableArrayRef buttons = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
646 CFMutableArrayRef povs = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
648 /* Scan the elements to see if Rx/Ry is present, if not then Accelerator/Brake can be mapped to it.
649 * (Xbox One controller triggers use accelerator/brake)
651 for ( idx = 0; idx < cnt; idx++ )
653 IOHIDElementRef element = ( IOHIDElementRef ) CFArrayGetValueAtIndex( elements, idx );
654 IOHIDElementType type = IOHIDElementGetType( element );
655 uint32_t usage_page = IOHIDElementGetUsagePage( element );
656 uint32_t usage = IOHIDElementGetUsage( element );
658 if (type == kIOHIDElementTypeInput_Misc && usage_page == kHIDPage_GenericDesktop &&
659 (usage == kHIDUsage_GD_Rx || usage == kHIDUsage_GD_Ry))
661 use_accel_brake_for_rx_ry = FALSE;
665 for ( idx = 0; idx < cnt; idx++ )
667 IOHIDElementRef element = ( IOHIDElementRef ) CFArrayGetValueAtIndex( elements, idx );
668 int type = IOHIDElementGetType( element );
669 int usage_page = IOHIDElementGetUsagePage( element );
671 TRACE("element %s\n", debugstr_element(element));
673 if (usage_page >= kHIDPage_VendorDefinedStart)
675 /* vendor pages can repurpose type ids, resulting in incorrect case matches below (e.g. ds4 controllers) */
676 continue;
679 switch(type)
681 case kIOHIDElementTypeInput_Button:
683 TRACE("kIOHIDElementTypeInput_Button usage_page %d\n", usage_page);
684 if ((usage_page != kHIDPage_Button) && (usage_page != kHIDPage_Consumer))
686 /* avoid strange elements found on the 360 controller */
687 continue;
690 if (CFArrayGetCount(buttons) < 128)
691 CFArrayAppendValue(buttons, element);
692 break;
694 case kIOHIDElementTypeInput_Axis:
696 TRACE("kIOHIDElementTypeInput_Axis\n");
697 CFArrayAppendValue(axes, element);
698 break;
700 case kIOHIDElementTypeInput_Misc:
702 uint32_t usage = IOHIDElementGetUsage( element );
703 switch(MAKEUINT64(usage_page, usage))
705 case MAKEUINT64(kHIDPage_GenericDesktop, kHIDUsage_GD_Hatswitch):
707 TRACE("kIOHIDElementTypeInput_Misc / kHIDUsage_GD_Hatswitch\n");
708 CFArrayAppendValue(povs, element);
709 break;
711 case MAKEUINT64(kHIDPage_GenericDesktop, kHIDUsage_GD_Slider):
712 sliders ++;
713 if (sliders > 2)
714 break;
715 /* fallthrough, sliders are axis */
716 case MAKEUINT64(kHIDPage_GenericDesktop, kHIDUsage_GD_X):
717 case MAKEUINT64(kHIDPage_GenericDesktop, kHIDUsage_GD_Y):
718 case MAKEUINT64(kHIDPage_GenericDesktop, kHIDUsage_GD_Z):
719 case MAKEUINT64(kHIDPage_GenericDesktop, kHIDUsage_GD_Rx):
720 case MAKEUINT64(kHIDPage_GenericDesktop, kHIDUsage_GD_Ry):
721 case MAKEUINT64(kHIDPage_GenericDesktop, kHIDUsage_GD_Rz):
722 case MAKEUINT64(kHIDPage_Simulation, kHIDUsage_Sim_Accelerator):
723 case MAKEUINT64(kHIDPage_Simulation, kHIDUsage_Sim_Brake):
725 if (usage == kHIDUsage_Sim_Accelerator || usage == kHIDUsage_Sim_Brake)
727 if (use_accel_brake_for_rx_ry)
728 TRACE("Using Sim_Accelerator/Brake for GD_Rx/Ry\n");
729 else
730 break;
733 TRACE("kIOHIDElementTypeInput_Misc / kHIDUsage_GD_* (%d)\n", usage);
734 axis_map[CFArrayGetCount(axes)]=MAKEUINT64(usage_page, usage);
735 CFArrayAppendValue(axes, element);
736 break;
738 default:
739 FIXME("kIOHIDElementTypeInput_Misc / Unhandled usage %i/%i\n", usage_page, usage);
741 break;
743 default:
744 FIXME("Unhandled type %i\n",type);
748 /* Sort buttons into correct order */
749 CFArraySortValues(buttons, CFRangeMake(0, CFArrayGetCount(buttons)), button_usage_comparator, NULL);
751 device->generic.devcaps.dwAxes = CFArrayGetCount(axes);
752 device->generic.devcaps.dwButtons = CFArrayGetCount(buttons);
753 device->generic.devcaps.dwPOVs = CFArrayGetCount(povs);
755 TRACE("axes %u povs %u buttons %u\n", device->generic.devcaps.dwAxes, device->generic.devcaps.dwPOVs,
756 device->generic.devcaps.dwButtons);
758 /* build our element array in the order that dinput expects */
759 CFArrayAppendArray(axes, povs, CFRangeMake(0, device->generic.devcaps.dwPOVs));
760 CFArrayAppendArray(axes, buttons, CFRangeMake(0, device->generic.devcaps.dwButtons));
761 device->elements = axes;
762 axes = NULL;
764 CFRelease(povs);
765 CFRelease(buttons);
766 CFRelease(elements);
768 else
770 device->generic.devcaps.dwAxes = 0;
771 device->generic.devcaps.dwButtons = 0;
772 device->generic.devcaps.dwPOVs = 0;
776 static void get_osx_device_elements_props(JoystickImpl *device)
778 TRACE("device %p\n", device);
780 if (device->elements)
782 CFIndex idx, cnt = CFArrayGetCount( device->elements );
784 for ( idx = 0; idx < cnt; idx++ )
786 IOHIDElementRef element = ( IOHIDElementRef ) CFArrayGetValueAtIndex( device->elements, idx );
788 TRACE("element %s\n", debugstr_element(element));
790 device->generic.props[idx].lDevMin = IOHIDElementGetLogicalMin(element);
791 device->generic.props[idx].lDevMax = IOHIDElementGetLogicalMax(element);
792 device->generic.props[idx].lMin = 0;
793 device->generic.props[idx].lMax = 0xffff;
794 device->generic.props[idx].lDeadZone = 0;
795 device->generic.props[idx].lSaturation = 0;
800 static void poll_osx_device_state(LPDIRECTINPUTDEVICE8A iface)
802 JoystickImpl *device = impl_from_IDirectInputDevice8A(iface);
803 IOHIDElementRef device_main_element;
804 IOHIDDeviceRef hid_device;
806 TRACE("device %p device->id %i\n", device, device->id);
808 if (!device_main_elements || device->id >= CFArrayGetCount(device_main_elements))
809 return;
811 device_main_element = (IOHIDElementRef) CFArrayGetValueAtIndex(device_main_elements, device->id);
812 hid_device = IOHIDElementGetDevice(device_main_element);
813 TRACE("main element %s hid_device %s\n", debugstr_element(device_main_element), debugstr_device(hid_device));
814 if (!hid_device)
815 return;
817 if (device->elements)
819 int button_idx = 0;
820 int pov_idx = 0;
821 int slider_idx = 0;
822 int inst_id;
823 CFIndex idx, cnt = CFArrayGetCount( device->elements );
825 for ( idx = 0; idx < cnt; idx++ )
827 IOHIDValueRef valueRef;
828 int val, oldVal, newVal;
829 IOHIDElementRef element = ( IOHIDElementRef ) CFArrayGetValueAtIndex( device->elements, idx );
830 int type = IOHIDElementGetType( element );
832 TRACE("element %s\n", debugstr_element(element));
834 switch(type)
836 case kIOHIDElementTypeInput_Button:
837 TRACE("kIOHIDElementTypeInput_Button\n");
838 if(button_idx < 128)
840 valueRef = NULL;
841 if (IOHIDDeviceGetValue(hid_device, element, &valueRef) != kIOReturnSuccess)
842 return;
843 if (valueRef == NULL)
844 return;
845 val = IOHIDValueGetIntegerValue(valueRef);
846 newVal = val ? 0x80 : 0x0;
847 oldVal = device->generic.js.rgbButtons[button_idx];
848 device->generic.js.rgbButtons[button_idx] = newVal;
849 TRACE("valueRef %s val %d oldVal %d newVal %d\n", debugstr_cf(valueRef), val, oldVal, newVal);
850 if (oldVal != newVal)
852 inst_id = DIDFT_MAKEINSTANCE(button_idx) | DIDFT_PSHBUTTON;
853 queue_event(iface,inst_id,newVal,GetCurrentTime(),device->generic.base.dinput->evsequence++);
855 button_idx ++;
857 break;
858 case kIOHIDElementTypeInput_Misc:
860 uint32_t usage_page = IOHIDElementGetUsagePage( element );
861 uint32_t usage = IOHIDElementGetUsage( element );
862 switch(MAKEUINT64(usage_page, usage))
864 case MAKEUINT64(kHIDPage_GenericDesktop, kHIDUsage_GD_Hatswitch):
866 TRACE("kIOHIDElementTypeInput_Misc / kHIDUsage_GD_Hatswitch\n");
867 valueRef = NULL;
868 if (IOHIDDeviceGetValue(hid_device, element, &valueRef) != kIOReturnSuccess)
869 return;
870 if (valueRef == NULL)
871 return;
872 val = IOHIDValueGetIntegerValue(valueRef);
873 oldVal = device->generic.js.rgdwPOV[pov_idx];
874 if ((val > device->generic.props[idx].lDevMax) || (val < device->generic.props[idx].lDevMin))
875 newVal = -1;
876 else
877 newVal = (val - device->generic.props[idx].lDevMin) * 4500;
878 device->generic.js.rgdwPOV[pov_idx] = newVal;
879 TRACE("valueRef %s val %d oldVal %d newVal %d\n", debugstr_cf(valueRef), val, oldVal, newVal);
880 if (oldVal != newVal)
882 inst_id = DIDFT_MAKEINSTANCE(pov_idx) | DIDFT_POV;
883 queue_event(iface,inst_id,newVal,GetCurrentTime(),device->generic.base.dinput->evsequence++);
885 pov_idx ++;
886 break;
888 case MAKEUINT64(kHIDPage_GenericDesktop, kHIDUsage_GD_X):
889 case MAKEUINT64(kHIDPage_GenericDesktop, kHIDUsage_GD_Y):
890 case MAKEUINT64(kHIDPage_GenericDesktop, kHIDUsage_GD_Z):
891 case MAKEUINT64(kHIDPage_GenericDesktop, kHIDUsage_GD_Rx):
892 case MAKEUINT64(kHIDPage_GenericDesktop, kHIDUsage_GD_Ry):
893 case MAKEUINT64(kHIDPage_GenericDesktop, kHIDUsage_GD_Rz):
894 case MAKEUINT64(kHIDPage_GenericDesktop, kHIDUsage_GD_Slider):
895 case MAKEUINT64(kHIDPage_Simulation, kHIDUsage_Sim_Accelerator):
896 case MAKEUINT64(kHIDPage_Simulation, kHIDUsage_Sim_Brake):
898 int wine_obj = -1;
900 valueRef = NULL;
901 if (IOHIDDeviceGetValue(hid_device, element, &valueRef) != kIOReturnSuccess)
902 return;
903 if (valueRef == NULL)
904 return;
905 val = IOHIDValueGetIntegerValue(valueRef);
906 newVal = joystick_map_axis(&device->generic.props[idx], val);
907 switch (MAKEUINT64(usage_page, usage))
909 case MAKEUINT64(kHIDPage_GenericDesktop, kHIDUsage_GD_X):
910 TRACE("kIOHIDElementTypeInput_Misc / kHIDUsage_GD_X\n");
911 wine_obj = 0;
912 oldVal = device->generic.js.lX;
913 device->generic.js.lX = newVal;
914 break;
915 case MAKEUINT64(kHIDPage_GenericDesktop, kHIDUsage_GD_Y):
916 TRACE("kIOHIDElementTypeInput_Misc / kHIDUsage_GD_Y\n");
917 wine_obj = 1;
918 oldVal = device->generic.js.lY;
919 device->generic.js.lY = newVal;
920 break;
921 case MAKEUINT64(kHIDPage_GenericDesktop, kHIDUsage_GD_Z):
922 TRACE("kIOHIDElementTypeInput_Misc / kHIDUsage_GD_Z\n");
923 wine_obj = 2;
924 oldVal = device->generic.js.lZ;
925 device->generic.js.lZ = newVal;
926 break;
927 case MAKEUINT64(kHIDPage_GenericDesktop, kHIDUsage_GD_Rx):
928 case MAKEUINT64(kHIDPage_Simulation, kHIDUsage_Sim_Accelerator):
929 TRACE("kIOHIDElementTypeInput_Misc / kHIDUsage_GD_Rx\n");
930 wine_obj = 3;
931 oldVal = device->generic.js.lRx;
932 device->generic.js.lRx = newVal;
933 break;
934 case MAKEUINT64(kHIDPage_GenericDesktop, kHIDUsage_GD_Ry):
935 case MAKEUINT64(kHIDPage_Simulation, kHIDUsage_Sim_Brake):
936 TRACE("kIOHIDElementTypeInput_Misc / kHIDUsage_GD_Ry\n");
937 wine_obj = 4;
938 oldVal = device->generic.js.lRy;
939 device->generic.js.lRy = newVal;
940 break;
941 case MAKEUINT64(kHIDPage_GenericDesktop, kHIDUsage_GD_Rz):
942 TRACE("kIOHIDElementTypeInput_Misc / kHIDUsage_GD_Rz\n");
943 wine_obj = 5;
944 oldVal = device->generic.js.lRz;
945 device->generic.js.lRz = newVal;
946 break;
947 case MAKEUINT64(kHIDPage_GenericDesktop, kHIDUsage_GD_Slider):
948 TRACE("kIOHIDElementTypeInput_Misc / kHIDUsage_GD_Slider\n");
949 wine_obj = 6 + slider_idx;
950 oldVal = device->generic.js.rglSlider[slider_idx];
951 device->generic.js.rglSlider[slider_idx] = newVal;
952 slider_idx ++;
953 break;
955 TRACE("valueRef %s val %d oldVal %d newVal %d\n", debugstr_cf(valueRef), val, oldVal, newVal);
956 if ((wine_obj != -1) &&
957 (oldVal != newVal))
959 inst_id = DIDFT_MAKEINSTANCE(wine_obj) | DIDFT_ABSAXIS;
960 queue_event(iface,inst_id,newVal,GetCurrentTime(),device->generic.base.dinput->evsequence++);
963 break;
965 default:
966 FIXME("kIOHIDElementTypeInput_Misc / unhandled usage %i\n", usage);
968 break;
970 default:
971 FIXME("Unhandled type %i\n",type);
977 static INT find_joystick_devices(void)
979 static INT joystick_devices_count = -1;
981 if (joystick_devices_count != -1) return joystick_devices_count;
983 joystick_devices_count = find_osx_devices();
985 return joystick_devices_count;
988 static DWORD make_vid_pid(IOHIDDeviceRef device)
990 long vendID, prodID;
992 vendID = get_device_property_long(device, CFSTR(kIOHIDVendorIDKey));
993 prodID = get_device_property_long(device, CFSTR(kIOHIDProductIDKey));
995 return MAKELONG(vendID, prodID);
998 static HRESULT joydev_enum_deviceA(DWORD dwDevType, DWORD dwFlags, LPDIDEVICEINSTANCEA lpddi, DWORD version, int id)
1000 IOHIDDeviceRef device;
1001 BOOL is_joystick;
1003 TRACE("dwDevType %u dwFlags 0x%08x version 0x%04x id %d\n", dwDevType, dwFlags, version, id);
1005 if (id >= find_joystick_devices()) return E_FAIL;
1007 device = get_device_ref(id);
1009 if ((dwDevType == 0) ||
1010 ((dwDevType == DIDEVTYPE_JOYSTICK) && (version >= 0x0300 && version < 0x0800)) ||
1011 (((dwDevType == DI8DEVCLASS_GAMECTRL) || (dwDevType == DI8DEVTYPE_JOYSTICK)) && (version >= 0x0800)))
1013 if (dwFlags & DIEDFL_FORCEFEEDBACK) {
1014 if(!device)
1015 return S_FALSE;
1016 if(get_ff(device, NULL) != S_OK)
1017 return S_FALSE;
1019 is_joystick = get_device_property_long(device, CFSTR(kIOHIDDeviceUsageKey)) == kHIDUsage_GD_Joystick;
1020 /* Return joystick */
1021 lpddi->guidInstance = DInput_Wine_OsX_Joystick_GUID;
1022 lpddi->guidInstance.Data3 = id;
1023 lpddi->guidProduct = DInput_PIDVID_Product_GUID;
1024 lpddi->guidProduct.Data1 = make_vid_pid(device);
1025 lpddi->dwDevType = get_device_type(version, is_joystick);
1026 lpddi->dwDevType |= DIDEVTYPE_HID;
1027 lpddi->wUsagePage = 0x01; /* Desktop */
1028 if (is_joystick)
1029 lpddi->wUsage = 0x04; /* Joystick */
1030 else
1031 lpddi->wUsage = 0x05; /* Game Pad */
1032 sprintf(lpddi->tszInstanceName, "Joystick %d", id);
1034 /* get the device name */
1035 get_osx_device_name(id, lpddi->tszProductName, MAX_PATH);
1037 lpddi->guidFFDriver = GUID_NULL;
1038 return S_OK;
1041 return S_FALSE;
1044 static HRESULT joydev_enum_deviceW(DWORD dwDevType, DWORD dwFlags, LPDIDEVICEINSTANCEW lpddi, DWORD version, int id)
1046 char name[MAX_PATH];
1047 char friendly[32];
1048 IOHIDDeviceRef device;
1049 BOOL is_joystick;
1051 TRACE("dwDevType %u dwFlags 0x%08x version 0x%04x id %d\n", dwDevType, dwFlags, version, id);
1053 if (id >= find_joystick_devices()) return E_FAIL;
1055 device = get_device_ref(id);
1057 if ((dwDevType == 0) ||
1058 ((dwDevType == DIDEVTYPE_JOYSTICK) && (version >= 0x0300 && version < 0x0800)) ||
1059 (((dwDevType == DI8DEVCLASS_GAMECTRL) || (dwDevType == DI8DEVTYPE_JOYSTICK)) && (version >= 0x0800))) {
1061 if (dwFlags & DIEDFL_FORCEFEEDBACK) {
1062 if(!device)
1063 return S_FALSE;
1064 if(get_ff(device, NULL) != S_OK)
1065 return S_FALSE;
1067 is_joystick = get_device_property_long(device, CFSTR(kIOHIDDeviceUsageKey)) == kHIDUsage_GD_Joystick;
1068 /* Return joystick */
1069 lpddi->guidInstance = DInput_Wine_OsX_Joystick_GUID;
1070 lpddi->guidInstance.Data3 = id;
1071 lpddi->guidProduct = DInput_PIDVID_Product_GUID;
1072 lpddi->guidProduct.Data1 = make_vid_pid(device);
1073 lpddi->dwDevType = get_device_type(version, is_joystick);
1074 lpddi->dwDevType |= DIDEVTYPE_HID;
1075 lpddi->wUsagePage = 0x01; /* Desktop */
1076 if (is_joystick)
1077 lpddi->wUsage = 0x04; /* Joystick */
1078 else
1079 lpddi->wUsage = 0x05; /* Game Pad */
1080 sprintf(friendly, "Joystick %d", id);
1081 MultiByteToWideChar(CP_ACP, 0, friendly, -1, lpddi->tszInstanceName, MAX_PATH);
1083 /* get the device name */
1084 get_osx_device_name(id, name, MAX_PATH);
1086 MultiByteToWideChar(CP_ACP, 0, name, -1, lpddi->tszProductName, MAX_PATH);
1087 lpddi->guidFFDriver = GUID_NULL;
1088 return S_OK;
1091 return S_FALSE;
1094 static const char *osx_ff_axis_name(UInt8 axis)
1096 static char ret[6];
1097 switch(axis){
1098 case FFJOFS_X:
1099 return "FFJOFS_X";
1100 case FFJOFS_Y:
1101 return "FFJOFS_Y";
1102 case FFJOFS_Z:
1103 return "FFJOFS_Z";
1105 sprintf(ret, "%u", (unsigned int)axis);
1106 return ret;
1109 static BOOL osx_axis_has_ff(FFCAPABILITIES *ffcaps, UInt8 axis)
1111 int i;
1112 for(i = 0; i < ffcaps->numFfAxes; ++i)
1113 if(ffcaps->ffAxes[i] == axis)
1114 return TRUE;
1115 return FALSE;
1118 static HRESULT alloc_device(REFGUID rguid, IDirectInputImpl *dinput,
1119 JoystickImpl **pdev, unsigned short index)
1121 DWORD i;
1122 IOHIDDeviceRef device;
1123 JoystickImpl* newDevice;
1124 char name[MAX_PATH];
1125 HRESULT hr;
1126 LPDIDATAFORMAT df = NULL;
1127 int idx = 0;
1128 uint64_t axis_map[8]; /* max axes */
1129 int slider_count = 0;
1130 FFCAPABILITIES ffcaps;
1132 TRACE("%s %p %p %hu\n", debugstr_guid(rguid), dinput, pdev, index);
1134 newDevice = HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(JoystickImpl));
1135 if (newDevice == 0) {
1136 WARN("out of memory\n");
1137 *pdev = 0;
1138 return DIERR_OUTOFMEMORY;
1141 newDevice->id = index;
1143 device = get_device_ref(index);
1145 newDevice->generic.guidInstance = DInput_Wine_OsX_Joystick_GUID;
1146 newDevice->generic.guidInstance.Data3 = index;
1147 newDevice->generic.guidProduct = DInput_PIDVID_Product_GUID;
1148 newDevice->generic.guidProduct.Data1 = make_vid_pid(device);
1149 newDevice->generic.joy_polldev = poll_osx_device_state;
1151 /* get the device name */
1152 get_osx_device_name(index, name, MAX_PATH);
1153 TRACE("Name %s\n",name);
1155 /* copy the device name */
1156 newDevice->generic.name = HeapAlloc(GetProcessHeap(),0,strlen(name) + 1);
1157 strcpy(newDevice->generic.name, name);
1159 list_init(&newDevice->effects);
1160 if(get_ff(device, &newDevice->ff) == S_OK){
1161 newDevice->generic.devcaps.dwFlags |= DIDC_FORCEFEEDBACK;
1163 hr = FFDeviceGetForceFeedbackCapabilities(newDevice->ff, &ffcaps);
1164 if(SUCCEEDED(hr)){
1165 TRACE("FF Capabilities:\n");
1166 TRACE("\tsupportedEffects: 0x%x\n", (unsigned int)ffcaps.supportedEffects);
1167 TRACE("\temulatedEffects: 0x%x\n", (unsigned int)ffcaps.emulatedEffects);
1168 TRACE("\tsubType: 0x%x\n", (unsigned int)ffcaps.subType);
1169 TRACE("\tnumFfAxes: %u\n", (unsigned int)ffcaps.numFfAxes);
1170 TRACE("\tffAxes: [");
1171 for(i = 0; i < ffcaps.numFfAxes; ++i){
1172 TRACE("%s", osx_ff_axis_name(ffcaps.ffAxes[i]));
1173 if(i < ffcaps.numFfAxes - 1)
1174 TRACE(", ");
1176 TRACE("]\n");
1177 TRACE("\tstorageCapacity: %u\n", (unsigned int)ffcaps.storageCapacity);
1178 TRACE("\tplaybackCapacity: %u\n", (unsigned int)ffcaps.playbackCapacity);
1181 hr = FFDeviceSendForceFeedbackCommand(newDevice->ff, FFSFFC_RESET);
1182 if(FAILED(hr))
1183 WARN("FFDeviceSendForceFeedbackCommand(FFSFFC_RESET) failed: %08x\n", hr);
1185 hr = FFDeviceSendForceFeedbackCommand(newDevice->ff, FFSFFC_SETACTUATORSON);
1186 if(FAILED(hr))
1187 WARN("FFDeviceSendForceFeedbackCommand(FFSFFC_SETACTUATORSON) failed: %08x\n", hr);
1190 memset(axis_map, 0, sizeof(axis_map));
1191 get_osx_device_elements(newDevice, axis_map);
1193 TRACE("%i axes %i buttons %i povs\n",newDevice->generic.devcaps.dwAxes,newDevice->generic.devcaps.dwButtons,newDevice->generic.devcaps.dwPOVs);
1195 if (newDevice->generic.devcaps.dwButtons > 128)
1197 WARN("Can't support %d buttons. Clamping down to 128\n", newDevice->generic.devcaps.dwButtons);
1198 newDevice->generic.devcaps.dwButtons = 128;
1201 newDevice->generic.base.IDirectInputDevice8A_iface.lpVtbl = &JoystickAvt;
1202 newDevice->generic.base.IDirectInputDevice8W_iface.lpVtbl = &JoystickWvt;
1203 newDevice->generic.base.ref = 1;
1204 newDevice->generic.base.dinput = dinput;
1205 newDevice->generic.base.guid = *rguid;
1206 InitializeCriticalSection(&newDevice->generic.base.crit);
1207 newDevice->generic.base.crit.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": JoystickImpl*->generic.base.crit");
1209 /* Create copy of default data format */
1210 if (!(df = HeapAlloc(GetProcessHeap(), 0, c_dfDIJoystick2.dwSize))) goto FAILED;
1211 memcpy(df, &c_dfDIJoystick2, c_dfDIJoystick2.dwSize);
1213 df->dwNumObjs = newDevice->generic.devcaps.dwAxes + newDevice->generic.devcaps.dwPOVs + newDevice->generic.devcaps.dwButtons;
1214 if (!(df->rgodf = HeapAlloc(GetProcessHeap(), 0, df->dwNumObjs * df->dwObjSize))) goto FAILED;
1216 for (i = 0; i < newDevice->generic.devcaps.dwAxes; i++)
1218 int wine_obj = -1;
1219 BOOL has_ff = FALSE;
1220 switch (axis_map[i])
1222 case MAKEUINT64(kHIDPage_GenericDesktop, kHIDUsage_GD_X):
1223 wine_obj = 0;
1224 has_ff = (newDevice->ff != 0) && osx_axis_has_ff(&ffcaps, FFJOFS_X);
1225 break;
1226 case MAKEUINT64(kHIDPage_GenericDesktop, kHIDUsage_GD_Y):
1227 wine_obj = 1;
1228 has_ff = (newDevice->ff != 0) && osx_axis_has_ff(&ffcaps, FFJOFS_Y);
1229 break;
1230 case MAKEUINT64(kHIDPage_GenericDesktop, kHIDUsage_GD_Z):
1231 wine_obj = 2;
1232 has_ff = (newDevice->ff != 0) && osx_axis_has_ff(&ffcaps, FFJOFS_Z);
1233 break;
1234 case MAKEUINT64(kHIDPage_GenericDesktop, kHIDUsage_GD_Rx):
1235 case MAKEUINT64(kHIDPage_Simulation, kHIDUsage_Sim_Accelerator):
1236 wine_obj = 3;
1237 has_ff = (newDevice->ff != 0) && osx_axis_has_ff(&ffcaps, FFJOFS_RX);
1238 break;
1239 case MAKEUINT64(kHIDPage_GenericDesktop, kHIDUsage_GD_Ry):
1240 case MAKEUINT64(kHIDPage_Simulation, kHIDUsage_Sim_Brake):
1241 wine_obj = 4;
1242 has_ff = (newDevice->ff != 0) && osx_axis_has_ff(&ffcaps, FFJOFS_RY);
1243 break;
1244 case MAKEUINT64(kHIDPage_GenericDesktop, kHIDUsage_GD_Rz):
1245 wine_obj = 5;
1246 has_ff = (newDevice->ff != 0) && osx_axis_has_ff(&ffcaps, FFJOFS_RZ);
1247 break;
1248 case MAKEUINT64(kHIDPage_GenericDesktop, kHIDUsage_GD_Slider):
1249 wine_obj = 6 + slider_count;
1250 has_ff = (newDevice->ff != 0) && osx_axis_has_ff(&ffcaps, FFJOFS_SLIDER(slider_count));
1251 slider_count++;
1252 break;
1254 if (wine_obj < 0 ) continue;
1256 memcpy(&df->rgodf[idx], &c_dfDIJoystick2.rgodf[wine_obj], df->dwObjSize);
1257 df->rgodf[idx].dwType = DIDFT_MAKEINSTANCE(wine_obj) | DIDFT_ABSAXIS;
1258 if(has_ff)
1259 df->rgodf[idx].dwFlags |= DIDOI_FFACTUATOR;
1260 ++idx;
1263 for (i = 0; i < newDevice->generic.devcaps.dwPOVs; i++)
1265 memcpy(&df->rgodf[idx], &c_dfDIJoystick2.rgodf[i + 8], df->dwObjSize);
1266 df->rgodf[idx++].dwType = DIDFT_MAKEINSTANCE(i) | DIDFT_POV;
1269 for (i = 0; i < newDevice->generic.devcaps.dwButtons; i++)
1271 memcpy(&df->rgodf[idx], &c_dfDIJoystick2.rgodf[i + 12], df->dwObjSize);
1272 df->rgodf[idx ].pguid = &GUID_Button;
1273 df->rgodf[idx++].dwType = DIDFT_MAKEINSTANCE(i) | DIDFT_PSHBUTTON;
1275 newDevice->generic.base.data_format.wine_df = df;
1277 /* initialize default properties */
1278 get_osx_device_elements_props(newDevice);
1280 IDirectInput_AddRef(&newDevice->generic.base.dinput->IDirectInput7A_iface);
1282 newDevice->generic.devcaps.dwSize = sizeof(newDevice->generic.devcaps);
1283 newDevice->generic.devcaps.dwFlags |= DIDC_ATTACHED;
1284 if (newDevice->generic.base.dinput->dwVersion >= 0x0800)
1285 newDevice->generic.devcaps.dwDevType = DI8DEVTYPE_JOYSTICK | (DI8DEVTYPEJOYSTICK_STANDARD << 8);
1286 else
1287 newDevice->generic.devcaps.dwDevType = DIDEVTYPE_JOYSTICK | (DIDEVTYPEJOYSTICK_TRADITIONAL << 8);
1288 newDevice->generic.devcaps.dwFFSamplePeriod = 0;
1289 newDevice->generic.devcaps.dwFFMinTimeResolution = 0;
1290 newDevice->generic.devcaps.dwFirmwareRevision = 0;
1291 newDevice->generic.devcaps.dwHardwareRevision = 0;
1292 newDevice->generic.devcaps.dwFFDriverVersion = 0;
1294 if (TRACE_ON(dinput)) {
1295 TRACE("allocated device %p\n", newDevice);
1296 _dump_DIDATAFORMAT(newDevice->generic.base.data_format.wine_df);
1297 _dump_DIDEVCAPS(&newDevice->generic.devcaps);
1300 *pdev = newDevice;
1302 return DI_OK;
1304 FAILED:
1305 hr = DIERR_OUTOFMEMORY;
1306 if (newDevice->ff) FFReleaseDevice(newDevice->ff);
1307 if (newDevice->elements) CFRelease(newDevice->elements);
1308 if (df) HeapFree(GetProcessHeap(), 0, df->rgodf);
1309 HeapFree(GetProcessHeap(), 0, df);
1310 release_DataFormat(&newDevice->generic.base.data_format);
1311 HeapFree(GetProcessHeap(),0,newDevice->generic.name);
1312 HeapFree(GetProcessHeap(),0,newDevice);
1313 *pdev = 0;
1315 return hr;
1318 /******************************************************************************
1319 * get_joystick_index : Get the joystick index from a given GUID
1321 static unsigned short get_joystick_index(REFGUID guid)
1323 GUID wine_joystick = DInput_Wine_OsX_Joystick_GUID;
1324 GUID dev_guid = *guid;
1326 wine_joystick.Data3 = 0;
1327 dev_guid.Data3 = 0;
1329 /* for the standard joystick GUID use index 0 */
1330 if(IsEqualGUID(&GUID_Joystick,guid)) return 0;
1332 /* for the wine joystick GUIDs use the index stored in Data3 */
1333 if(IsEqualGUID(&wine_joystick, &dev_guid)) return guid->Data3;
1335 return 0xffff;
1338 static HRESULT joydev_create_device(IDirectInputImpl *dinput, REFGUID rguid, REFIID riid, LPVOID *pdev, int unicode)
1340 unsigned short index;
1341 int joystick_devices_count;
1343 TRACE("%p %s %s %p %i\n", dinput, debugstr_guid(rguid), debugstr_guid(riid), pdev, unicode);
1344 *pdev = NULL;
1346 if ((joystick_devices_count = find_joystick_devices()) == 0)
1347 return DIERR_DEVICENOTREG;
1349 if ((index = get_joystick_index(rguid)) < 0xffff &&
1350 joystick_devices_count && index < joystick_devices_count)
1352 JoystickImpl *This;
1353 HRESULT hr;
1355 if (riid == NULL)
1356 ;/* nothing */
1357 else if (IsEqualGUID(&IID_IDirectInputDeviceA, riid) ||
1358 IsEqualGUID(&IID_IDirectInputDevice2A, riid) ||
1359 IsEqualGUID(&IID_IDirectInputDevice7A, riid) ||
1360 IsEqualGUID(&IID_IDirectInputDevice8A, riid))
1362 unicode = 0;
1364 else if (IsEqualGUID(&IID_IDirectInputDeviceW, riid) ||
1365 IsEqualGUID(&IID_IDirectInputDevice2W, riid) ||
1366 IsEqualGUID(&IID_IDirectInputDevice7W, riid) ||
1367 IsEqualGUID(&IID_IDirectInputDevice8W, riid))
1369 unicode = 1;
1371 else
1373 WARN("no interface\n");
1374 return DIERR_NOINTERFACE;
1377 hr = alloc_device(rguid, dinput, &This, index);
1378 if (!This) return hr;
1380 if (unicode)
1381 *pdev = &This->generic.base.IDirectInputDevice8W_iface;
1382 else
1383 *pdev = &This->generic.base.IDirectInputDevice8A_iface;
1384 return hr;
1387 return DIERR_DEVICENOTREG;
1390 static HRESULT WINAPI JoystickWImpl_GetProperty(LPDIRECTINPUTDEVICE8W iface, REFGUID rguid, LPDIPROPHEADER pdiph)
1392 JoystickImpl *This = impl_from_IDirectInputDevice8W(iface);
1394 TRACE("(%p)->(%s,%p)\n", This, debugstr_guid(rguid), pdiph);
1395 _dump_DIPROPHEADER(pdiph);
1397 if (!IS_DIPROP(rguid)) return DI_OK;
1399 switch (LOWORD(rguid)) {
1400 case (DWORD_PTR) DIPROP_GUIDANDPATH:
1402 static const WCHAR formatW[] = {'\\','\\','?','\\','h','i','d','#','v','i','d','_','%','0','4','x','&',
1403 'p','i','d','_','%','0','4','x','&','%','s','_','%','i',0};
1404 static const WCHAR miW[] = {'m','i',0};
1405 static const WCHAR igW[] = {'i','g',0};
1407 BOOL is_gamepad;
1408 IOHIDDeviceRef device = get_device_ref(This->id);
1409 LPDIPROPGUIDANDPATH pd = (LPDIPROPGUIDANDPATH)pdiph;
1410 WORD vid = get_device_property_long(device, CFSTR(kIOHIDVendorIDKey));
1411 WORD pid = get_device_property_long(device, CFSTR(kIOHIDProductIDKey));
1413 if (!pid || !vid)
1414 return DIERR_UNSUPPORTED;
1416 is_gamepad = is_xinput_device(&This->generic.devcaps, vid, pid);
1417 pd->guidClass = GUID_DEVCLASS_HIDCLASS;
1418 sprintfW(pd->wszPath, formatW, vid, pid, is_gamepad ? igW : miW, This->id);
1420 TRACE("DIPROP_GUIDANDPATH(%s, %s): returning fake path\n", debugstr_guid(&pd->guidClass), debugstr_w(pd->wszPath));
1421 break;
1424 default:
1425 return JoystickWGenericImpl_GetProperty(iface, rguid, pdiph);
1428 return DI_OK;
1431 static HRESULT WINAPI JoystickAImpl_GetProperty(LPDIRECTINPUTDEVICE8A iface, REFGUID rguid, LPDIPROPHEADER pdiph)
1433 JoystickImpl *This = impl_from_IDirectInputDevice8A(iface);
1434 return JoystickWImpl_GetProperty(IDirectInputDevice8W_from_impl(This), rguid, pdiph);
1437 static HRESULT osx_set_autocenter(JoystickImpl *This,
1438 const DIPROPDWORD *header)
1440 UInt32 v;
1441 HRESULT hr;
1442 if(!This->ff)
1443 return DIERR_UNSUPPORTED;
1444 v = header->dwData;
1445 hr = osx_to_win32_hresult(FFDeviceSetForceFeedbackProperty(This->ff, FFPROP_AUTOCENTER, &v));
1446 TRACE("returning: %08x\n", hr);
1447 return hr;
1450 static HRESULT osx_set_ffgain(JoystickImpl *This, const DIPROPDWORD *header)
1452 UInt32 v;
1453 HRESULT hr;
1454 if(!This->ff)
1455 return DIERR_UNSUPPORTED;
1456 v = header->dwData;
1457 hr = osx_to_win32_hresult(FFDeviceSetForceFeedbackProperty(This->ff, FFPROP_FFGAIN, &v));
1458 TRACE("returning: %08x\n", hr);
1459 return hr;
1462 static HRESULT WINAPI JoystickWImpl_SetProperty(IDirectInputDevice8W *iface,
1463 const GUID *prop, const DIPROPHEADER *header)
1465 JoystickImpl *This = impl_from_IDirectInputDevice8W(iface);
1467 TRACE("%p %s %p\n", This, debugstr_guid(prop), header);
1469 switch(LOWORD(prop))
1471 case (DWORD_PTR)DIPROP_AUTOCENTER:
1472 return osx_set_autocenter(This, (const DIPROPDWORD *)header);
1473 case (DWORD_PTR)DIPROP_FFGAIN:
1474 return osx_set_ffgain(This, (const DIPROPDWORD *)header);
1477 return JoystickWGenericImpl_SetProperty(iface, prop, header);
1480 static HRESULT WINAPI JoystickAImpl_SetProperty(IDirectInputDevice8A *iface,
1481 const GUID *prop, const DIPROPHEADER *header)
1483 JoystickImpl *This = impl_from_IDirectInputDevice8A(iface);
1485 TRACE("%p %s %p\n", This, debugstr_guid(prop), header);
1487 switch(LOWORD(prop))
1489 case (DWORD_PTR)DIPROP_AUTOCENTER:
1490 return osx_set_autocenter(This, (const DIPROPDWORD *)header);
1491 case (DWORD_PTR)DIPROP_FFGAIN:
1492 return osx_set_ffgain(This, (const DIPROPDWORD *)header);
1495 return JoystickAGenericImpl_SetProperty(iface, prop, header);
1498 static CFUUIDRef effect_win_to_mac(const GUID *effect)
1500 #define DO_MAP(X) \
1501 if(IsEqualGUID(&GUID_##X, effect)) \
1502 return kFFEffectType_##X##_ID;
1503 DO_MAP(ConstantForce)
1504 DO_MAP(RampForce)
1505 DO_MAP(Square)
1506 DO_MAP(Sine)
1507 DO_MAP(Triangle)
1508 DO_MAP(SawtoothUp)
1509 DO_MAP(SawtoothDown)
1510 DO_MAP(Spring)
1511 DO_MAP(Damper)
1512 DO_MAP(Inertia)
1513 DO_MAP(Friction)
1514 DO_MAP(CustomForce)
1515 #undef DO_MAP
1516 WARN("Unknown effect GUID! %s\n", debugstr_guid(effect));
1517 return 0;
1520 static HRESULT WINAPI JoystickWImpl_CreateEffect(IDirectInputDevice8W *iface,
1521 const GUID *type, const DIEFFECT *params, IDirectInputEffect **out,
1522 IUnknown *outer)
1524 JoystickImpl *This = impl_from_IDirectInputDevice8W(iface);
1525 EffectImpl *effect;
1526 HRESULT hr;
1528 TRACE("(%p)->(%s %p %p %p)\n", This, debugstr_guid(type), params, out, outer);
1529 dump_DIEFFECT(params, type, 0);
1531 if(!This->ff){
1532 TRACE("No force feedback support\n");
1533 *out = NULL;
1534 return DIERR_UNSUPPORTED;
1537 if(outer)
1538 WARN("aggregation not implemented\n");
1540 effect = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*This));
1541 effect->IDirectInputEffect_iface.lpVtbl = &EffectVtbl;
1542 effect->ref = 1;
1543 effect->guid = *type;
1544 effect->device = This;
1546 /* Mac's FFEFFECT and Win's DIEFFECT are binary identical. */
1547 hr = osx_to_win32_hresult(FFDeviceCreateEffect(This->ff,
1548 effect_win_to_mac(type), (FFEFFECT*)params, &effect->effect));
1549 if(FAILED(hr)){
1550 WARN("FFDeviceCreateEffect failed: %08x\n", hr);
1551 HeapFree(GetProcessHeap(), 0, effect);
1552 return hr;
1555 list_add_tail(&This->effects, &effect->entry);
1556 *out = &effect->IDirectInputEffect_iface;
1558 TRACE("allocated effect: %p\n", effect);
1560 return S_OK;
1563 static HRESULT WINAPI JoystickAImpl_CreateEffect(IDirectInputDevice8A *iface,
1564 const GUID *type, const DIEFFECT *params, IDirectInputEffect **out,
1565 IUnknown *outer)
1567 JoystickImpl *This = impl_from_IDirectInputDevice8A(iface);
1569 TRACE("(%p)->(%s %p %p %p)\n", This, debugstr_guid(type), params, out, outer);
1571 return JoystickWImpl_CreateEffect(&This->generic.base.IDirectInputDevice8W_iface,
1572 type, params, out, outer);
1575 static HRESULT WINAPI JoystickWImpl_SendForceFeedbackCommand(IDirectInputDevice8W *iface,
1576 DWORD flags)
1578 JoystickImpl *This = impl_from_IDirectInputDevice8W(iface);
1579 HRESULT hr;
1581 TRACE("%p 0x%x\n", This, flags);
1583 if(!This->ff)
1584 return DI_NOEFFECT;
1586 hr = osx_to_win32_hresult(FFDeviceSendForceFeedbackCommand(This->ff, flags));
1587 if(FAILED(hr)){
1588 WARN("FFDeviceSendForceFeedbackCommand failed: %08x\n", hr);
1589 return hr;
1592 return S_OK;
1595 static HRESULT WINAPI JoystickAImpl_SendForceFeedbackCommand(IDirectInputDevice8A *iface,
1596 DWORD flags)
1598 JoystickImpl *This = impl_from_IDirectInputDevice8A(iface);
1600 TRACE("%p 0x%x\n", This, flags);
1602 return JoystickWImpl_SendForceFeedbackCommand(&This->generic.base.IDirectInputDevice8W_iface, flags);
1605 const struct dinput_device joystick_osx_device = {
1606 "Wine OS X joystick driver",
1607 joydev_enum_deviceA,
1608 joydev_enum_deviceW,
1609 joydev_create_device
1612 static const IDirectInputDevice8AVtbl JoystickAvt =
1614 IDirectInputDevice2AImpl_QueryInterface,
1615 IDirectInputDevice2AImpl_AddRef,
1616 IDirectInputDevice2AImpl_Release,
1617 JoystickAGenericImpl_GetCapabilities,
1618 IDirectInputDevice2AImpl_EnumObjects,
1619 JoystickAImpl_GetProperty,
1620 JoystickAImpl_SetProperty,
1621 IDirectInputDevice2AImpl_Acquire,
1622 IDirectInputDevice2AImpl_Unacquire,
1623 JoystickAGenericImpl_GetDeviceState,
1624 IDirectInputDevice2AImpl_GetDeviceData,
1625 IDirectInputDevice2AImpl_SetDataFormat,
1626 IDirectInputDevice2AImpl_SetEventNotification,
1627 IDirectInputDevice2AImpl_SetCooperativeLevel,
1628 JoystickAGenericImpl_GetObjectInfo,
1629 JoystickAGenericImpl_GetDeviceInfo,
1630 IDirectInputDevice2AImpl_RunControlPanel,
1631 IDirectInputDevice2AImpl_Initialize,
1632 JoystickAImpl_CreateEffect,
1633 IDirectInputDevice2AImpl_EnumEffects,
1634 IDirectInputDevice2AImpl_GetEffectInfo,
1635 IDirectInputDevice2AImpl_GetForceFeedbackState,
1636 JoystickAImpl_SendForceFeedbackCommand,
1637 IDirectInputDevice2AImpl_EnumCreatedEffectObjects,
1638 IDirectInputDevice2AImpl_Escape,
1639 JoystickAGenericImpl_Poll,
1640 IDirectInputDevice2AImpl_SendDeviceData,
1641 IDirectInputDevice7AImpl_EnumEffectsInFile,
1642 IDirectInputDevice7AImpl_WriteEffectToFile,
1643 JoystickAGenericImpl_BuildActionMap,
1644 JoystickAGenericImpl_SetActionMap,
1645 IDirectInputDevice8AImpl_GetImageInfo
1648 static const IDirectInputDevice8WVtbl JoystickWvt =
1650 IDirectInputDevice2WImpl_QueryInterface,
1651 IDirectInputDevice2WImpl_AddRef,
1652 IDirectInputDevice2WImpl_Release,
1653 JoystickWGenericImpl_GetCapabilities,
1654 IDirectInputDevice2WImpl_EnumObjects,
1655 JoystickWImpl_GetProperty,
1656 JoystickWImpl_SetProperty,
1657 IDirectInputDevice2WImpl_Acquire,
1658 IDirectInputDevice2WImpl_Unacquire,
1659 JoystickWGenericImpl_GetDeviceState,
1660 IDirectInputDevice2WImpl_GetDeviceData,
1661 IDirectInputDevice2WImpl_SetDataFormat,
1662 IDirectInputDevice2WImpl_SetEventNotification,
1663 IDirectInputDevice2WImpl_SetCooperativeLevel,
1664 JoystickWGenericImpl_GetObjectInfo,
1665 JoystickWGenericImpl_GetDeviceInfo,
1666 IDirectInputDevice2WImpl_RunControlPanel,
1667 IDirectInputDevice2WImpl_Initialize,
1668 JoystickWImpl_CreateEffect,
1669 IDirectInputDevice2WImpl_EnumEffects,
1670 IDirectInputDevice2WImpl_GetEffectInfo,
1671 IDirectInputDevice2WImpl_GetForceFeedbackState,
1672 JoystickWImpl_SendForceFeedbackCommand,
1673 IDirectInputDevice2WImpl_EnumCreatedEffectObjects,
1674 IDirectInputDevice2WImpl_Escape,
1675 JoystickWGenericImpl_Poll,
1676 IDirectInputDevice2WImpl_SendDeviceData,
1677 IDirectInputDevice7WImpl_EnumEffectsInFile,
1678 IDirectInputDevice7WImpl_WriteEffectToFile,
1679 JoystickWGenericImpl_BuildActionMap,
1680 JoystickWGenericImpl_SetActionMap,
1681 IDirectInputDevice8WImpl_GetImageInfo
1684 static HRESULT WINAPI effect_QueryInterface(IDirectInputEffect *iface,
1685 const GUID *guid, void **out)
1687 EffectImpl *This = impl_from_IDirectInputEffect(iface);
1689 TRACE("%p %s %p\n", This, debugstr_guid(guid), out);
1691 if(IsEqualIID(guid, &IID_IUnknown) || IsEqualIID(guid, &IID_IDirectInputEffect)){
1692 *out = iface;
1693 IDirectInputEffect_AddRef(iface);
1694 return S_OK;
1697 return E_NOINTERFACE;
1700 static ULONG WINAPI effect_AddRef(IDirectInputEffect *iface)
1702 EffectImpl *This = impl_from_IDirectInputEffect(iface);
1703 ULONG ref = InterlockedIncrement(&This->ref);
1704 TRACE("%p, ref is now: %u\n", This, ref);
1705 return ref;
1708 static ULONG WINAPI effect_Release(IDirectInputEffect *iface)
1710 EffectImpl *This = impl_from_IDirectInputEffect(iface);
1711 ULONG ref = InterlockedDecrement(&This->ref);
1712 TRACE("%p, ref is now: %u\n", This, ref);
1714 if(!ref){
1715 list_remove(&This->entry);
1716 FFDeviceReleaseEffect(This->device->ff, This->effect);
1717 HeapFree(GetProcessHeap(), 0, This);
1720 return ref;
1723 static HRESULT WINAPI effect_Initialize(IDirectInputEffect *iface, HINSTANCE hinst,
1724 DWORD version, const GUID *guid)
1726 EffectImpl *This = impl_from_IDirectInputEffect(iface);
1727 TRACE("%p %p 0x%x, %s\n", This, hinst, version, debugstr_guid(guid));
1728 return S_OK;
1731 static HRESULT WINAPI effect_GetEffectGuid(IDirectInputEffect *iface, GUID *out)
1733 EffectImpl *This = impl_from_IDirectInputEffect(iface);
1734 TRACE("%p %p\n", This, out);
1735 *out = This->guid;
1736 return S_OK;
1739 static HRESULT WINAPI effect_GetParameters(IDirectInputEffect *iface,
1740 DIEFFECT *effect, DWORD flags)
1742 EffectImpl *This = impl_from_IDirectInputEffect(iface);
1743 TRACE("%p %p 0x%x\n", This, effect, flags);
1744 return osx_to_win32_hresult(FFEffectGetParameters(This->effect, (FFEFFECT*)effect, flags));
1747 static HRESULT WINAPI effect_SetParameters(IDirectInputEffect *iface,
1748 const DIEFFECT *effect, DWORD flags)
1750 EffectImpl *This = impl_from_IDirectInputEffect(iface);
1751 TRACE("%p %p 0x%x\n", This, effect, flags);
1752 dump_DIEFFECT(effect, &This->guid, flags);
1753 return osx_to_win32_hresult(FFEffectSetParameters(This->effect, (FFEFFECT*)effect, flags));
1756 static HRESULT WINAPI effect_Start(IDirectInputEffect *iface, DWORD iterations,
1757 DWORD flags)
1759 EffectImpl *This = impl_from_IDirectInputEffect(iface);
1760 TRACE("%p 0x%x 0x%x\n", This, iterations, flags);
1761 return osx_to_win32_hresult(FFEffectStart(This->effect, iterations, flags));
1764 static HRESULT WINAPI effect_Stop(IDirectInputEffect *iface)
1766 EffectImpl *This = impl_from_IDirectInputEffect(iface);
1767 TRACE("%p\n", This);
1768 return osx_to_win32_hresult(FFEffectStop(This->effect));
1771 static HRESULT WINAPI effect_GetEffectStatus(IDirectInputEffect *iface, DWORD *flags)
1773 EffectImpl *This = impl_from_IDirectInputEffect(iface);
1774 TRACE("%p %p\n", This, flags);
1775 return osx_to_win32_hresult(FFEffectGetEffectStatus(This->effect, (UInt32*)flags));
1778 static HRESULT WINAPI effect_Download(IDirectInputEffect *iface)
1780 EffectImpl *This = impl_from_IDirectInputEffect(iface);
1781 TRACE("%p\n", This);
1782 return osx_to_win32_hresult(FFEffectDownload(This->effect));
1785 static HRESULT WINAPI effect_Unload(IDirectInputEffect *iface)
1787 EffectImpl *This = impl_from_IDirectInputEffect(iface);
1788 TRACE("%p\n", This);
1789 return osx_to_win32_hresult(FFEffectUnload(This->effect));
1792 static HRESULT WINAPI effect_Escape(IDirectInputEffect *iface, DIEFFESCAPE *escape)
1794 EffectImpl *This = impl_from_IDirectInputEffect(iface);
1795 TRACE("%p %p\n", This, escape);
1796 return osx_to_win32_hresult(FFEffectEscape(This->effect, (FFEFFESCAPE*)escape));
1799 static const IDirectInputEffectVtbl EffectVtbl = {
1800 effect_QueryInterface,
1801 effect_AddRef,
1802 effect_Release,
1803 effect_Initialize,
1804 effect_GetEffectGuid,
1805 effect_GetParameters,
1806 effect_SetParameters,
1807 effect_Start,
1808 effect_Stop,
1809 effect_GetEffectStatus,
1810 effect_Download,
1811 effect_Unload,
1812 effect_Escape
1815 #else /* HAVE_IOHIDMANAGERCREATE */
1817 const struct dinput_device joystick_osx_device = {
1818 "Wine OS X joystick driver",
1819 NULL,
1820 NULL,
1821 NULL
1824 #endif /* HAVE_IOHIDMANAGERCREATE */