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