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