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