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