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