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
24 #include "wine/port.h"
26 #if defined(HAVE_IOKIT_HID_IOHIDLIB_H)
28 #define LPDWORD UInt32*
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>
68 #undef HRESULT_FACILITY
74 #undef STDMETHODCALLTYPE
80 #endif /* HAVE_IOKIT_HID_IOHIDLIB_H */
82 #include "wine/debug.h"
83 #include "wine/unicode.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
;
109 struct JoystickGenericImpl generic
;
115 FFDeviceObjectReference ff
;
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
;
139 JoystickImpl
*device
;
140 FFEffectObjectReference effect
;
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 */
165 return E_OUTOFMEMORY
;
169 return E_NOINTERFACE
;
179 return E_ACCESSDENIED
;
186 static long get_device_property_long(IOHIDDeviceRef device
, CFStringRef key
)
193 assert(IOHIDDeviceGetTypeID() == CFGetTypeID(device
));
195 ref
= IOHIDDeviceGetProperty(device
, key
);
197 if (ref
&& CFNumberGetTypeID() == CFGetTypeID(ref
))
198 CFNumberGetValue((CFNumberRef
)ref
, kCFNumberLongType
, &result
);
204 static CFStringRef
copy_device_name(IOHIDDeviceRef device
)
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
);
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
);
229 ERR("NULL device\n");
230 name
= CFStringCreateCopy(kCFAllocatorDefault
, CFSTR(""));
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
));
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
);
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
)
273 if (!t
) return "(null)";
275 if (CFGetTypeID(t
) == CFStringGetTypeID())
278 s
= CFCopyDescription(t
);
279 ret
= CFStringGetCStringPtr(s
, kCFStringEncodingUTF8
);
280 if (ret
) ret
= debugstr_a(ret
);
283 const UniChar
* u
= CFStringGetCharactersPtr(s
);
285 ret
= debugstr_wn((const WCHAR
*)u
, CFStringGetLength(s
));
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
);
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
))
322 device_main_element
= (IOHIDElementRef
)CFArrayGetValueAtIndex(device_main_elements
, id
);
323 if (!device_main_element
)
325 ERR("Invalid Element requested %i\n",id
);
329 hid_device
= IOHIDElementGetDevice(device_main_element
);
332 ERR("Invalid Device requested %i\n",id
);
336 TRACE("-> %s\n", debugstr_device(hid_device
));
340 static HRESULT
get_ff(IOHIDDeviceRef device
, FFDeviceObjectReference
*ret
)
342 io_service_t service
;
343 CFMutableDictionaryRef matching
;
344 CFTypeRef location_id
;
347 TRACE("device %s\n", debugstr_device(device
));
349 matching
= IOServiceMatching(kIOHIDDeviceKey
);
351 WARN("IOServiceMatching failed, force feedback disabled\n");
352 return DIERR_DEVICENOTREG
;
355 location_id
= IOHIDDeviceGetProperty(device
, CFSTR(kIOHIDLocationIDKey
));
358 WARN("IOHIDDeviceGetProperty failed, force feedback disabled\n");
359 return DIERR_DEVICENOTREG
;
362 CFDictionaryAddValue(matching
, CFSTR(kIOHIDLocationIDKey
), location_id
);
364 service
= IOServiceGetMatchingService(kIOMasterPortDefault
, matching
);
367 hr
= osx_to_win32_hresult(FFCreateDevice(service
, ret
));
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
);
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
);
387 int number
= kHIDPage_GenericDesktop
;
388 CFNumberRef page
= CFNumberCreate( kCFAllocatorDefault
,
389 kCFNumberIntType
, &number
);
393 CFNumberRef cf_usage
;
395 CFDictionarySetValue( result
, CFSTR( kIOHIDDeviceUsagePageKey
), page
);
398 cf_usage
= CFNumberCreate( kCFAllocatorDefault
,
399 kCFNumberIntType
, &usage
);
402 CFDictionarySetValue( result
, CFSTR( kIOHIDDeviceUsageKey
), cf_usage
);
403 CFRelease( cf_usage
);
407 ERR("CFNumberCreate() failed.\n");
414 ERR("CFNumberCreate failed.\n");
421 ERR("CFDictionaryCreateMutable failed.\n");
428 static CFIndex
find_top_level(IOHIDDeviceRef hid_device
, CFMutableArrayRef main_elements
)
433 TRACE("hid_device %s\n", debugstr_device(hid_device
));
438 elements
= IOHIDDeviceCopyMatchingElements(hid_device
, NULL
, 0);
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
);
467 TRACE("-> total %d\n", (int)total
);
471 static void get_element_children(IOHIDElementRef element
, CFMutableArrayRef all_children
)
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
);
490 CFArrayAppendValue(all_children
, child
);
494 static int find_osx_devices(void)
496 IOHIDManagerRef hid_manager
;
497 CFMutableDictionaryRef result
;
499 CFMutableArrayRef matching
;
503 hid_manager
= IOHIDManagerCreate( kCFAllocatorDefault
, 0L );
504 if (IOHIDManagerOpen( hid_manager
, 0 ) != kIOReturnSuccess
)
506 ERR("Couldn't open IOHIDManager.\n");
507 CFRelease( hid_manager
);
511 matching
= CFArrayCreateMutable( kCFAllocatorDefault
, 0,
512 &kCFTypeArrayCallBacks
);
514 /* build matching dictionary */
515 result
= create_osx_device_match(kHIDUsage_GD_Joystick
);
521 CFArrayAppendValue( matching
, result
);
523 result
= create_osx_device_match(kHIDUsage_GD_GamePad
);
529 CFArrayAppendValue( matching
, result
);
532 IOHIDManagerSetDeviceMatchingMultiple( hid_manager
, matching
);
533 CFRelease( matching
);
534 devset
= IOHIDManagerCopyDevices( hid_manager
);
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
);
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
);
553 num_main_elements
= 0;
554 for (idx
= 0; idx
< num_devices
; idx
++)
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
;
567 TRACE("found %i device(s), %i collection(s)\n",(int)num_devices
,(int)num_main_elements
);
568 return (int)num_main_elements
;
572 IOHIDManagerClose( hid_manager
, 0 );
573 CFRelease( hid_manager
);
577 static int get_osx_device_name(int id
, char *name
, int length
)
580 IOHIDDeviceRef hid_device
;
582 hid_device
= get_device_ref(id
);
584 TRACE("id %d hid_device %s\n", id
, debugstr_device(hid_device
));
592 str
= IOHIDDeviceGetProperty(hid_device
, CFSTR( kIOHIDProductKey
));
595 CFIndex len
= CFStringGetLength(str
);
598 CFStringGetCString(str
,name
,length
,kCFStringEncodingASCII
);
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
);
613 return kCFCompareLessThan
;
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
;
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
))
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
)
638 elements
= CFArrayCreateMutable(kCFAllocatorDefault
, 0, &kCFTypeArrayCallBacks
);
639 get_element_children(device_main_element
, 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) */
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 */
690 if (CFArrayGetCount(buttons
) < 128)
691 CFArrayAppendValue(buttons
, element
);
694 case kIOHIDElementTypeInput_Axis
:
696 TRACE("kIOHIDElementTypeInput_Axis\n");
697 CFArrayAppendValue(axes
, element
);
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
);
711 case MAKEUINT64(kHIDPage_GenericDesktop
, kHIDUsage_GD_Slider
):
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");
733 TRACE("kIOHIDElementTypeInput_Misc / kHIDUsage_GD_* (%d)\n", usage
);
734 axis_map
[CFArrayGetCount(axes
)]=MAKEUINT64(usage_page
, usage
);
735 CFArrayAppendValue(axes
, element
);
739 FIXME("kIOHIDElementTypeInput_Misc / Unhandled usage %i/%i\n", usage_page
, usage
);
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
;
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
))
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
));
817 if (device
->elements
)
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
));
836 case kIOHIDElementTypeInput_Button
:
837 TRACE("kIOHIDElementTypeInput_Button\n");
841 if (IOHIDDeviceGetValue(hid_device
, element
, &valueRef
) != kIOReturnSuccess
)
843 if (valueRef
== NULL
)
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
++);
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");
868 if (IOHIDDeviceGetValue(hid_device
, element
, &valueRef
) != kIOReturnSuccess
)
870 if (valueRef
== NULL
)
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
))
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
++);
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
):
901 if (IOHIDDeviceGetValue(hid_device
, element
, &valueRef
) != kIOReturnSuccess
)
903 if (valueRef
== NULL
)
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");
912 oldVal
= device
->generic
.js
.lX
;
913 device
->generic
.js
.lX
= newVal
;
915 case MAKEUINT64(kHIDPage_GenericDesktop
, kHIDUsage_GD_Y
):
916 TRACE("kIOHIDElementTypeInput_Misc / kHIDUsage_GD_Y\n");
918 oldVal
= device
->generic
.js
.lY
;
919 device
->generic
.js
.lY
= newVal
;
921 case MAKEUINT64(kHIDPage_GenericDesktop
, kHIDUsage_GD_Z
):
922 TRACE("kIOHIDElementTypeInput_Misc / kHIDUsage_GD_Z\n");
924 oldVal
= device
->generic
.js
.lZ
;
925 device
->generic
.js
.lZ
= newVal
;
927 case MAKEUINT64(kHIDPage_GenericDesktop
, kHIDUsage_GD_Rx
):
928 case MAKEUINT64(kHIDPage_Simulation
, kHIDUsage_Sim_Accelerator
):
929 TRACE("kIOHIDElementTypeInput_Misc / kHIDUsage_GD_Rx\n");
931 oldVal
= device
->generic
.js
.lRx
;
932 device
->generic
.js
.lRx
= newVal
;
934 case MAKEUINT64(kHIDPage_GenericDesktop
, kHIDUsage_GD_Ry
):
935 case MAKEUINT64(kHIDPage_Simulation
, kHIDUsage_Sim_Brake
):
936 TRACE("kIOHIDElementTypeInput_Misc / kHIDUsage_GD_Ry\n");
938 oldVal
= device
->generic
.js
.lRy
;
939 device
->generic
.js
.lRy
= newVal
;
941 case MAKEUINT64(kHIDPage_GenericDesktop
, kHIDUsage_GD_Rz
):
942 TRACE("kIOHIDElementTypeInput_Misc / kHIDUsage_GD_Rz\n");
944 oldVal
= device
->generic
.js
.lRz
;
945 device
->generic
.js
.lRz
= newVal
;
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
;
955 TRACE("valueRef %s val %d oldVal %d newVal %d\n", debugstr_cf(valueRef
), val
, oldVal
, newVal
);
956 if ((wine_obj
!= -1) &&
959 inst_id
= DIDFT_MAKEINSTANCE(wine_obj
) | DIDFT_ABSAXIS
;
960 queue_event(iface
,inst_id
,newVal
,GetCurrentTime(),device
->generic
.base
.dinput
->evsequence
++);
966 FIXME("kIOHIDElementTypeInput_Misc / unhandled usage %i\n", usage
);
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
)
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
;
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
) {
1016 if(get_ff(device
, NULL
) != S_OK
)
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 */
1029 lpddi
->wUsage
= 0x04; /* Joystick */
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
;
1044 static HRESULT
joydev_enum_deviceW(DWORD dwDevType
, DWORD dwFlags
, LPDIDEVICEINSTANCEW lpddi
, DWORD version
, int id
)
1046 char name
[MAX_PATH
];
1048 IOHIDDeviceRef device
;
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
) {
1064 if(get_ff(device
, NULL
) != S_OK
)
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 */
1077 lpddi
->wUsage
= 0x04; /* Joystick */
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
;
1094 static const char *osx_ff_axis_name(UInt8 axis
)
1105 sprintf(ret
, "%u", (unsigned int)axis
);
1109 static BOOL
osx_axis_has_ff(FFCAPABILITIES
*ffcaps
, UInt8 axis
)
1112 for(i
= 0; i
< ffcaps
->numFfAxes
; ++i
)
1113 if(ffcaps
->ffAxes
[i
] == axis
)
1118 static HRESULT
alloc_device(REFGUID rguid
, IDirectInputImpl
*dinput
,
1119 JoystickImpl
**pdev
, unsigned short index
)
1122 IOHIDDeviceRef device
;
1123 JoystickImpl
* newDevice
;
1124 char name
[MAX_PATH
];
1126 LPDIDATAFORMAT df
= NULL
;
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");
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
);
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)
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
);
1183 WARN("FFDeviceSendForceFeedbackCommand(FFSFFC_RESET) failed: %08x\n", hr
);
1185 hr
= FFDeviceSendForceFeedbackCommand(newDevice
->ff
, FFSFFC_SETACTUATORSON
);
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
++)
1219 BOOL has_ff
= FALSE
;
1220 switch (axis_map
[i
])
1222 case MAKEUINT64(kHIDPage_GenericDesktop
, kHIDUsage_GD_X
):
1224 has_ff
= (newDevice
->ff
!= 0) && osx_axis_has_ff(&ffcaps
, FFJOFS_X
);
1226 case MAKEUINT64(kHIDPage_GenericDesktop
, kHIDUsage_GD_Y
):
1228 has_ff
= (newDevice
->ff
!= 0) && osx_axis_has_ff(&ffcaps
, FFJOFS_Y
);
1230 case MAKEUINT64(kHIDPage_GenericDesktop
, kHIDUsage_GD_Z
):
1232 has_ff
= (newDevice
->ff
!= 0) && osx_axis_has_ff(&ffcaps
, FFJOFS_Z
);
1234 case MAKEUINT64(kHIDPage_GenericDesktop
, kHIDUsage_GD_Rx
):
1235 case MAKEUINT64(kHIDPage_Simulation
, kHIDUsage_Sim_Accelerator
):
1237 has_ff
= (newDevice
->ff
!= 0) && osx_axis_has_ff(&ffcaps
, FFJOFS_RX
);
1239 case MAKEUINT64(kHIDPage_GenericDesktop
, kHIDUsage_GD_Ry
):
1240 case MAKEUINT64(kHIDPage_Simulation
, kHIDUsage_Sim_Brake
):
1242 has_ff
= (newDevice
->ff
!= 0) && osx_axis_has_ff(&ffcaps
, FFJOFS_RY
);
1244 case MAKEUINT64(kHIDPage_GenericDesktop
, kHIDUsage_GD_Rz
):
1246 has_ff
= (newDevice
->ff
!= 0) && osx_axis_has_ff(&ffcaps
, FFJOFS_RZ
);
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
));
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
;
1259 df
->rgodf
[idx
].dwFlags
|= DIDOI_FFACTUATOR
;
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);
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
);
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
);
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;
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
;
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
);
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
)
1357 else if (IsEqualGUID(&IID_IDirectInputDeviceA
, riid
) ||
1358 IsEqualGUID(&IID_IDirectInputDevice2A
, riid
) ||
1359 IsEqualGUID(&IID_IDirectInputDevice7A
, riid
) ||
1360 IsEqualGUID(&IID_IDirectInputDevice8A
, riid
))
1364 else if (IsEqualGUID(&IID_IDirectInputDeviceW
, riid
) ||
1365 IsEqualGUID(&IID_IDirectInputDevice2W
, riid
) ||
1366 IsEqualGUID(&IID_IDirectInputDevice7W
, riid
) ||
1367 IsEqualGUID(&IID_IDirectInputDevice8W
, riid
))
1373 WARN("no interface\n");
1374 return DIERR_NOINTERFACE
;
1377 hr
= alloc_device(rguid
, dinput
, &This
, index
);
1378 if (!This
) return hr
;
1381 *pdev
= &This
->generic
.base
.IDirectInputDevice8W_iface
;
1383 *pdev
= &This
->generic
.base
.IDirectInputDevice8A_iface
;
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};
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
));
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
));
1425 return JoystickWGenericImpl_GetProperty(iface
, rguid
, pdiph
);
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
)
1443 return DIERR_UNSUPPORTED
;
1445 hr
= osx_to_win32_hresult(FFDeviceSetForceFeedbackProperty(This
->ff
, FFPROP_AUTOCENTER
, &v
));
1446 TRACE("returning: %08x\n", hr
);
1450 static HRESULT
osx_set_ffgain(JoystickImpl
*This
, const DIPROPDWORD
*header
)
1455 return DIERR_UNSUPPORTED
;
1457 hr
= osx_to_win32_hresult(FFDeviceSetForceFeedbackProperty(This
->ff
, FFPROP_FFGAIN
, &v
));
1458 TRACE("returning: %08x\n", 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
)
1501 if(IsEqualGUID(&GUID_##X, effect)) \
1502 return kFFEffectType_##X##_ID;
1503 DO_MAP(ConstantForce
)
1509 DO_MAP(SawtoothDown
)
1516 WARN("Unknown effect GUID! %s\n", debugstr_guid(effect
));
1520 static HRESULT WINAPI
JoystickWImpl_CreateEffect(IDirectInputDevice8W
*iface
,
1521 const GUID
*type
, const DIEFFECT
*params
, IDirectInputEffect
**out
,
1524 JoystickImpl
*This
= impl_from_IDirectInputDevice8W(iface
);
1528 TRACE("(%p)->(%s %p %p %p)\n", This
, debugstr_guid(type
), params
, out
, outer
);
1529 dump_DIEFFECT(params
, type
, 0);
1532 TRACE("No force feedback support\n");
1534 return DIERR_UNSUPPORTED
;
1538 WARN("aggregation not implemented\n");
1540 effect
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(*This
));
1541 effect
->IDirectInputEffect_iface
.lpVtbl
= &EffectVtbl
;
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
));
1550 WARN("FFDeviceCreateEffect failed: %08x\n", hr
);
1551 HeapFree(GetProcessHeap(), 0, effect
);
1555 list_add_tail(&This
->effects
, &effect
->entry
);
1556 *out
= &effect
->IDirectInputEffect_iface
;
1558 TRACE("allocated effect: %p\n", effect
);
1563 static HRESULT WINAPI
JoystickAImpl_CreateEffect(IDirectInputDevice8A
*iface
,
1564 const GUID
*type
, const DIEFFECT
*params
, IDirectInputEffect
**out
,
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
,
1578 JoystickImpl
*This
= impl_from_IDirectInputDevice8W(iface
);
1581 TRACE("%p 0x%x\n", This
, flags
);
1586 hr
= osx_to_win32_hresult(FFDeviceSendForceFeedbackCommand(This
->ff
, flags
));
1588 WARN("FFDeviceSendForceFeedbackCommand failed: %08x\n", hr
);
1595 static HRESULT WINAPI
JoystickAImpl_SendForceFeedbackCommand(IDirectInputDevice8A
*iface
,
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
)){
1693 IDirectInputEffect_AddRef(iface
);
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
);
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
);
1715 list_remove(&This
->entry
);
1716 FFDeviceReleaseEffect(This
->device
->ff
, This
->effect
);
1717 HeapFree(GetProcessHeap(), 0, This
);
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
));
1731 static HRESULT WINAPI
effect_GetEffectGuid(IDirectInputEffect
*iface
, GUID
*out
)
1733 EffectImpl
*This
= impl_from_IDirectInputEffect(iface
);
1734 TRACE("%p %p\n", This
, out
);
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
,
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
,
1804 effect_GetEffectGuid
,
1805 effect_GetParameters
,
1806 effect_SetParameters
,
1809 effect_GetEffectStatus
,
1815 #else /* HAVE_IOHIDMANAGERCREATE */
1817 const struct dinput_device joystick_osx_device
= {
1818 "Wine OS X joystick driver",
1824 #endif /* HAVE_IOHIDMANAGERCREATE */