1 /* DirectInput Joystick device for Mac OS/X
2 *
3 * Copyright 1998 Marcus Meissner
4 * Copyright 1998,1999 Lionel Ulmer
5 * Copyright 2000-2001 TransGaming Technologies Inc.
6 * Copyright 2009 CodeWeavers, Aric Stewart
7 *
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation; either
11 * version 2.1 of the License, or (at your option) any later version.
12 *
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
17 *
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with this library; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
21 */
22
23 #include "config.h"
24 #include "wine/port.h"
25
26 #if defined(HAVE_IOKIT_HID_IOHIDLIB_H)
27 #define 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 */
81
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"
90
91 #include "dinput_private.h"
92 #include "device_private.h"
93 #include "joystick_private.h"
94
95 #ifdef HAVE_IOHIDMANAGERCREATE
96
97 WINE_DEFAULT_DEBUG_CHANNEL(dinput);
98
99 static CFMutableArrayRef device_main_elements = NULL;
100
101 typedef struct JoystickImpl JoystickImpl;
102 static const IDirectInputDevice8AVtbl JoystickAvt;
103 static const IDirectInputDevice8WVtbl JoystickWvt;
104
105 struct JoystickImpl
106 {
107 struct JoystickGenericImpl generic;
108
109 /* osx private */
110 int id;
111 CFArrayRef elements;
112 ObjProps **propmap;
113 FFDeviceObjectReference ff;
114 struct list effects;
115 };
116
impl_from_IDirectInputDevice8A(IDirectInputDevice8A * iface)117 static inline JoystickImpl *impl_from_IDirectInputDevice8A(IDirectInputDevice8A *iface)
118 {
119 return CONTAINING_RECORD(CONTAINING_RECORD(CONTAINING_RECORD(iface, IDirectInputDeviceImpl, IDirectInputDevice8A_iface),
120 JoystickGenericImpl, base), JoystickImpl, generic);
121 }
impl_from_IDirectInputDevice8W(IDirectInputDevice8W * iface)122 static inline JoystickImpl *impl_from_IDirectInputDevice8W(IDirectInputDevice8W *iface)
123 {
124 return CONTAINING_RECORD(CONTAINING_RECORD(CONTAINING_RECORD(iface, IDirectInputDeviceImpl, IDirectInputDevice8W_iface),
125 JoystickGenericImpl, base), JoystickImpl, generic);
126 }
127
IDirectInputDevice8W_from_impl(JoystickImpl * This)128 static inline IDirectInputDevice8W *IDirectInputDevice8W_from_impl(JoystickImpl *This)
129 {
130 return &This->generic.base.IDirectInputDevice8W_iface;
131 }
132
133 typedef struct _EffectImpl {
134 IDirectInputEffect IDirectInputEffect_iface;
135 LONG ref;
136
137 JoystickImpl *device;
138 FFEffectObjectReference effect;
139 GUID guid;
140
141 struct list entry;
142 } EffectImpl;
143
impl_from_IDirectInputEffect(IDirectInputEffect * iface)144 static EffectImpl *impl_from_IDirectInputEffect(IDirectInputEffect *iface)
145 {
146 return CONTAINING_RECORD(iface, EffectImpl, IDirectInputEffect_iface);
147 }
148
149 static const IDirectInputEffectVtbl EffectVtbl;
150
151 static const GUID DInput_Wine_OsX_Joystick_GUID = { /* 59CAD8F6-E617-41E2-8EB7-47B23EEEDC5A */
152 0x59CAD8F6, 0xE617, 0x41E2, {0x8E, 0xB7, 0x47, 0xB2, 0x3E, 0xEE, 0xDC, 0x5A}
153 };
154
osx_to_win32_hresult(HRESULT in)155 static HRESULT osx_to_win32_hresult(HRESULT in)
156 {
157 /* OSX returns 16-bit COM runtime errors, which we should
158 * convert to win32 */
159 switch(in){
160 case 0x80000001:
161 return E_NOTIMPL;
162 case 0x80000002:
163 return E_OUTOFMEMORY;
164 case 0x80000003:
165 return E_INVALIDARG;
166 case 0x80000004:
167 return E_NOINTERFACE;
168 case 0x80000005:
169 return E_POINTER;
170 case 0x80000006:
171 return E_HANDLE;
172 case 0x80000007:
173 return E_ABORT;
174 case 0x80000008:
175 return E_FAIL;
176 case 0x80000009:
177 return E_ACCESSDENIED;
178 case 0x8000FFFF:
179 return E_UNEXPECTED;
180 }
181 return in;
182 }
183
get_device_property_long(IOHIDDeviceRef device,CFStringRef key)184 static long get_device_property_long(IOHIDDeviceRef device, CFStringRef key)
185 {
186 CFTypeRef ref;
187 long result = 0;
188
189 if (device)
190 {
191 assert(IOHIDDeviceGetTypeID() == CFGetTypeID(device));
192
193 ref = IOHIDDeviceGetProperty(device, key);
194
195 if (ref && CFNumberGetTypeID() == CFGetTypeID(ref))
196 CFNumberGetValue((CFNumberRef)ref, kCFNumberLongType, &result);
197 }
198
199 return result;
200 }
201
copy_device_name(IOHIDDeviceRef device)202 static CFStringRef copy_device_name(IOHIDDeviceRef device)
203 {
204 CFStringRef name;
205
206 if (device)
207 {
208 CFTypeRef ref_name;
209
210 assert(IOHIDDeviceGetTypeID() == CFGetTypeID(device));
211
212 ref_name = IOHIDDeviceGetProperty(device, CFSTR(kIOHIDProductKey));
213
214 if (ref_name && CFStringGetTypeID() == CFGetTypeID(ref_name))
215 name = CFStringCreateCopy(kCFAllocatorDefault, ref_name);
216 else
217 {
218 long vendID, prodID;
219
220 vendID = get_device_property_long(device, CFSTR(kIOHIDVendorIDKey));
221 prodID = get_device_property_long(device, CFSTR(kIOHIDProductIDKey));
222 name = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("0x%04lx 0x%04lx"), vendID, prodID);
223 }
224 }
225 else
226 {
227 ERR("NULL device\n");
228 name = CFStringCreateCopy(kCFAllocatorDefault, CFSTR(""));
229 }
230
231 return name;
232 }
233
get_device_location_ID(IOHIDDeviceRef device)234 static long get_device_location_ID(IOHIDDeviceRef device)
235 {
236 return get_device_property_long(device, CFSTR(kIOHIDLocationIDKey));
237 }
238
copy_set_to_array(const void * value,void * context)239 static void copy_set_to_array(const void *value, void *context)
240 {
241 CFArrayAppendValue(context, value);
242 }
243
device_name_comparator(IOHIDDeviceRef device1,IOHIDDeviceRef device2)244 static CFComparisonResult device_name_comparator(IOHIDDeviceRef device1, IOHIDDeviceRef device2)
245 {
246 CFStringRef name1 = copy_device_name(device1), name2 = copy_device_name(device2);
247 CFComparisonResult result = CFStringCompare(name1, name2, (kCFCompareForcedOrdering | kCFCompareNumerically));
248 CFRelease(name1);
249 CFRelease(name2);
250 return result;
251 }
252
device_location_name_comparator(const void * val1,const void * val2,void * context)253 static CFComparisonResult device_location_name_comparator(const void *val1, const void *val2, void *context)
254 {
255 IOHIDDeviceRef device1 = (IOHIDDeviceRef)val1, device2 = (IOHIDDeviceRef)val2;
256 long loc1 = get_device_location_ID(device1), loc2 = get_device_location_ID(device2);
257
258 if (loc1 < loc2)
259 return kCFCompareLessThan;
260 else if (loc1 > loc2)
261 return kCFCompareGreaterThan;
262 /* virtual joysticks may not have a kIOHIDLocationIDKey and will default to location ID of 0, this orders virtual joysticks by their name */
263 return device_name_comparator(device1, device2);
264 }
265
debugstr_cf(CFTypeRef t)266 static const char* debugstr_cf(CFTypeRef t)
267 {
268 CFStringRef s;
269 const char* ret;
270
271 if (!t) return "(null)";
272
273 if (CFGetTypeID(t) == CFStringGetTypeID())
274 s = t;
275 else
276 s = CFCopyDescription(t);
277 ret = CFStringGetCStringPtr(s, kCFStringEncodingUTF8);
278 if (ret) ret = debugstr_a(ret);
279 if (!ret)
280 {
281 const UniChar* u = CFStringGetCharactersPtr(s);
282 if (u)
283 ret = debugstr_wn((const WCHAR*)u, CFStringGetLength(s));
284 }
285 if (!ret)
286 {
287 UniChar buf[200];
288 int len = min(CFStringGetLength(s), ARRAY_SIZE(buf));
289 CFStringGetCharacters(s, CFRangeMake(0, len), buf);
290 ret = debugstr_wn(buf, len);
291 }
292 if (s != t) CFRelease(s);
293 return ret;
294 }
295
debugstr_device(IOHIDDeviceRef device)296 static const char* debugstr_device(IOHIDDeviceRef device)
297 {
298 return wine_dbg_sprintf("<IOHIDDevice %p product %s IOHIDLocationID %lu>", device,
299 debugstr_cf(IOHIDDeviceGetProperty(device, CFSTR(kIOHIDProductKey))),
300 get_device_location_ID(device));
301 }
302
debugstr_element(IOHIDElementRef element)303 static const char* debugstr_element(IOHIDElementRef element)
304 {
305 return wine_dbg_sprintf("<IOHIDElement %p type %d usage %u/%u device %p>", element,
306 IOHIDElementGetType(element), IOHIDElementGetUsagePage(element),
307 IOHIDElementGetUsage(element), IOHIDElementGetDevice(element));
308 }
309
get_device_ref(int id)310 static IOHIDDeviceRef get_device_ref(int id)
311 {
312 IOHIDElementRef device_main_element;
313 IOHIDDeviceRef hid_device;
314
315 TRACE("id %d\n", id);
316
317 if (!device_main_elements || id >= CFArrayGetCount(device_main_elements))
318 return 0;
319
320 device_main_element = (IOHIDElementRef)CFArrayGetValueAtIndex(device_main_elements, id);
321 if (!device_main_element)
322 {
323 ERR("Invalid Element requested %i\n",id);
324 return 0;
325 }
326
327 hid_device = IOHIDElementGetDevice(device_main_element);
328 if (!hid_device)
329 {
330 ERR("Invalid Device requested %i\n",id);
331 return 0;
332 }
333
334 TRACE("-> %s\n", debugstr_device(hid_device));
335 return hid_device;
336 }
337
get_ff(IOHIDDeviceRef device,FFDeviceObjectReference * ret)338 static HRESULT get_ff(IOHIDDeviceRef device, FFDeviceObjectReference *ret)
339 {
340 io_service_t service;
341 CFMutableDictionaryRef matching;
342 CFTypeRef location_id;
343 HRESULT hr;
344
345 TRACE("device %s\n", debugstr_device(device));
346
347 matching = IOServiceMatching(kIOHIDDeviceKey);
348 if(!matching){
349 WARN("IOServiceMatching failed, force feedback disabled\n");
350 return DIERR_DEVICENOTREG;
351 }
352
353 location_id = IOHIDDeviceGetProperty(device, CFSTR(kIOHIDLocationIDKey));
354 if(!location_id){
355 CFRelease(matching);
356 WARN("IOHIDDeviceGetProperty failed, force feedback disabled\n");
357 return DIERR_DEVICENOTREG;
358 }
359
360 CFDictionaryAddValue(matching, CFSTR(kIOHIDLocationIDKey), location_id);
361
362 service = IOServiceGetMatchingService(kIOMasterPortDefault, matching);
363
364 if (ret)
365 hr = osx_to_win32_hresult(FFCreateDevice(service, ret));
366 else
367 hr = FFIsForceFeedback(service) == FF_OK ? S_OK : S_FALSE;
368
369 IOObjectRelease(service);
370 TRACE("-> hr 0x%08x *ret %p\n", hr, ret ? *ret : NULL);
371 return hr;
372 }
373
create_osx_device_match(int usage)374 static CFMutableDictionaryRef create_osx_device_match(int usage)
375 {
376 CFMutableDictionaryRef result;
377
378 TRACE("usage %d\n", usage);
379
380 result = CFDictionaryCreateMutable( kCFAllocatorDefault, 0,
381 &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks );
382
383 if ( result )
384 {
385 int number = kHIDPage_GenericDesktop;
386 CFNumberRef page = CFNumberCreate( kCFAllocatorDefault,
387 kCFNumberIntType, &number);
388
389 if (page)
390 {
391 CFNumberRef cf_usage;
392
393 CFDictionarySetValue( result, CFSTR( kIOHIDDeviceUsagePageKey ), page );
394 CFRelease( page );
395
396 cf_usage = CFNumberCreate( kCFAllocatorDefault,
397 kCFNumberIntType, &usage);
398 if (cf_usage)
399 {
400 CFDictionarySetValue( result, CFSTR( kIOHIDDeviceUsageKey ), cf_usage );
401 CFRelease( cf_usage );
402 }
403 else
404 {
405 ERR("CFNumberCreate() failed.\n");
406 CFRelease(result);
407 return NULL;
408 }
409 }
410 else
411 {
412 ERR("CFNumberCreate failed.\n");
413 CFRelease(result);
414 return NULL;
415 }
416 }
417 else
418 {
419 ERR("CFDictionaryCreateMutable failed.\n");
420 return NULL;
421 }
422
423 return result;
424 }
425
find_top_level(IOHIDDeviceRef hid_device,CFMutableArrayRef main_elements)426 static CFIndex find_top_level(IOHIDDeviceRef hid_device, CFMutableArrayRef main_elements)
427 {
428 CFArrayRef elements;
429 CFIndex total = 0;
430
431 TRACE("hid_device %s\n", debugstr_device(hid_device));
432
433 if (!hid_device)
434 return 0;
435
436 elements = IOHIDDeviceCopyMatchingElements(hid_device, NULL, 0);
437
438 if (elements)
439 {
440 CFIndex idx, cnt = CFArrayGetCount(elements);
441 for (idx=0; idx<cnt; idx++)
442 {
443 IOHIDElementRef element = (IOHIDElementRef)CFArrayGetValueAtIndex(elements, idx);
444 int type = IOHIDElementGetType(element);
445
446 TRACE("element %s\n", debugstr_element(element));
447
448 /* Check for top-level gaming device collections */
449 if (type == kIOHIDElementTypeCollection && IOHIDElementGetParent(element) == 0)
450 {
451 int usage_page = IOHIDElementGetUsagePage(element);
452 int usage = IOHIDElementGetUsage(element);
453
454 if (usage_page == kHIDPage_GenericDesktop &&
455 (usage == kHIDUsage_GD_Joystick || usage == kHIDUsage_GD_GamePad))
456 {
457 CFArrayAppendValue(main_elements, element);
458 total++;
459 }
460 }
461 }
462 CFRelease(elements);
463 }
464
465 TRACE("-> total %d\n", (int)total);
466 return total;
467 }
468
get_element_children(IOHIDElementRef element,CFMutableArrayRef all_children)469 static void get_element_children(IOHIDElementRef element, CFMutableArrayRef all_children)
470 {
471 CFIndex idx, cnt;
472 CFArrayRef element_children = IOHIDElementGetChildren(element);
473
474 TRACE("element %s\n", debugstr_element(element));
475
476 cnt = CFArrayGetCount(element_children);
477
478 /* Either add the element to the array or grab its children */
479 for (idx=0; idx<cnt; idx++)
480 {
481 IOHIDElementRef child;
482
483 child = (IOHIDElementRef)CFArrayGetValueAtIndex(element_children, idx);
484 TRACE("child %s\n", debugstr_element(child));
485 if (IOHIDElementGetType(child) == kIOHIDElementTypeCollection)
486 get_element_children(child, all_children);
487 else
488 CFArrayAppendValue(all_children, child);
489 }
490 }
491
find_osx_devices(void)492 static int find_osx_devices(void)
493 {
494 IOHIDManagerRef hid_manager;
495 CFMutableDictionaryRef result;
496 CFSetRef devset;
497 CFMutableArrayRef matching;
498
499 TRACE("()\n");
500
501 hid_manager = IOHIDManagerCreate( kCFAllocatorDefault, 0L );
502 if (IOHIDManagerOpen( hid_manager, 0 ) != kIOReturnSuccess)
503 {
504 ERR("Couldn't open IOHIDManager.\n");
505 CFRelease( hid_manager );
506 return 0;
507 }
508
509 matching = CFArrayCreateMutable( kCFAllocatorDefault, 0,
510 &kCFTypeArrayCallBacks );
511
512 /* build matching dictionary */
513 result = create_osx_device_match(kHIDUsage_GD_Joystick);
514 if (!result)
515 {
516 CFRelease(matching);
517 goto fail;
518 }
519 CFArrayAppendValue( matching, result );
520 CFRelease( result );
521 result = create_osx_device_match(kHIDUsage_GD_GamePad);
522 if (!result)
523 {
524 CFRelease(matching);
525 goto fail;
526 }
527 CFArrayAppendValue( matching, result );
528 CFRelease( result );
529
530 IOHIDManagerSetDeviceMatchingMultiple( hid_manager, matching);
531 CFRelease( matching );
532 devset = IOHIDManagerCopyDevices( hid_manager );
533 if (devset)
534 {
535 CFIndex num_devices, num_main_elements, idx;
536 CFMutableArrayRef devices;
537
538 num_devices = CFSetGetCount(devset);
539 devices = CFArrayCreateMutable(kCFAllocatorDefault, num_devices, &kCFTypeArrayCallBacks);
540 CFSetApplyFunction(devset, copy_set_to_array, devices);
541 CFRelease(devset);
542 CFArraySortValues(devices, CFRangeMake(0, num_devices), device_location_name_comparator, NULL);
543
544 device_main_elements = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
545 if (!device_main_elements)
546 {
547 CFRelease( devices );
548 goto fail;
549 }
550
551 num_main_elements = 0;
552 for (idx = 0; idx < num_devices; idx++)
553 {
554 CFIndex top;
555 IOHIDDeviceRef hid_device;
556
557 hid_device = (IOHIDDeviceRef) CFArrayGetValueAtIndex(devices, idx);
558 TRACE("hid_device %s\n", debugstr_device(hid_device));
559 top = find_top_level(hid_device, device_main_elements);
560 num_main_elements += top;
561 }
562
563 CFRelease(devices);
564
565 TRACE("found %i device(s), %i collection(s)\n",(int)num_devices,(int)num_main_elements);
566 return (int)num_main_elements;
567 }
568
569 fail:
570 IOHIDManagerClose( hid_manager, 0 );
571 CFRelease( hid_manager );
572 return 0;
573 }
574
get_osx_device_name(int id,char * name,int length)575 static int get_osx_device_name(int id, char *name, int length)
576 {
577 CFStringRef str;
578 IOHIDDeviceRef hid_device;
579
580 hid_device = get_device_ref(id);
581
582 TRACE("id %d hid_device %s\n", id, debugstr_device(hid_device));
583
584 if (name)
585 name[0] = 0;
586
587 if (!hid_device)
588 return 0;
589
590 str = IOHIDDeviceGetProperty(hid_device, CFSTR( kIOHIDProductKey ));
591 if (str)
592 {
593 CFIndex len = CFStringGetLength(str);
594 if (length >= len)
595 {
596 CFStringGetCString(str,name,length,kCFStringEncodingASCII);
597 return len;
598 }
599 else
600 return (len+1);
601 }
602 return 0;
603 }
604
button_usage_comparator(const void * val1,const void * val2,void * context)605 static CFComparisonResult button_usage_comparator(const void *val1, const void *val2, void *context)
606 {
607 IOHIDElementRef element1 = (IOHIDElementRef)val1, element2 = (IOHIDElementRef)val2;
608 int usage1 = IOHIDElementGetUsage(element1), usage2 = IOHIDElementGetUsage(element2);
609
610 if (usage1 < usage2)
611 return kCFCompareLessThan;
612 if (usage1 > usage2)
613 return kCFCompareGreaterThan;
614 return kCFCompareEqualTo;
615 }
616
get_osx_device_elements(JoystickImpl * device,int axis_map[8])617 static void get_osx_device_elements(JoystickImpl *device, int axis_map[8])
618 {
619 IOHIDElementRef device_main_element;
620 CFMutableArrayRef elements;
621 DWORD sliders = 0;
622
623 TRACE("device %p device->id %d\n", device, device->id);
624
625 device->elements = NULL;
626
627 if (!device_main_elements || device->id >= CFArrayGetCount(device_main_elements))
628 return;
629
630 device_main_element = (IOHIDElementRef)CFArrayGetValueAtIndex(device_main_elements, device->id);
631 TRACE("device_main_element %s\n", debugstr_element(device_main_element));
632 if (!device_main_element)
633 return;
634
635 elements = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
636 get_element_children(device_main_element, elements);
637
638 if (elements)
639 {
640 CFIndex idx, cnt = CFArrayGetCount( elements );
641 CFMutableArrayRef axes = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
642 CFMutableArrayRef buttons = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
643 CFMutableArrayRef povs = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
644
645 for ( idx = 0; idx < cnt; idx++ )
646 {
647 IOHIDElementRef element = ( IOHIDElementRef ) CFArrayGetValueAtIndex( elements, idx );
648 int type = IOHIDElementGetType( element );
649 int usage_page = IOHIDElementGetUsagePage( element );
650
651 TRACE("element %s\n", debugstr_element(element));
652
653 if (usage_page >= kHIDPage_VendorDefinedStart)
654 {
655 /* vendor pages can repurpose type ids, resulting in incorrect case matches below (e.g. ds4 controllers) */
656 continue;
657 }
658
659 switch(type)
660 {
661 case kIOHIDElementTypeInput_Button:
662 {
663 TRACE("kIOHIDElementTypeInput_Button usage_page %d\n", usage_page);
664 if (usage_page != kHIDPage_Button)
665 {
666 /* avoid strange elements found on the 360 controller */
667 continue;
668 }
669
670 if (CFArrayGetCount(buttons) < 128)
671 CFArrayAppendValue(buttons, element);
672 break;
673 }
674 case kIOHIDElementTypeInput_Axis:
675 {
676 TRACE("kIOHIDElementTypeInput_Axis\n");
677 CFArrayAppendValue(axes, element);
678 break;
679 }
680 case kIOHIDElementTypeInput_Misc:
681 {
682 uint32_t usage = IOHIDElementGetUsage( element );
683 switch(usage)
684 {
685 case kHIDUsage_GD_Hatswitch:
686 {
687 TRACE("kIOHIDElementTypeInput_Misc / kHIDUsage_GD_Hatswitch\n");
688 CFArrayAppendValue(povs, element);
689 break;
690 }
691 case kHIDUsage_GD_Slider:
692 sliders ++;
693 if (sliders > 2)
694 break;
695 /* fallthrough, sliders are axis */
696 case kHIDUsage_GD_X:
697 case kHIDUsage_GD_Y:
698 case kHIDUsage_GD_Z:
699 case kHIDUsage_GD_Rx:
700 case kHIDUsage_GD_Ry:
701 case kHIDUsage_GD_Rz:
702 {
703 TRACE("kIOHIDElementTypeInput_Misc / kHIDUsage_GD_* (%d)\n", usage);
704 axis_map[CFArrayGetCount(axes)]=usage;
705 CFArrayAppendValue(axes, element);
706 break;
707 }
708 default:
709 FIXME("kIOHIDElementTypeInput_Misc / Unhandled usage %i\n", usage);
710 }
711 break;
712 }
713 default:
714 FIXME("Unhandled type %i\n",type);
715 }
716 }
717
718 /* Sort buttons into correct order */
719 CFArraySortValues(buttons, CFRangeMake(0, CFArrayGetCount(buttons)), button_usage_comparator, NULL);
720
721 device->generic.devcaps.dwAxes = CFArrayGetCount(axes);
722 device->generic.devcaps.dwButtons = CFArrayGetCount(buttons);
723 device->generic.devcaps.dwPOVs = CFArrayGetCount(povs);
724
725 TRACE("axes %u povs %u buttons %u\n", device->generic.devcaps.dwAxes, device->generic.devcaps.dwPOVs,
726 device->generic.devcaps.dwButtons);
727
728 /* build our element array in the order that dinput expects */
729 CFArrayAppendArray(axes, povs, CFRangeMake(0, device->generic.devcaps.dwPOVs));
730 CFArrayAppendArray(axes, buttons, CFRangeMake(0, device->generic.devcaps.dwButtons));
731 device->elements = axes;
732 axes = NULL;
733
734 CFRelease(povs);
735 CFRelease(buttons);
736 CFRelease(elements);
737 }
738 else
739 {
740 device->generic.devcaps.dwAxes = 0;
741 device->generic.devcaps.dwButtons = 0;
742 device->generic.devcaps.dwPOVs = 0;
743 }
744 }
745
get_osx_device_elements_props(JoystickImpl * device)746 static void get_osx_device_elements_props(JoystickImpl *device)
747 {
748 TRACE("device %p\n", device);
749
750 if (device->elements)
751 {
752 CFIndex idx, cnt = CFArrayGetCount( device->elements );
753
754 for ( idx = 0; idx < cnt; idx++ )
755 {
756 IOHIDElementRef element = ( IOHIDElementRef ) CFArrayGetValueAtIndex( device->elements, idx );
757
758 TRACE("element %s\n", debugstr_element(element));
759
760 device->generic.props[idx].lDevMin = IOHIDElementGetLogicalMin(element);
761 device->generic.props[idx].lDevMax = IOHIDElementGetLogicalMax(element);
762 device->generic.props[idx].lMin = 0;
763 device->generic.props[idx].lMax = 0xffff;
764 device->generic.props[idx].lDeadZone = 0;
765 device->generic.props[idx].lSaturation = 0;
766 }
767 }
768 }
769
poll_osx_device_state(LPDIRECTINPUTDEVICE8A iface)770 static void poll_osx_device_state(LPDIRECTINPUTDEVICE8A iface)
771 {
772 JoystickImpl *device = impl_from_IDirectInputDevice8A(iface);
773 IOHIDElementRef device_main_element;
774 IOHIDDeviceRef hid_device;
775
776 TRACE("device %p device->id %i\n", device, device->id);
777
778 if (!device_main_elements || device->id >= CFArrayGetCount(device_main_elements))
779 return;
780
781 device_main_element = (IOHIDElementRef) CFArrayGetValueAtIndex(device_main_elements, device->id);
782 hid_device = IOHIDElementGetDevice(device_main_element);
783 TRACE("main element %s hid_device %s\n", debugstr_element(device_main_element), debugstr_device(hid_device));
784 if (!hid_device)
785 return;
786
787 if (device->elements)
788 {
789 int button_idx = 0;
790 int pov_idx = 0;
791 int slider_idx = 0;
792 int inst_id;
793 CFIndex idx, cnt = CFArrayGetCount( device->elements );
794
795 for ( idx = 0; idx < cnt; idx++ )
796 {
797 IOHIDValueRef valueRef;
798 int val, oldVal, newVal;
799 IOHIDElementRef element = ( IOHIDElementRef ) CFArrayGetValueAtIndex( device->elements, idx );
800 int type = IOHIDElementGetType( element );
801
802 TRACE("element %s\n", debugstr_element(element));
803
804 switch(type)
805 {
806 case kIOHIDElementTypeInput_Button:
807 TRACE("kIOHIDElementTypeInput_Button\n");
808 if(button_idx < 128)
809 {
810 valueRef = NULL;
811 if (IOHIDDeviceGetValue(hid_device, element, &valueRef) != kIOReturnSuccess)
812 return;
813 if (valueRef == NULL)
814 return;
815 val = IOHIDValueGetIntegerValue(valueRef);
816 newVal = val ? 0x80 : 0x0;
817 oldVal = device->generic.js.rgbButtons[button_idx];
818 device->generic.js.rgbButtons[button_idx] = newVal;
819 TRACE("valueRef %s val %d oldVal %d newVal %d\n", debugstr_cf(valueRef), val, oldVal, newVal);
820 if (oldVal != newVal)
821 {
822 button_idx = device->generic.button_map[button_idx];
823
824 inst_id = DIDFT_MAKEINSTANCE(button_idx) | DIDFT_PSHBUTTON;
825 queue_event(iface,inst_id,newVal,GetCurrentTime(),device->generic.base.dinput->evsequence++);
826 }
827 button_idx ++;
828 }
829 break;
830 case kIOHIDElementTypeInput_Misc:
831 {
832 uint32_t usage = IOHIDElementGetUsage( element );
833 switch(usage)
834 {
835 case kHIDUsage_GD_Hatswitch:
836 {
837 TRACE("kIOHIDElementTypeInput_Misc / kHIDUsage_GD_Hatswitch\n");
838 valueRef = NULL;
839 if (IOHIDDeviceGetValue(hid_device, element, &valueRef) != kIOReturnSuccess)
840 return;
841 if (valueRef == NULL)
842 return;
843 val = IOHIDValueGetIntegerValue(valueRef);
844 oldVal = device->generic.js.rgdwPOV[pov_idx];
845 if (val >= 8)
846 newVal = -1;
847 else
848 newVal = val * 4500;
849 device->generic.js.rgdwPOV[pov_idx] = newVal;
850 TRACE("valueRef %s val %d oldVal %d newVal %d\n", debugstr_cf(valueRef), val, oldVal, newVal);
851 if (oldVal != newVal)
852 {
853 inst_id = DIDFT_MAKEINSTANCE(pov_idx) | DIDFT_POV;
854 queue_event(iface,inst_id,newVal,GetCurrentTime(),device->generic.base.dinput->evsequence++);
855 }
856 pov_idx ++;
857 break;
858 }
859 case kHIDUsage_GD_X:
860 case kHIDUsage_GD_Y:
861 case kHIDUsage_GD_Z:
862 case kHIDUsage_GD_Rx:
863 case kHIDUsage_GD_Ry:
864 case kHIDUsage_GD_Rz:
865 case kHIDUsage_GD_Slider:
866 {
867 int wine_obj = -1;
868
869 valueRef = NULL;
870 if (IOHIDDeviceGetValue(hid_device, element, &valueRef) != kIOReturnSuccess)
871 return;
872 if (valueRef == NULL)
873 return;
874 val = IOHIDValueGetIntegerValue(valueRef);
875 newVal = joystick_map_axis(&device->generic.props[idx], val);
876 switch (usage)
877 {
878 case kHIDUsage_GD_X:
879 TRACE("kIOHIDElementTypeInput_Misc / kHIDUsage_GD_X\n");
880 wine_obj = 0;
881 oldVal = device->generic.js.lX;
882 device->generic.js.lX = newVal;
883 break;
884 case kHIDUsage_GD_Y:
885 TRACE("kIOHIDElementTypeInput_Misc / kHIDUsage_GD_Y\n");
886 wine_obj = 1;
887 oldVal = device->generic.js.lY;
888 device->generic.js.lY = newVal;
889 break;
890 case kHIDUsage_GD_Z:
891 TRACE("kIOHIDElementTypeInput_Misc / kHIDUsage_GD_Z\n");
892 wine_obj = 2;
893 oldVal = device->generic.js.lZ;
894 device->generic.js.lZ = newVal;
895 break;
896 case kHIDUsage_GD_Rx:
897 TRACE("kIOHIDElementTypeInput_Misc / kHIDUsage_GD_Rx\n");
898 wine_obj = 3;
899 oldVal = device->generic.js.lRx;
900 device->generic.js.lRx = newVal;
901 break;
902 case kHIDUsage_GD_Ry:
903 TRACE("kIOHIDElementTypeInput_Misc / kHIDUsage_GD_Ry\n");
904 wine_obj = 4;
905 oldVal = device->generic.js.lRy;
906 device->generic.js.lRy = newVal;
907 break;
908 case kHIDUsage_GD_Rz:
909 TRACE("kIOHIDElementTypeInput_Misc / kHIDUsage_GD_Rz\n");
910 wine_obj = 5;
911 oldVal = device->generic.js.lRz;
912 device->generic.js.lRz = newVal;
913 break;
914 case kHIDUsage_GD_Slider:
915 TRACE("kIOHIDElementTypeInput_Misc / kHIDUsage_GD_Slider\n");
916 wine_obj = 6 + slider_idx;
917 oldVal = device->generic.js.rglSlider[slider_idx];
918 device->generic.js.rglSlider[slider_idx] = newVal;
919 slider_idx ++;
920 break;
921 }
922 TRACE("valueRef %s val %d oldVal %d newVal %d\n", debugstr_cf(valueRef), val, oldVal, newVal);
923 if ((wine_obj != -1) &&
924 (oldVal != newVal))
925 {
926 inst_id = DIDFT_MAKEINSTANCE(wine_obj) | DIDFT_ABSAXIS;
927 queue_event(iface,inst_id,newVal,GetCurrentTime(),device->generic.base.dinput->evsequence++);
928 }
929
930 break;
931 }
932 default:
933 FIXME("kIOHIDElementTypeInput_Misc / unhandled usage %i\n", usage);
934 }
935 break;
936 }
937 default:
938 FIXME("Unhandled type %i\n",type);
939 }
940 }
941 }
942 }
943
find_joystick_devices(void)944 static INT find_joystick_devices(void)
945 {
946 static INT joystick_devices_count = -1;
947
948 if (joystick_devices_count != -1) return joystick_devices_count;
949
950 joystick_devices_count = find_osx_devices();
951
952 return joystick_devices_count;
953 }
954
make_vid_pid(IOHIDDeviceRef device)955 static DWORD make_vid_pid(IOHIDDeviceRef device)
956 {
957 long vendID, prodID;
958
959 vendID = get_device_property_long(device, CFSTR(kIOHIDVendorIDKey));
960 prodID = get_device_property_long(device, CFSTR(kIOHIDProductIDKey));
961
962 return MAKELONG(vendID, prodID);
963 }
964
joydev_enum_deviceA(DWORD dwDevType,DWORD dwFlags,LPDIDEVICEINSTANCEA lpddi,DWORD version,int id)965 static HRESULT joydev_enum_deviceA(DWORD dwDevType, DWORD dwFlags, LPDIDEVICEINSTANCEA lpddi, DWORD version, int id)
966 {
967 IOHIDDeviceRef device;
968 BOOL is_joystick;
969
970 TRACE("dwDevType %u dwFlags 0x%08x version 0x%04x id %d\n", dwDevType, dwFlags, version, id);
971
972 if (id >= find_joystick_devices()) return E_FAIL;
973
974 device = get_device_ref(id);
975
976 if ((dwDevType == 0) ||
977 ((dwDevType == DIDEVTYPE_JOYSTICK) && (version >= 0x0300 && version < 0x0800)) ||
978 (((dwDevType == DI8DEVCLASS_GAMECTRL) || (dwDevType == DI8DEVTYPE_JOYSTICK)) && (version >= 0x0800)))
979 {
980 if (dwFlags & DIEDFL_FORCEFEEDBACK) {
981 if(!device)
982 return S_FALSE;
983 if(get_ff(device, NULL) != S_OK)
984 return S_FALSE;
985 }
986 is_joystick = get_device_property_long(device, CFSTR(kIOHIDDeviceUsageKey)) == kHIDUsage_GD_Joystick;
987 /* Return joystick */
988 lpddi->guidInstance = DInput_Wine_OsX_Joystick_GUID;
989 lpddi->guidInstance.Data3 = id;
990 lpddi->guidProduct = DInput_PIDVID_Product_GUID;
991 lpddi->guidProduct.Data1 = make_vid_pid(device);
992 lpddi->dwDevType = get_device_type(version, is_joystick);
993 lpddi->dwDevType |= DIDEVTYPE_HID;
994 lpddi->wUsagePage = 0x01; /* Desktop */
995 if (is_joystick)
996 lpddi->wUsage = 0x04; /* Joystick */
997 else
998 lpddi->wUsage = 0x05; /* Game Pad */
999 sprintf(lpddi->tszInstanceName, "Joystick %d", id);
1000
1001 /* get the device name */
1002 get_osx_device_name(id, lpddi->tszProductName, MAX_PATH);
1003
1004 lpddi->guidFFDriver = GUID_NULL;
1005 return S_OK;
1006 }
1007
1008 return S_FALSE;
1009 }
1010
joydev_enum_deviceW(DWORD dwDevType,DWORD dwFlags,LPDIDEVICEINSTANCEW lpddi,DWORD version,int id)1011 static HRESULT joydev_enum_deviceW(DWORD dwDevType, DWORD dwFlags, LPDIDEVICEINSTANCEW lpddi, DWORD version, int id)
1012 {
1013 char name[MAX_PATH];
1014 char friendly[32];
1015 IOHIDDeviceRef device;
1016 BOOL is_joystick;
1017
1018 TRACE("dwDevType %u dwFlags 0x%08x version 0x%04x id %d\n", dwDevType, dwFlags, version, id);
1019
1020 if (id >= find_joystick_devices()) return E_FAIL;
1021
1022 device = get_device_ref(id);
1023
1024 if ((dwDevType == 0) ||
1025 ((dwDevType == DIDEVTYPE_JOYSTICK) && (version >= 0x0300 && version < 0x0800)) ||
1026 (((dwDevType == DI8DEVCLASS_GAMECTRL) || (dwDevType == DI8DEVTYPE_JOYSTICK)) && (version >= 0x0800))) {
1027
1028 if (dwFlags & DIEDFL_FORCEFEEDBACK) {
1029 if(!device)
1030 return S_FALSE;
1031 if(get_ff(device, NULL) != S_OK)
1032 return S_FALSE;
1033 }
1034 is_joystick = get_device_property_long(device, CFSTR(kIOHIDDeviceUsageKey)) == kHIDUsage_GD_Joystick;
1035 /* Return joystick */
1036 lpddi->guidInstance = DInput_Wine_OsX_Joystick_GUID;
1037 lpddi->guidInstance.Data3 = id;
1038 lpddi->guidProduct = DInput_PIDVID_Product_GUID;
1039 lpddi->guidProduct.Data1 = make_vid_pid(device);
1040 lpddi->dwDevType = get_device_type(version, is_joystick);
1041 lpddi->dwDevType |= DIDEVTYPE_HID;
1042 lpddi->wUsagePage = 0x01; /* Desktop */
1043 if (is_joystick)
1044 lpddi->wUsage = 0x04; /* Joystick */
1045 else
1046 lpddi->wUsage = 0x05; /* Game Pad */
1047 sprintf(friendly, "Joystick %d", id);
1048 MultiByteToWideChar(CP_ACP, 0, friendly, -1, lpddi->tszInstanceName, MAX_PATH);
1049
1050 /* get the device name */
1051 get_osx_device_name(id, name, MAX_PATH);
1052
1053 MultiByteToWideChar(CP_ACP, 0, name, -1, lpddi->tszProductName, MAX_PATH);
1054 lpddi->guidFFDriver = GUID_NULL;
1055 return S_OK;
1056 }
1057
1058 return S_FALSE;
1059 }
1060
osx_ff_axis_name(UInt8 axis)1061 static const char *osx_ff_axis_name(UInt8 axis)
1062 {
1063 static char ret[6];
1064 switch(axis){
1065 case FFJOFS_X:
1066 return "FFJOFS_X";
1067 case FFJOFS_Y:
1068 return "FFJOFS_Y";
1069 case FFJOFS_Z:
1070 return "FFJOFS_Z";
1071 }
1072 sprintf(ret, "%u", (unsigned int)axis);
1073 return ret;
1074 }
1075
osx_axis_has_ff(FFCAPABILITIES * ffcaps,UInt8 axis)1076 static BOOL osx_axis_has_ff(FFCAPABILITIES *ffcaps, UInt8 axis)
1077 {
1078 int i;
1079 for(i = 0; i < ffcaps->numFfAxes; ++i)
1080 if(ffcaps->ffAxes[i] == axis)
1081 return TRUE;
1082 return FALSE;
1083 }
1084
alloc_device(REFGUID rguid,IDirectInputImpl * dinput,JoystickImpl ** pdev,unsigned short index)1085 static HRESULT alloc_device(REFGUID rguid, IDirectInputImpl *dinput,
1086 JoystickImpl **pdev, unsigned short index)
1087 {
1088 DWORD i;
1089 IOHIDDeviceRef device;
1090 JoystickImpl* newDevice;
1091 char name[MAX_PATH];
1092 HRESULT hr;
1093 LPDIDATAFORMAT df = NULL;
1094 int idx = 0;
1095 int axis_map[8]; /* max axes */
1096 int slider_count = 0;
1097 FFCAPABILITIES ffcaps;
1098
1099 TRACE("%s %p %p %hu\n", debugstr_guid(rguid), dinput, pdev, index);
1100
1101 newDevice = HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(JoystickImpl));
1102 if (newDevice == 0) {
1103 WARN("out of memory\n");
1104 *pdev = 0;
1105 return DIERR_OUTOFMEMORY;
1106 }
1107
1108 newDevice->id = index;
1109
1110 device = get_device_ref(index);
1111
1112 newDevice->generic.guidInstance = DInput_Wine_OsX_Joystick_GUID;
1113 newDevice->generic.guidInstance.Data3 = index;
1114 newDevice->generic.guidProduct = DInput_PIDVID_Product_GUID;
1115 newDevice->generic.guidProduct.Data1 = make_vid_pid(device);
1116 newDevice->generic.joy_polldev = poll_osx_device_state;
1117
1118 /* get the device name */
1119 get_osx_device_name(index, name, MAX_PATH);
1120 TRACE("Name %s\n",name);
1121
1122 /* copy the device name */
1123 newDevice->generic.name = HeapAlloc(GetProcessHeap(),0,strlen(name) + 1);
1124 strcpy(newDevice->generic.name, name);
1125
1126 list_init(&newDevice->effects);
1127 if(get_ff(device, &newDevice->ff) == S_OK){
1128 newDevice->generic.devcaps.dwFlags |= DIDC_FORCEFEEDBACK;
1129
1130 hr = FFDeviceGetForceFeedbackCapabilities(newDevice->ff, &ffcaps);
1131 if(SUCCEEDED(hr)){
1132 TRACE("FF Capabilities:\n");
1133 TRACE("\tsupportedEffects: 0x%x\n", (unsigned int)ffcaps.supportedEffects);
1134 TRACE("\temulatedEffects: 0x%x\n", (unsigned int)ffcaps.emulatedEffects);
1135 TRACE("\tsubType: 0x%x\n", (unsigned int)ffcaps.subType);
1136 TRACE("\tnumFfAxes: %u\n", (unsigned int)ffcaps.numFfAxes);
1137 TRACE("\tffAxes: [");
1138 for(i = 0; i < ffcaps.numFfAxes; ++i){
1139 TRACE("%s", osx_ff_axis_name(ffcaps.ffAxes[i]));
1140 if(i < ffcaps.numFfAxes - 1)
1141 TRACE(", ");
1142 }
1143 TRACE("]\n");
1144 TRACE("\tstorageCapacity: %u\n", (unsigned int)ffcaps.storageCapacity);
1145 TRACE("\tplaybackCapacity: %u\n", (unsigned int)ffcaps.playbackCapacity);
1146 }
1147
1148 hr = FFDeviceSendForceFeedbackCommand(newDevice->ff, FFSFFC_RESET);
1149 if(FAILED(hr))
1150 WARN("FFDeviceSendForceFeedbackCommand(FFSFFC_RESET) failed: %08x\n", hr);
1151
1152 hr = FFDeviceSendForceFeedbackCommand(newDevice->ff, FFSFFC_SETACTUATORSON);
1153 if(FAILED(hr))
1154 WARN("FFDeviceSendForceFeedbackCommand(FFSFFC_SETACTUATORSON) failed: %08x\n", hr);
1155 }
1156
1157 memset(axis_map, 0, sizeof(axis_map));
1158 get_osx_device_elements(newDevice, axis_map);
1159
1160 TRACE("%i axes %i buttons %i povs\n",newDevice->generic.devcaps.dwAxes,newDevice->generic.devcaps.dwButtons,newDevice->generic.devcaps.dwPOVs);
1161
1162 if (newDevice->generic.devcaps.dwButtons > 128)
1163 {
1164 WARN("Can't support %d buttons. Clamping down to 128\n", newDevice->generic.devcaps.dwButtons);
1165 newDevice->generic.devcaps.dwButtons = 128;
1166 }
1167
1168 newDevice->generic.base.IDirectInputDevice8A_iface.lpVtbl = &JoystickAvt;
1169 newDevice->generic.base.IDirectInputDevice8W_iface.lpVtbl = &JoystickWvt;
1170 newDevice->generic.base.ref = 1;
1171 newDevice->generic.base.dinput = dinput;
1172 newDevice->generic.base.guid = *rguid;
1173 InitializeCriticalSection(&newDevice->generic.base.crit);
1174 newDevice->generic.base.crit.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": JoystickImpl*->generic.base.crit");
1175
1176 /* Create copy of default data format */
1177 if (!(df = HeapAlloc(GetProcessHeap(), 0, c_dfDIJoystick2.dwSize))) goto FAILED;
1178 memcpy(df, &c_dfDIJoystick2, c_dfDIJoystick2.dwSize);
1179
1180 df->dwNumObjs = newDevice->generic.devcaps.dwAxes + newDevice->generic.devcaps.dwPOVs + newDevice->generic.devcaps.dwButtons;
1181 if (!(df->rgodf = HeapAlloc(GetProcessHeap(), 0, df->dwNumObjs * df->dwObjSize))) goto FAILED;
1182
1183 for (i = 0; i < newDevice->generic.devcaps.dwAxes; i++)
1184 {
1185 int wine_obj = -1;
1186 BOOL has_ff = FALSE;
1187 switch (axis_map[i])
1188 {
1189 case kHIDUsage_GD_X:
1190 wine_obj = 0;
1191 has_ff = (newDevice->ff != 0) && osx_axis_has_ff(&ffcaps, FFJOFS_X);
1192 break;
1193 case kHIDUsage_GD_Y:
1194 wine_obj = 1;
1195 has_ff = (newDevice->ff != 0) && osx_axis_has_ff(&ffcaps, FFJOFS_Y);
1196 break;
1197 case kHIDUsage_GD_Z:
1198 wine_obj = 2;
1199 has_ff = (newDevice->ff != 0) && osx_axis_has_ff(&ffcaps, FFJOFS_Z);
1200 break;
1201 case kHIDUsage_GD_Rx:
1202 wine_obj = 3;
1203 has_ff = (newDevice->ff != 0) && osx_axis_has_ff(&ffcaps, FFJOFS_RX);
1204 break;
1205 case kHIDUsage_GD_Ry:
1206 wine_obj = 4;
1207 has_ff = (newDevice->ff != 0) && osx_axis_has_ff(&ffcaps, FFJOFS_RY);
1208 break;
1209 case kHIDUsage_GD_Rz:
1210 wine_obj = 5;
1211 has_ff = (newDevice->ff != 0) && osx_axis_has_ff(&ffcaps, FFJOFS_RZ);
1212 break;
1213 case kHIDUsage_GD_Slider:
1214 wine_obj = 6 + slider_count;
1215 has_ff = (newDevice->ff != 0) && osx_axis_has_ff(&ffcaps, FFJOFS_SLIDER(slider_count));
1216 slider_count++;
1217 break;
1218 }
1219 if (wine_obj < 0 ) continue;
1220
1221 memcpy(&df->rgodf[idx], &c_dfDIJoystick2.rgodf[wine_obj], df->dwObjSize);
1222 df->rgodf[idx].dwType = DIDFT_MAKEINSTANCE(wine_obj) | DIDFT_ABSAXIS;
1223 if(has_ff)
1224 df->rgodf[idx].dwFlags |= DIDOI_FFACTUATOR;
1225 ++idx;
1226 }
1227
1228 for (i = 0; i < newDevice->generic.devcaps.dwPOVs; i++)
1229 {
1230 memcpy(&df->rgodf[idx], &c_dfDIJoystick2.rgodf[i + 8], df->dwObjSize);
1231 df->rgodf[idx++].dwType = DIDFT_MAKEINSTANCE(i) | DIDFT_POV;
1232 }
1233
1234 for (i = 0; i < newDevice->generic.devcaps.dwButtons; i++)
1235 {
1236 memcpy(&df->rgodf[idx], &c_dfDIJoystick2.rgodf[i + 12], df->dwObjSize);
1237 df->rgodf[idx ].pguid = &GUID_Button;
1238 df->rgodf[idx++].dwType = DIDFT_MAKEINSTANCE(i) | DIDFT_PSHBUTTON;
1239 }
1240 newDevice->generic.base.data_format.wine_df = df;
1241
1242 /* initialize default properties */
1243 get_osx_device_elements_props(newDevice);
1244
1245 IDirectInput_AddRef(&newDevice->generic.base.dinput->IDirectInput7A_iface);
1246
1247 EnterCriticalSection(&dinput->crit);
1248 list_add_tail(&dinput->devices_list, &newDevice->generic.base.entry);
1249 LeaveCriticalSection(&dinput->crit);
1250
1251 newDevice->generic.devcaps.dwSize = sizeof(newDevice->generic.devcaps);
1252 newDevice->generic.devcaps.dwFlags |= DIDC_ATTACHED;
1253 if (newDevice->generic.base.dinput->dwVersion >= 0x0800)
1254 newDevice->generic.devcaps.dwDevType = DI8DEVTYPE_JOYSTICK | (DI8DEVTYPEJOYSTICK_STANDARD << 8);
1255 else
1256 newDevice->generic.devcaps.dwDevType = DIDEVTYPE_JOYSTICK | (DIDEVTYPEJOYSTICK_TRADITIONAL << 8);
1257 newDevice->generic.devcaps.dwFFSamplePeriod = 0;
1258 newDevice->generic.devcaps.dwFFMinTimeResolution = 0;
1259 newDevice->generic.devcaps.dwFirmwareRevision = 0;
1260 newDevice->generic.devcaps.dwHardwareRevision = 0;
1261 newDevice->generic.devcaps.dwFFDriverVersion = 0;
1262
1263 if (TRACE_ON(dinput)) {
1264 TRACE("allocated device %p\n", newDevice);
1265 _dump_DIDATAFORMAT(newDevice->generic.base.data_format.wine_df);
1266 _dump_DIDEVCAPS(&newDevice->generic.devcaps);
1267 }
1268
1269 *pdev = newDevice;
1270
1271 return DI_OK;
1272
1273 FAILED:
1274 hr = DIERR_OUTOFMEMORY;
1275 if (newDevice->ff) FFReleaseDevice(newDevice->ff);
1276 if (newDevice->elements) CFRelease(newDevice->elements);
1277 if (df) HeapFree(GetProcessHeap(), 0, df->rgodf);
1278 HeapFree(GetProcessHeap(), 0, df);
1279 release_DataFormat(&newDevice->generic.base.data_format);
1280 HeapFree(GetProcessHeap(),0,newDevice->generic.name);
1281 HeapFree(GetProcessHeap(),0,newDevice);
1282 *pdev = 0;
1283
1284 return hr;
1285 }
1286
1287 /******************************************************************************
1288 * get_joystick_index : Get the joystick index from a given GUID
1289 */
get_joystick_index(REFGUID guid)1290 static unsigned short get_joystick_index(REFGUID guid)
1291 {
1292 GUID wine_joystick = DInput_Wine_OsX_Joystick_GUID;
1293 GUID dev_guid = *guid;
1294
1295 wine_joystick.Data3 = 0;
1296 dev_guid.Data3 = 0;
1297
1298 /* for the standard joystick GUID use index 0 */
1299 if(IsEqualGUID(&GUID_Joystick,guid)) return 0;
1300
1301 /* for the wine joystick GUIDs use the index stored in Data3 */
1302 if(IsEqualGUID(&wine_joystick, &dev_guid)) return guid->Data3;
1303
1304 return 0xffff;
1305 }
1306
joydev_create_device(IDirectInputImpl * dinput,REFGUID rguid,REFIID riid,LPVOID * pdev,int unicode)1307 static HRESULT joydev_create_device(IDirectInputImpl *dinput, REFGUID rguid, REFIID riid, LPVOID *pdev, int unicode)
1308 {
1309 unsigned short index;
1310 int joystick_devices_count;
1311
1312 TRACE("%p %s %s %p %i\n", dinput, debugstr_guid(rguid), debugstr_guid(riid), pdev, unicode);
1313 *pdev = NULL;
1314
1315 if ((joystick_devices_count = find_joystick_devices()) == 0)
1316 return DIERR_DEVICENOTREG;
1317
1318 if ((index = get_joystick_index(rguid)) < 0xffff &&
1319 joystick_devices_count && index < joystick_devices_count)
1320 {
1321 JoystickImpl *This;
1322 HRESULT hr;
1323
1324 if (riid == NULL)
1325 ;/* nothing */
1326 else if (IsEqualGUID(&IID_IDirectInputDeviceA, riid) ||
1327 IsEqualGUID(&IID_IDirectInputDevice2A, riid) ||
1328 IsEqualGUID(&IID_IDirectInputDevice7A, riid) ||
1329 IsEqualGUID(&IID_IDirectInputDevice8A, riid))
1330 {
1331 unicode = 0;
1332 }
1333 else if (IsEqualGUID(&IID_IDirectInputDeviceW, riid) ||
1334 IsEqualGUID(&IID_IDirectInputDevice2W, riid) ||
1335 IsEqualGUID(&IID_IDirectInputDevice7W, riid) ||
1336 IsEqualGUID(&IID_IDirectInputDevice8W, riid))
1337 {
1338 unicode = 1;
1339 }
1340 else
1341 {
1342 WARN("no interface\n");
1343 return DIERR_NOINTERFACE;
1344 }
1345
1346 hr = alloc_device(rguid, dinput, &This, index);
1347 if (!This) return hr;
1348
1349 if (unicode)
1350 *pdev = &This->generic.base.IDirectInputDevice8W_iface;
1351 else
1352 *pdev = &This->generic.base.IDirectInputDevice8A_iface;
1353 return hr;
1354 }
1355
1356 return DIERR_DEVICENOTREG;
1357 }
1358
JoystickWImpl_GetProperty(LPDIRECTINPUTDEVICE8W iface,REFGUID rguid,LPDIPROPHEADER pdiph)1359 static HRESULT WINAPI JoystickWImpl_GetProperty(LPDIRECTINPUTDEVICE8W iface, REFGUID rguid, LPDIPROPHEADER pdiph)
1360 {
1361 JoystickImpl *This = impl_from_IDirectInputDevice8W(iface);
1362
1363 TRACE("(%p)->(%s,%p)\n", This, debugstr_guid(rguid), pdiph);
1364 _dump_DIPROPHEADER(pdiph);
1365
1366 if (!IS_DIPROP(rguid)) return DI_OK;
1367
1368 switch (LOWORD(rguid)) {
1369 case (DWORD_PTR) DIPROP_GUIDANDPATH:
1370 {
1371 static const WCHAR formatW[] = {'\\','\\','?','\\','h','i','d','#','v','i','d','_','%','0','4','x','&',
1372 'p','i','d','_','%','0','4','x','&','%','s','_','%','i',0};
1373 static const WCHAR miW[] = {'m','i',0};
1374 static const WCHAR igW[] = {'i','g',0};
1375
1376 BOOL is_gamepad;
1377 IOHIDDeviceRef device = get_device_ref(This->id);
1378 LPDIPROPGUIDANDPATH pd = (LPDIPROPGUIDANDPATH)pdiph;
1379 WORD vid = get_device_property_long(device, CFSTR(kIOHIDVendorIDKey));
1380 WORD pid = get_device_property_long(device, CFSTR(kIOHIDProductIDKey));
1381
1382 if (!pid || !vid)
1383 return DIERR_UNSUPPORTED;
1384
1385 is_gamepad = is_xinput_device(&This->generic.devcaps, vid, pid);
1386 pd->guidClass = GUID_DEVCLASS_HIDCLASS;
1387 sprintfW(pd->wszPath, formatW, vid, pid, is_gamepad ? igW : miW, This->id);
1388
1389 TRACE("DIPROP_GUIDANDPATH(%s, %s): returning fake path\n", debugstr_guid(&pd->guidClass), debugstr_w(pd->wszPath));
1390 break;
1391 }
1392
1393 default:
1394 return JoystickWGenericImpl_GetProperty(iface, rguid, pdiph);
1395 }
1396
1397 return DI_OK;
1398 }
1399
JoystickAImpl_GetProperty(LPDIRECTINPUTDEVICE8A iface,REFGUID rguid,LPDIPROPHEADER pdiph)1400 static HRESULT WINAPI JoystickAImpl_GetProperty(LPDIRECTINPUTDEVICE8A iface, REFGUID rguid, LPDIPROPHEADER pdiph)
1401 {
1402 JoystickImpl *This = impl_from_IDirectInputDevice8A(iface);
1403 return JoystickWImpl_GetProperty(IDirectInputDevice8W_from_impl(This), rguid, pdiph);
1404 }
1405
osx_set_autocenter(JoystickImpl * This,const DIPROPDWORD * header)1406 static HRESULT osx_set_autocenter(JoystickImpl *This,
1407 const DIPROPDWORD *header)
1408 {
1409 UInt32 v;
1410 HRESULT hr;
1411 if(!This->ff)
1412 return DIERR_UNSUPPORTED;
1413 v = header->dwData;
1414 hr = osx_to_win32_hresult(FFDeviceSetForceFeedbackProperty(This->ff, FFPROP_AUTOCENTER, &v));
1415 TRACE("returning: %08x\n", hr);
1416 return hr;
1417 }
1418
osx_set_ffgain(JoystickImpl * This,const DIPROPDWORD * header)1419 static HRESULT osx_set_ffgain(JoystickImpl *This, const DIPROPDWORD *header)
1420 {
1421 UInt32 v;
1422 HRESULT hr;
1423 if(!This->ff)
1424 return DIERR_UNSUPPORTED;
1425 v = header->dwData;
1426 hr = osx_to_win32_hresult(FFDeviceSetForceFeedbackProperty(This->ff, FFPROP_FFGAIN, &v));
1427 TRACE("returning: %08x\n", hr);
1428 return hr;
1429 }
1430
JoystickWImpl_SetProperty(IDirectInputDevice8W * iface,const GUID * prop,const DIPROPHEADER * header)1431 static HRESULT WINAPI JoystickWImpl_SetProperty(IDirectInputDevice8W *iface,
1432 const GUID *prop, const DIPROPHEADER *header)
1433 {
1434 JoystickImpl *This = impl_from_IDirectInputDevice8W(iface);
1435
1436 TRACE("%p %s %p\n", This, debugstr_guid(prop), header);
1437
1438 switch(LOWORD(prop))
1439 {
1440 case (DWORD_PTR)DIPROP_AUTOCENTER:
1441 return osx_set_autocenter(This, (const DIPROPDWORD *)header);
1442 case (DWORD_PTR)DIPROP_FFGAIN:
1443 return osx_set_ffgain(This, (const DIPROPDWORD *)header);
1444 }
1445
1446 return JoystickWGenericImpl_SetProperty(iface, prop, header);
1447 }
1448
JoystickAImpl_SetProperty(IDirectInputDevice8A * iface,const GUID * prop,const DIPROPHEADER * header)1449 static HRESULT WINAPI JoystickAImpl_SetProperty(IDirectInputDevice8A *iface,
1450 const GUID *prop, const DIPROPHEADER *header)
1451 {
1452 JoystickImpl *This = impl_from_IDirectInputDevice8A(iface);
1453
1454 TRACE("%p %s %p\n", This, debugstr_guid(prop), header);
1455
1456 switch(LOWORD(prop))
1457 {
1458 case (DWORD_PTR)DIPROP_AUTOCENTER:
1459 return osx_set_autocenter(This, (const DIPROPDWORD *)header);
1460 case (DWORD_PTR)DIPROP_FFGAIN:
1461 return osx_set_ffgain(This, (const DIPROPDWORD *)header);
1462 }
1463
1464 return JoystickAGenericImpl_SetProperty(iface, prop, header);
1465 }
1466
effect_win_to_mac(const GUID * effect)1467 static CFUUIDRef effect_win_to_mac(const GUID *effect)
1468 {
1469 #define DO_MAP(X) \
1470 if(IsEqualGUID(&GUID_##X, effect)) \
1471 return kFFEffectType_##X##_ID;
1472 DO_MAP(ConstantForce)
1473 DO_MAP(RampForce)
1474 DO_MAP(Square)
1475 DO_MAP(Sine)
1476 DO_MAP(Triangle)
1477 DO_MAP(SawtoothUp)
1478 DO_MAP(SawtoothDown)
1479 DO_MAP(Spring)
1480 DO_MAP(Damper)
1481 DO_MAP(Inertia)
1482 DO_MAP(Friction)
1483 DO_MAP(CustomForce)
1484 #undef DO_MAP
1485 WARN("Unknown effect GUID! %s\n", debugstr_guid(effect));
1486 return 0;
1487 }
1488
JoystickWImpl_CreateEffect(IDirectInputDevice8W * iface,const GUID * type,const DIEFFECT * params,IDirectInputEffect ** out,IUnknown * outer)1489 static HRESULT WINAPI JoystickWImpl_CreateEffect(IDirectInputDevice8W *iface,
1490 const GUID *type, const DIEFFECT *params, IDirectInputEffect **out,
1491 IUnknown *outer)
1492 {
1493 JoystickImpl *This = impl_from_IDirectInputDevice8W(iface);
1494 EffectImpl *effect;
1495 HRESULT hr;
1496
1497 TRACE("(%p)->(%s %p %p %p)\n", This, debugstr_guid(type), params, out, outer);
1498 dump_DIEFFECT(params, type, 0);
1499
1500 if(!This->ff){
1501 TRACE("No force feedback support\n");
1502 *out = NULL;
1503 return DIERR_UNSUPPORTED;
1504 }
1505
1506 if(outer)
1507 WARN("aggregation not implemented\n");
1508
1509 effect = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*This));
1510 effect->IDirectInputEffect_iface.lpVtbl = &EffectVtbl;
1511 effect->ref = 1;
1512 effect->guid = *type;
1513 effect->device = This;
1514
1515 /* Mac's FFEFFECT and Win's DIEFFECT are binary identical. */
1516 hr = osx_to_win32_hresult(FFDeviceCreateEffect(This->ff,
1517 effect_win_to_mac(type), (FFEFFECT*)params, &effect->effect));
1518 if(FAILED(hr)){
1519 WARN("FFDeviceCreateEffect failed: %08x\n", hr);
1520 HeapFree(GetProcessHeap(), 0, effect);
1521 return hr;
1522 }
1523
1524 list_add_tail(&This->effects, &effect->entry);
1525 *out = &effect->IDirectInputEffect_iface;
1526
1527 TRACE("allocated effect: %p\n", effect);
1528
1529 return S_OK;
1530 }
1531
JoystickAImpl_CreateEffect(IDirectInputDevice8A * iface,const GUID * type,const DIEFFECT * params,IDirectInputEffect ** out,IUnknown * outer)1532 static HRESULT WINAPI JoystickAImpl_CreateEffect(IDirectInputDevice8A *iface,
1533 const GUID *type, const DIEFFECT *params, IDirectInputEffect **out,
1534 IUnknown *outer)
1535 {
1536 JoystickImpl *This = impl_from_IDirectInputDevice8A(iface);
1537
1538 TRACE("(%p)->(%s %p %p %p)\n", This, debugstr_guid(type), params, out, outer);
1539
1540 return JoystickWImpl_CreateEffect(&This->generic.base.IDirectInputDevice8W_iface,
1541 type, params, out, outer);
1542 }
1543
JoystickWImpl_SendForceFeedbackCommand(IDirectInputDevice8W * iface,DWORD flags)1544 static HRESULT WINAPI JoystickWImpl_SendForceFeedbackCommand(IDirectInputDevice8W *iface,
1545 DWORD flags)
1546 {
1547 JoystickImpl *This = impl_from_IDirectInputDevice8W(iface);
1548 HRESULT hr;
1549
1550 TRACE("%p 0x%x\n", This, flags);
1551
1552 if(!This->ff)
1553 return DI_NOEFFECT;
1554
1555 hr = osx_to_win32_hresult(FFDeviceSendForceFeedbackCommand(This->ff, flags));
1556 if(FAILED(hr)){
1557 WARN("FFDeviceSendForceFeedbackCommand failed: %08x\n", hr);
1558 return hr;
1559 }
1560
1561 return S_OK;
1562 }
1563
JoystickAImpl_SendForceFeedbackCommand(IDirectInputDevice8A * iface,DWORD flags)1564 static HRESULT WINAPI JoystickAImpl_SendForceFeedbackCommand(IDirectInputDevice8A *iface,
1565 DWORD flags)
1566 {
1567 JoystickImpl *This = impl_from_IDirectInputDevice8A(iface);
1568
1569 TRACE("%p 0x%x\n", This, flags);
1570
1571 return JoystickWImpl_SendForceFeedbackCommand(&This->generic.base.IDirectInputDevice8W_iface, flags);
1572 }
1573
1574 const struct dinput_device joystick_osx_device = {
1575 "Wine OS X joystick driver",
1576 joydev_enum_deviceA,
1577 joydev_enum_deviceW,
1578 joydev_create_device
1579 };
1580
1581 static const IDirectInputDevice8AVtbl JoystickAvt =
1582 {
1583 IDirectInputDevice2AImpl_QueryInterface,
1584 IDirectInputDevice2AImpl_AddRef,
1585 IDirectInputDevice2AImpl_Release,
1586 JoystickAGenericImpl_GetCapabilities,
1587 IDirectInputDevice2AImpl_EnumObjects,
1588 JoystickAImpl_GetProperty,
1589 JoystickAImpl_SetProperty,
1590 IDirectInputDevice2AImpl_Acquire,
1591 IDirectInputDevice2AImpl_Unacquire,
1592 JoystickAGenericImpl_GetDeviceState,
1593 IDirectInputDevice2AImpl_GetDeviceData,
1594 IDirectInputDevice2AImpl_SetDataFormat,
1595 IDirectInputDevice2AImpl_SetEventNotification,
1596 IDirectInputDevice2AImpl_SetCooperativeLevel,
1597 JoystickAGenericImpl_GetObjectInfo,
1598 JoystickAGenericImpl_GetDeviceInfo,
1599 IDirectInputDevice2AImpl_RunControlPanel,
1600 IDirectInputDevice2AImpl_Initialize,
1601 JoystickAImpl_CreateEffect,
1602 IDirectInputDevice2AImpl_EnumEffects,
1603 IDirectInputDevice2AImpl_GetEffectInfo,
1604 IDirectInputDevice2AImpl_GetForceFeedbackState,
1605 JoystickAImpl_SendForceFeedbackCommand,
1606 IDirectInputDevice2AImpl_EnumCreatedEffectObjects,
1607 IDirectInputDevice2AImpl_Escape,
1608 JoystickAGenericImpl_Poll,
1609 IDirectInputDevice2AImpl_SendDeviceData,
1610 IDirectInputDevice7AImpl_EnumEffectsInFile,
1611 IDirectInputDevice7AImpl_WriteEffectToFile,
1612 JoystickAGenericImpl_BuildActionMap,
1613 JoystickAGenericImpl_SetActionMap,
1614 IDirectInputDevice8AImpl_GetImageInfo
1615 };
1616
1617 static const IDirectInputDevice8WVtbl JoystickWvt =
1618 {
1619 IDirectInputDevice2WImpl_QueryInterface,
1620 IDirectInputDevice2WImpl_AddRef,
1621 IDirectInputDevice2WImpl_Release,
1622 JoystickWGenericImpl_GetCapabilities,
1623 IDirectInputDevice2WImpl_EnumObjects,
1624 JoystickWImpl_GetProperty,
1625 JoystickWImpl_SetProperty,
1626 IDirectInputDevice2WImpl_Acquire,
1627 IDirectInputDevice2WImpl_Unacquire,
1628 JoystickWGenericImpl_GetDeviceState,
1629 IDirectInputDevice2WImpl_GetDeviceData,
1630 IDirectInputDevice2WImpl_SetDataFormat,
1631 IDirectInputDevice2WImpl_SetEventNotification,
1632 IDirectInputDevice2WImpl_SetCooperativeLevel,
1633 JoystickWGenericImpl_GetObjectInfo,
1634 JoystickWGenericImpl_GetDeviceInfo,
1635 IDirectInputDevice2WImpl_RunControlPanel,
1636 IDirectInputDevice2WImpl_Initialize,
1637 JoystickWImpl_CreateEffect,
1638 IDirectInputDevice2WImpl_EnumEffects,
1639 IDirectInputDevice2WImpl_GetEffectInfo,
1640 IDirectInputDevice2WImpl_GetForceFeedbackState,
1641 JoystickWImpl_SendForceFeedbackCommand,
1642 IDirectInputDevice2WImpl_EnumCreatedEffectObjects,
1643 IDirectInputDevice2WImpl_Escape,
1644 JoystickWGenericImpl_Poll,
1645 IDirectInputDevice2WImpl_SendDeviceData,
1646 IDirectInputDevice7WImpl_EnumEffectsInFile,
1647 IDirectInputDevice7WImpl_WriteEffectToFile,
1648 JoystickWGenericImpl_BuildActionMap,
1649 JoystickWGenericImpl_SetActionMap,
1650 IDirectInputDevice8WImpl_GetImageInfo
1651 };
1652
effect_QueryInterface(IDirectInputEffect * iface,const GUID * guid,void ** out)1653 static HRESULT WINAPI effect_QueryInterface(IDirectInputEffect *iface,
1654 const GUID *guid, void **out)
1655 {
1656 EffectImpl *This = impl_from_IDirectInputEffect(iface);
1657
1658 TRACE("%p %s %p\n", This, debugstr_guid(guid), out);
1659
1660 if(IsEqualIID(guid, &IID_IUnknown) || IsEqualIID(guid, &IID_IDirectInputEffect)){
1661 *out = iface;
1662 IDirectInputEffect_AddRef(iface);
1663 return S_OK;
1664 }
1665
1666 return E_NOINTERFACE;
1667 }
1668
effect_AddRef(IDirectInputEffect * iface)1669 static ULONG WINAPI effect_AddRef(IDirectInputEffect *iface)
1670 {
1671 EffectImpl *This = impl_from_IDirectInputEffect(iface);
1672 ULONG ref = InterlockedIncrement(&This->ref);
1673 TRACE("%p, ref is now: %u\n", This, ref);
1674 return ref;
1675 }
1676
effect_Release(IDirectInputEffect * iface)1677 static ULONG WINAPI effect_Release(IDirectInputEffect *iface)
1678 {
1679 EffectImpl *This = impl_from_IDirectInputEffect(iface);
1680 ULONG ref = InterlockedDecrement(&This->ref);
1681 TRACE("%p, ref is now: %u\n", This, ref);
1682
1683 if(!ref){
1684 list_remove(&This->entry);
1685 FFDeviceReleaseEffect(This->device->ff, This->effect);
1686 HeapFree(GetProcessHeap(), 0, This);
1687 }
1688
1689 return ref;
1690 }
1691
effect_Initialize(IDirectInputEffect * iface,HINSTANCE hinst,DWORD version,const GUID * guid)1692 static HRESULT WINAPI effect_Initialize(IDirectInputEffect *iface, HINSTANCE hinst,
1693 DWORD version, const GUID *guid)
1694 {
1695 EffectImpl *This = impl_from_IDirectInputEffect(iface);
1696 TRACE("%p %p 0x%x, %s\n", This, hinst, version, debugstr_guid(guid));
1697 return S_OK;
1698 }
1699
effect_GetEffectGuid(IDirectInputEffect * iface,GUID * out)1700 static HRESULT WINAPI effect_GetEffectGuid(IDirectInputEffect *iface, GUID *out)
1701 {
1702 EffectImpl *This = impl_from_IDirectInputEffect(iface);
1703 TRACE("%p %p\n", This, out);
1704 *out = This->guid;
1705 return S_OK;
1706 }
1707
effect_GetParameters(IDirectInputEffect * iface,DIEFFECT * effect,DWORD flags)1708 static HRESULT WINAPI effect_GetParameters(IDirectInputEffect *iface,
1709 DIEFFECT *effect, DWORD flags)
1710 {
1711 EffectImpl *This = impl_from_IDirectInputEffect(iface);
1712 TRACE("%p %p 0x%x\n", This, effect, flags);
1713 return osx_to_win32_hresult(FFEffectGetParameters(This->effect, (FFEFFECT*)effect, flags));
1714 }
1715
effect_SetParameters(IDirectInputEffect * iface,const DIEFFECT * effect,DWORD flags)1716 static HRESULT WINAPI effect_SetParameters(IDirectInputEffect *iface,
1717 const DIEFFECT *effect, DWORD flags)
1718 {
1719 EffectImpl *This = impl_from_IDirectInputEffect(iface);
1720 TRACE("%p %p 0x%x\n", This, effect, flags);
1721 dump_DIEFFECT(effect, &This->guid, flags);
1722 return osx_to_win32_hresult(FFEffectSetParameters(This->effect, (FFEFFECT*)effect, flags));
1723 }
1724
effect_Start(IDirectInputEffect * iface,DWORD iterations,DWORD flags)1725 static HRESULT WINAPI effect_Start(IDirectInputEffect *iface, DWORD iterations,
1726 DWORD flags)
1727 {
1728 EffectImpl *This = impl_from_IDirectInputEffect(iface);
1729 TRACE("%p 0x%x 0x%x\n", This, iterations, flags);
1730 return osx_to_win32_hresult(FFEffectStart(This->effect, iterations, flags));
1731 }
1732
effect_Stop(IDirectInputEffect * iface)1733 static HRESULT WINAPI effect_Stop(IDirectInputEffect *iface)
1734 {
1735 EffectImpl *This = impl_from_IDirectInputEffect(iface);
1736 TRACE("%p\n", This);
1737 return osx_to_win32_hresult(FFEffectStop(This->effect));
1738 }
1739
effect_GetEffectStatus(IDirectInputEffect * iface,DWORD * flags)1740 static HRESULT WINAPI effect_GetEffectStatus(IDirectInputEffect *iface, DWORD *flags)
1741 {
1742 EffectImpl *This = impl_from_IDirectInputEffect(iface);
1743 TRACE("%p %p\n", This, flags);
1744 return osx_to_win32_hresult(FFEffectGetEffectStatus(This->effect, (UInt32*)flags));
1745 }
1746
effect_Download(IDirectInputEffect * iface)1747 static HRESULT WINAPI effect_Download(IDirectInputEffect *iface)
1748 {
1749 EffectImpl *This = impl_from_IDirectInputEffect(iface);
1750 TRACE("%p\n", This);
1751 return osx_to_win32_hresult(FFEffectDownload(This->effect));
1752 }
1753
effect_Unload(IDirectInputEffect * iface)1754 static HRESULT WINAPI effect_Unload(IDirectInputEffect *iface)
1755 {
1756 EffectImpl *This = impl_from_IDirectInputEffect(iface);
1757 TRACE("%p\n", This);
1758 return osx_to_win32_hresult(FFEffectUnload(This->effect));
1759 }
1760
effect_Escape(IDirectInputEffect * iface,DIEFFESCAPE * escape)1761 static HRESULT WINAPI effect_Escape(IDirectInputEffect *iface, DIEFFESCAPE *escape)
1762 {
1763 EffectImpl *This = impl_from_IDirectInputEffect(iface);
1764 TRACE("%p %p\n", This, escape);
1765 return osx_to_win32_hresult(FFEffectEscape(This->effect, (FFEFFESCAPE*)escape));
1766 }
1767
1768 static const IDirectInputEffectVtbl EffectVtbl = {
1769 effect_QueryInterface,
1770 effect_AddRef,
1771 effect_Release,
1772 effect_Initialize,
1773 effect_GetEffectGuid,
1774 effect_GetParameters,
1775 effect_SetParameters,
1776 effect_Start,
1777 effect_Stop,
1778 effect_GetEffectStatus,
1779 effect_Download,
1780 effect_Unload,
1781 effect_Escape
1782 };
1783
1784 #else /* HAVE_IOHIDMANAGERCREATE */
1785
1786 const struct dinput_device joystick_osx_device = {
1787 "Wine OS X joystick driver",
1788 NULL,
1789 NULL,
1790 NULL
1791 };
1792
1793 #endif /* HAVE_IOHIDMANAGERCREATE */
1794