SDL_syshaptic.c (40911B)
1 /* 2 Simple DirectMedia Layer 3 Copyright (C) 1997-2020 Sam Lantinga <slouken@libsdl.org> 4 5 This software is provided 'as-is', without any express or implied 6 warranty. In no event will the authors be held liable for any damages 7 arising from the use of this software. 8 9 Permission is granted to anyone to use this software for any purpose, 10 including commercial applications, and to alter it and redistribute it 11 freely, subject to the following restrictions: 12 13 1. The origin of this software must not be misrepresented; you must not 14 claim that you wrote the original software. If you use this software 15 in a product, an acknowledgment in the product documentation would be 16 appreciated but is not required. 17 2. Altered source versions must be plainly marked as such, and must not be 18 misrepresented as being the original software. 19 3. This notice may not be removed or altered from any source distribution. 20 */ 21 #include "../../SDL_internal.h" 22 23 #ifdef SDL_HAPTIC_IOKIT 24 25 #include "SDL_stdinc.h" 26 #include "SDL_haptic.h" 27 #include "../SDL_syshaptic.h" 28 #include "SDL_joystick.h" 29 #include "../../joystick/SDL_sysjoystick.h" /* For the real SDL_Joystick */ 30 #include "../../joystick/darwin/SDL_iokitjoystick_c.h" /* For joystick hwdata */ 31 #include "SDL_syshaptic_c.h" 32 33 #include <IOKit/IOKitLib.h> 34 #include <IOKit/hid/IOHIDKeys.h> 35 #include <IOKit/hid/IOHIDUsageTables.h> 36 #include <ForceFeedback/ForceFeedback.h> 37 #include <ForceFeedback/ForceFeedbackConstants.h> 38 39 #ifndef IO_OBJECT_NULL 40 #define IO_OBJECT_NULL ((io_service_t)0) 41 #endif 42 43 /* 44 * List of available haptic devices. 45 */ 46 typedef struct SDL_hapticlist_item 47 { 48 char name[256]; /* Name of the device. */ 49 50 io_service_t dev; /* Node we use to create the device. */ 51 SDL_Haptic *haptic; /* Haptic currently associated with it. */ 52 53 /* Usage pages for determining if it's a mouse or not. */ 54 long usage; 55 long usagePage; 56 57 struct SDL_hapticlist_item *next; 58 } SDL_hapticlist_item; 59 60 61 /* 62 * Haptic system hardware data. 63 */ 64 struct haptic_hwdata 65 { 66 FFDeviceObjectReference device; /* Hardware device. */ 67 UInt8 axes[3]; 68 }; 69 70 71 /* 72 * Haptic system effect data. 73 */ 74 struct haptic_hweffect 75 { 76 FFEffectObjectReference ref; /* Reference. */ 77 struct FFEFFECT effect; /* Hardware effect. */ 78 }; 79 80 /* 81 * Prototypes. 82 */ 83 static void SDL_SYS_HapticFreeFFEFFECT(FFEFFECT * effect, int type); 84 static int HIDGetDeviceProduct(io_service_t dev, char *name); 85 86 static SDL_hapticlist_item *SDL_hapticlist = NULL; 87 static SDL_hapticlist_item *SDL_hapticlist_tail = NULL; 88 static int numhaptics = -1; 89 90 /* 91 * Like strerror but for force feedback errors. 92 */ 93 static const char * 94 FFStrError(unsigned int err) 95 { 96 switch (err) { 97 case FFERR_DEVICEFULL: 98 return "device full"; 99 /* This should be valid, but for some reason isn't defined... */ 100 /* case FFERR_DEVICENOTREG: 101 return "device not registered"; */ 102 case FFERR_DEVICEPAUSED: 103 return "device paused"; 104 case FFERR_DEVICERELEASED: 105 return "device released"; 106 case FFERR_EFFECTPLAYING: 107 return "effect playing"; 108 case FFERR_EFFECTTYPEMISMATCH: 109 return "effect type mismatch"; 110 case FFERR_EFFECTTYPENOTSUPPORTED: 111 return "effect type not supported"; 112 case FFERR_GENERIC: 113 return "undetermined error"; 114 case FFERR_HASEFFECTS: 115 return "device has effects"; 116 case FFERR_INCOMPLETEEFFECT: 117 return "incomplete effect"; 118 case FFERR_INTERNAL: 119 return "internal fault"; 120 case FFERR_INVALIDDOWNLOADID: 121 return "invalid download id"; 122 case FFERR_INVALIDPARAM: 123 return "invalid parameter"; 124 case FFERR_MOREDATA: 125 return "more data"; 126 case FFERR_NOINTERFACE: 127 return "interface not supported"; 128 case FFERR_NOTDOWNLOADED: 129 return "effect is not downloaded"; 130 case FFERR_NOTINITIALIZED: 131 return "object has not been initialized"; 132 case FFERR_OUTOFMEMORY: 133 return "out of memory"; 134 case FFERR_UNPLUGGED: 135 return "device is unplugged"; 136 case FFERR_UNSUPPORTED: 137 return "function call unsupported"; 138 case FFERR_UNSUPPORTEDAXIS: 139 return "axis unsupported"; 140 141 default: 142 return "unknown error"; 143 } 144 } 145 146 147 /* 148 * Initializes the haptic subsystem. 149 */ 150 int 151 SDL_SYS_HapticInit(void) 152 { 153 IOReturn result; 154 io_iterator_t iter; 155 CFDictionaryRef match; 156 io_service_t device; 157 158 if (numhaptics != -1) { 159 return SDL_SetError("Haptic subsystem already initialized!"); 160 } 161 numhaptics = 0; 162 163 /* Get HID devices. */ 164 match = IOServiceMatching(kIOHIDDeviceKey); 165 if (match == NULL) { 166 return SDL_SetError("Haptic: Failed to get IOServiceMatching."); 167 } 168 169 /* Now search I/O Registry for matching devices. */ 170 result = IOServiceGetMatchingServices(kIOMasterPortDefault, match, &iter); 171 if (result != kIOReturnSuccess) { 172 return SDL_SetError("Haptic: Couldn't create a HID object iterator."); 173 } 174 /* IOServiceGetMatchingServices consumes dictionary. */ 175 176 if (!IOIteratorIsValid(iter)) { /* No iterator. */ 177 return 0; 178 } 179 180 while ((device = IOIteratorNext(iter)) != IO_OBJECT_NULL) { 181 MacHaptic_MaybeAddDevice(device); 182 /* always release as the AddDevice will retain IF it's a forcefeedback device */ 183 IOObjectRelease(device); 184 } 185 IOObjectRelease(iter); 186 187 return numhaptics; 188 } 189 190 int 191 SDL_SYS_NumHaptics(void) 192 { 193 return numhaptics; 194 } 195 196 static SDL_hapticlist_item * 197 HapticByDevIndex(int device_index) 198 { 199 SDL_hapticlist_item *item = SDL_hapticlist; 200 201 if ((device_index < 0) || (device_index >= numhaptics)) { 202 return NULL; 203 } 204 205 while (device_index > 0) { 206 SDL_assert(item != NULL); 207 --device_index; 208 item = item->next; 209 } 210 211 return item; 212 } 213 214 int 215 MacHaptic_MaybeAddDevice( io_object_t device ) 216 { 217 IOReturn result; 218 CFMutableDictionaryRef hidProperties; 219 CFTypeRef refCF; 220 SDL_hapticlist_item *item; 221 222 if (numhaptics == -1) { 223 return -1; /* not initialized. We'll pick these up on enumeration if we init later. */ 224 } 225 226 /* Check for force feedback. */ 227 if (FFIsForceFeedback(device) != FF_OK) { 228 return -1; 229 } 230 231 /* Make sure we don't already have it */ 232 for (item = SDL_hapticlist; item ; item = item->next) 233 { 234 if (IOObjectIsEqualTo((io_object_t) item->dev, device)) { 235 /* Already added */ 236 return -1; 237 } 238 } 239 240 item = (SDL_hapticlist_item *)SDL_calloc(1, sizeof(SDL_hapticlist_item)); 241 if (item == NULL) { 242 return SDL_SetError("Could not allocate haptic storage"); 243 } 244 245 /* retain it as we are going to keep it around a while */ 246 IOObjectRetain(device); 247 248 /* Set basic device data. */ 249 HIDGetDeviceProduct(device, item->name); 250 item->dev = device; 251 252 /* Set usage pages. */ 253 hidProperties = 0; 254 refCF = 0; 255 result = IORegistryEntryCreateCFProperties(device, 256 &hidProperties, 257 kCFAllocatorDefault, 258 kNilOptions); 259 if ((result == KERN_SUCCESS) && hidProperties) { 260 refCF = CFDictionaryGetValue(hidProperties, 261 CFSTR(kIOHIDPrimaryUsagePageKey)); 262 if (refCF) { 263 if (!CFNumberGetValue(refCF, kCFNumberLongType, &item->usagePage)) { 264 SDL_SetError("Haptic: Receiving device's usage page."); 265 } 266 refCF = CFDictionaryGetValue(hidProperties, 267 CFSTR(kIOHIDPrimaryUsageKey)); 268 if (refCF) { 269 if (!CFNumberGetValue(refCF, kCFNumberLongType, &item->usage)) { 270 SDL_SetError("Haptic: Receiving device's usage."); 271 } 272 } 273 } 274 CFRelease(hidProperties); 275 } 276 277 if (SDL_hapticlist_tail == NULL) { 278 SDL_hapticlist = SDL_hapticlist_tail = item; 279 } else { 280 SDL_hapticlist_tail->next = item; 281 SDL_hapticlist_tail = item; 282 } 283 284 /* Device has been added. */ 285 ++numhaptics; 286 287 return numhaptics; 288 } 289 290 int 291 MacHaptic_MaybeRemoveDevice( io_object_t device ) 292 { 293 SDL_hapticlist_item *item; 294 SDL_hapticlist_item *prev = NULL; 295 296 if (numhaptics == -1) { 297 return -1; /* not initialized. ignore this. */ 298 } 299 300 for (item = SDL_hapticlist; item != NULL; item = item->next) { 301 /* found it, remove it. */ 302 if (IOObjectIsEqualTo((io_object_t) item->dev, device)) { 303 const int retval = item->haptic ? item->haptic->index : -1; 304 305 if (prev != NULL) { 306 prev->next = item->next; 307 } else { 308 SDL_assert(SDL_hapticlist == item); 309 SDL_hapticlist = item->next; 310 } 311 if (item == SDL_hapticlist_tail) { 312 SDL_hapticlist_tail = prev; 313 } 314 315 /* Need to decrement the haptic count */ 316 --numhaptics; 317 /* !!! TODO: Send a haptic remove event? */ 318 319 IOObjectRelease(item->dev); 320 SDL_free(item); 321 return retval; 322 } 323 prev = item; 324 } 325 326 return -1; 327 } 328 329 /* 330 * Return the name of a haptic device, does not need to be opened. 331 */ 332 const char * 333 SDL_SYS_HapticName(int index) 334 { 335 SDL_hapticlist_item *item; 336 item = HapticByDevIndex(index); 337 return item->name; 338 } 339 340 /* 341 * Gets the device's product name. 342 */ 343 static int 344 HIDGetDeviceProduct(io_service_t dev, char *name) 345 { 346 CFMutableDictionaryRef hidProperties, usbProperties; 347 io_registry_entry_t parent1, parent2; 348 kern_return_t ret; 349 350 hidProperties = usbProperties = 0; 351 352 ret = IORegistryEntryCreateCFProperties(dev, &hidProperties, 353 kCFAllocatorDefault, kNilOptions); 354 if ((ret != KERN_SUCCESS) || !hidProperties) { 355 return SDL_SetError("Haptic: Unable to create CFProperties."); 356 } 357 358 /* Mac OS X currently is not mirroring all USB properties to HID page so need to look at USB device page also 359 * get dictionary for USB properties: step up two levels and get CF dictionary for USB properties 360 */ 361 if ((KERN_SUCCESS == 362 IORegistryEntryGetParentEntry(dev, kIOServicePlane, &parent1)) 363 && (KERN_SUCCESS == 364 IORegistryEntryGetParentEntry(parent1, kIOServicePlane, &parent2)) 365 && (KERN_SUCCESS == 366 IORegistryEntryCreateCFProperties(parent2, &usbProperties, 367 kCFAllocatorDefault, 368 kNilOptions))) { 369 if (usbProperties) { 370 CFTypeRef refCF = 0; 371 /* get device info 372 * try hid dictionary first, if fail then go to USB dictionary 373 */ 374 375 376 /* Get product name */ 377 refCF = CFDictionaryGetValue(hidProperties, CFSTR(kIOHIDProductKey)); 378 if (!refCF) { 379 refCF = CFDictionaryGetValue(usbProperties, 380 CFSTR("USB Product Name")); 381 } 382 if (refCF) { 383 if (!CFStringGetCString(refCF, name, 256, 384 CFStringGetSystemEncoding())) { 385 return SDL_SetError("Haptic: CFStringGetCString error retrieving pDevice->product."); 386 } 387 } 388 389 CFRelease(usbProperties); 390 } else { 391 return SDL_SetError("Haptic: IORegistryEntryCreateCFProperties failed to create usbProperties."); 392 } 393 394 /* Release stuff. */ 395 if (kIOReturnSuccess != IOObjectRelease(parent2)) { 396 SDL_SetError("Haptic: IOObjectRelease error with parent2."); 397 } 398 if (kIOReturnSuccess != IOObjectRelease(parent1)) { 399 SDL_SetError("Haptic: IOObjectRelease error with parent1."); 400 } 401 } else { 402 return SDL_SetError("Haptic: Error getting registry entries."); 403 } 404 405 return 0; 406 } 407 408 409 #define FF_TEST(ff, s) \ 410 if (features.supportedEffects & (ff)) supported |= (s) 411 /* 412 * Gets supported features. 413 */ 414 static unsigned int 415 GetSupportedFeatures(SDL_Haptic * haptic) 416 { 417 HRESULT ret; 418 FFDeviceObjectReference device; 419 FFCAPABILITIES features; 420 unsigned int supported; 421 Uint32 val; 422 423 device = haptic->hwdata->device; 424 425 ret = FFDeviceGetForceFeedbackCapabilities(device, &features); 426 if (ret != FF_OK) { 427 return SDL_SetError("Haptic: Unable to get device's supported features."); 428 } 429 430 supported = 0; 431 432 /* Get maximum effects. */ 433 haptic->neffects = features.storageCapacity; 434 haptic->nplaying = features.playbackCapacity; 435 436 /* Test for effects. */ 437 FF_TEST(FFCAP_ET_CONSTANTFORCE, SDL_HAPTIC_CONSTANT); 438 FF_TEST(FFCAP_ET_RAMPFORCE, SDL_HAPTIC_RAMP); 439 /* !!! FIXME: put this back when we have more bits in 2.1 */ 440 /* FF_TEST(FFCAP_ET_SQUARE, SDL_HAPTIC_SQUARE); */ 441 FF_TEST(FFCAP_ET_SINE, SDL_HAPTIC_SINE); 442 FF_TEST(FFCAP_ET_TRIANGLE, SDL_HAPTIC_TRIANGLE); 443 FF_TEST(FFCAP_ET_SAWTOOTHUP, SDL_HAPTIC_SAWTOOTHUP); 444 FF_TEST(FFCAP_ET_SAWTOOTHDOWN, SDL_HAPTIC_SAWTOOTHDOWN); 445 FF_TEST(FFCAP_ET_SPRING, SDL_HAPTIC_SPRING); 446 FF_TEST(FFCAP_ET_DAMPER, SDL_HAPTIC_DAMPER); 447 FF_TEST(FFCAP_ET_INERTIA, SDL_HAPTIC_INERTIA); 448 FF_TEST(FFCAP_ET_FRICTION, SDL_HAPTIC_FRICTION); 449 FF_TEST(FFCAP_ET_CUSTOMFORCE, SDL_HAPTIC_CUSTOM); 450 451 /* Check if supports gain. */ 452 ret = FFDeviceGetForceFeedbackProperty(device, FFPROP_FFGAIN, 453 &val, sizeof(val)); 454 if (ret == FF_OK) { 455 supported |= SDL_HAPTIC_GAIN; 456 } else if (ret != FFERR_UNSUPPORTED) { 457 return SDL_SetError("Haptic: Unable to get if device supports gain: %s.", 458 FFStrError(ret)); 459 } 460 461 /* Checks if supports autocenter. */ 462 ret = FFDeviceGetForceFeedbackProperty(device, FFPROP_AUTOCENTER, 463 &val, sizeof(val)); 464 if (ret == FF_OK) { 465 supported |= SDL_HAPTIC_AUTOCENTER; 466 } else if (ret != FFERR_UNSUPPORTED) { 467 return SDL_SetError 468 ("Haptic: Unable to get if device supports autocenter: %s.", 469 FFStrError(ret)); 470 } 471 472 /* Check for axes, we have an artificial limit on axes */ 473 haptic->naxes = ((features.numFfAxes) > 3) ? 3 : features.numFfAxes; 474 /* Actually store the axes we want to use */ 475 SDL_memcpy(haptic->hwdata->axes, features.ffAxes, 476 haptic->naxes * sizeof(Uint8)); 477 478 /* Always supported features. */ 479 supported |= SDL_HAPTIC_STATUS | SDL_HAPTIC_PAUSE; 480 481 haptic->supported = supported; 482 return 0; 483 } 484 485 486 /* 487 * Opens the haptic device from the file descriptor. 488 */ 489 static int 490 SDL_SYS_HapticOpenFromService(SDL_Haptic * haptic, io_service_t service) 491 { 492 HRESULT ret; 493 int ret2; 494 495 /* Allocate the hwdata */ 496 haptic->hwdata = (struct haptic_hwdata *) 497 SDL_malloc(sizeof(*haptic->hwdata)); 498 if (haptic->hwdata == NULL) { 499 SDL_OutOfMemory(); 500 goto creat_err; 501 } 502 SDL_memset(haptic->hwdata, 0, sizeof(*haptic->hwdata)); 503 504 /* Open the device */ 505 ret = FFCreateDevice(service, &haptic->hwdata->device); 506 if (ret != FF_OK) { 507 SDL_SetError("Haptic: Unable to create device from service: %s.", 508 FFStrError(ret)); 509 goto creat_err; 510 } 511 512 /* Get supported features. */ 513 ret2 = GetSupportedFeatures(haptic); 514 if (ret2 < 0) { 515 goto open_err; 516 } 517 518 519 /* Reset and then enable actuators. */ 520 ret = FFDeviceSendForceFeedbackCommand(haptic->hwdata->device, 521 FFSFFC_RESET); 522 if (ret != FF_OK) { 523 SDL_SetError("Haptic: Unable to reset device: %s.", FFStrError(ret)); 524 goto open_err; 525 } 526 ret = FFDeviceSendForceFeedbackCommand(haptic->hwdata->device, 527 FFSFFC_SETACTUATORSON); 528 if (ret != FF_OK) { 529 SDL_SetError("Haptic: Unable to enable actuators: %s.", 530 FFStrError(ret)); 531 goto open_err; 532 } 533 534 535 /* Allocate effects memory. */ 536 haptic->effects = (struct haptic_effect *) 537 SDL_malloc(sizeof(struct haptic_effect) * haptic->neffects); 538 if (haptic->effects == NULL) { 539 SDL_OutOfMemory(); 540 goto open_err; 541 } 542 /* Clear the memory */ 543 SDL_memset(haptic->effects, 0, 544 sizeof(struct haptic_effect) * haptic->neffects); 545 546 return 0; 547 548 /* Error handling */ 549 open_err: 550 FFReleaseDevice(haptic->hwdata->device); 551 creat_err: 552 if (haptic->hwdata != NULL) { 553 SDL_free(haptic->hwdata); 554 haptic->hwdata = NULL; 555 } 556 return -1; 557 558 } 559 560 561 /* 562 * Opens a haptic device for usage. 563 */ 564 int 565 SDL_SYS_HapticOpen(SDL_Haptic * haptic) 566 { 567 SDL_hapticlist_item *item; 568 item = HapticByDevIndex(haptic->index); 569 570 return SDL_SYS_HapticOpenFromService(haptic, item->dev); 571 } 572 573 574 /* 575 * Opens a haptic device from first mouse it finds for usage. 576 */ 577 int 578 SDL_SYS_HapticMouse(void) 579 { 580 int device_index = 0; 581 SDL_hapticlist_item *item; 582 583 for (item = SDL_hapticlist; item; item = item->next) { 584 if ((item->usagePage == kHIDPage_GenericDesktop) && 585 (item->usage == kHIDUsage_GD_Mouse)) { 586 return device_index; 587 } 588 ++device_index; 589 } 590 591 return -1; 592 } 593 594 595 /* 596 * Checks to see if a joystick has haptic features. 597 */ 598 int 599 SDL_SYS_JoystickIsHaptic(SDL_Joystick * joystick) 600 { 601 #ifdef SDL_JOYSTICK_IOKIT 602 if (joystick->driver != &SDL_DARWIN_JoystickDriver) { 603 return SDL_FALSE; 604 } 605 if (joystick->hwdata->ffservice != 0) { 606 return SDL_TRUE; 607 } 608 #endif 609 return SDL_FALSE; 610 } 611 612 613 /* 614 * Checks to see if the haptic device and joystick are in reality the same. 615 */ 616 int 617 SDL_SYS_JoystickSameHaptic(SDL_Haptic * haptic, SDL_Joystick * joystick) 618 { 619 #ifdef SDL_JOYSTICK_IOKIT 620 if (joystick->driver != &SDL_DARWIN_JoystickDriver) { 621 return 0; 622 } 623 if (IOObjectIsEqualTo((io_object_t) ((size_t)haptic->hwdata->device), 624 joystick->hwdata->ffservice)) { 625 return 1; 626 } 627 #endif 628 return 0; 629 } 630 631 632 /* 633 * Opens a SDL_Haptic from a SDL_Joystick. 634 */ 635 int 636 SDL_SYS_HapticOpenFromJoystick(SDL_Haptic * haptic, SDL_Joystick * joystick) 637 { 638 #ifdef SDL_JOYSTICK_IOKIT 639 int device_index = 0; 640 SDL_hapticlist_item *item; 641 642 if (joystick->driver != &SDL_DARWIN_JoystickDriver) { 643 return -1; 644 } 645 for (item = SDL_hapticlist; item; item = item->next) { 646 if (IOObjectIsEqualTo((io_object_t) item->dev, 647 joystick->hwdata->ffservice)) { 648 haptic->index = device_index; 649 break; 650 } 651 ++device_index; 652 } 653 654 return SDL_SYS_HapticOpenFromService(haptic, joystick->hwdata->ffservice); 655 #else 656 return -1; 657 #endif 658 } 659 660 661 /* 662 * Closes the haptic device. 663 */ 664 void 665 SDL_SYS_HapticClose(SDL_Haptic * haptic) 666 { 667 if (haptic->hwdata) { 668 669 /* Free Effects. */ 670 SDL_free(haptic->effects); 671 haptic->effects = NULL; 672 haptic->neffects = 0; 673 674 /* Clean up */ 675 FFReleaseDevice(haptic->hwdata->device); 676 677 /* Free */ 678 SDL_free(haptic->hwdata); 679 haptic->hwdata = NULL; 680 } 681 } 682 683 684 /* 685 * Clean up after system specific haptic stuff 686 */ 687 void 688 SDL_SYS_HapticQuit(void) 689 { 690 SDL_hapticlist_item *item; 691 SDL_hapticlist_item *next = NULL; 692 693 for (item = SDL_hapticlist; item; item = next) { 694 next = item->next; 695 /* Opened and not closed haptics are leaked, this is on purpose. 696 * Close your haptic devices after usage. */ 697 698 /* Free the io_service_t */ 699 IOObjectRelease(item->dev); 700 SDL_free(item); 701 } 702 703 numhaptics = -1; 704 SDL_hapticlist = NULL; 705 SDL_hapticlist_tail = NULL; 706 } 707 708 709 /* 710 * Converts an SDL trigger button to an FFEFFECT trigger button. 711 */ 712 static DWORD 713 FFGetTriggerButton(Uint16 button) 714 { 715 DWORD dwTriggerButton; 716 717 dwTriggerButton = FFEB_NOTRIGGER; 718 719 if (button != 0) { 720 dwTriggerButton = FFJOFS_BUTTON(button - 1); 721 } 722 723 return dwTriggerButton; 724 } 725 726 727 /* 728 * Sets the direction. 729 */ 730 static int 731 SDL_SYS_SetDirection(FFEFFECT * effect, SDL_HapticDirection * dir, int naxes) 732 { 733 LONG *rglDir; 734 735 /* Handle no axes a part. */ 736 if (naxes == 0) { 737 effect->dwFlags |= FFEFF_SPHERICAL; /* Set as default. */ 738 effect->rglDirection = NULL; 739 return 0; 740 } 741 742 /* Has axes. */ 743 rglDir = SDL_malloc(sizeof(LONG) * naxes); 744 if (rglDir == NULL) { 745 return SDL_OutOfMemory(); 746 } 747 SDL_memset(rglDir, 0, sizeof(LONG) * naxes); 748 effect->rglDirection = rglDir; 749 750 switch (dir->type) { 751 case SDL_HAPTIC_POLAR: 752 effect->dwFlags |= FFEFF_POLAR; 753 rglDir[0] = dir->dir[0]; 754 return 0; 755 case SDL_HAPTIC_CARTESIAN: 756 effect->dwFlags |= FFEFF_CARTESIAN; 757 rglDir[0] = dir->dir[0]; 758 if (naxes > 1) { 759 rglDir[1] = dir->dir[1]; 760 } 761 if (naxes > 2) { 762 rglDir[2] = dir->dir[2]; 763 } 764 return 0; 765 case SDL_HAPTIC_SPHERICAL: 766 effect->dwFlags |= FFEFF_SPHERICAL; 767 rglDir[0] = dir->dir[0]; 768 if (naxes > 1) { 769 rglDir[1] = dir->dir[1]; 770 } 771 if (naxes > 2) { 772 rglDir[2] = dir->dir[2]; 773 } 774 return 0; 775 case SDL_HAPTIC_STEERING_AXIS: 776 effect->dwFlags |= FFEFF_CARTESIAN; 777 rglDir[0] = 0; 778 return 0; 779 780 default: 781 return SDL_SetError("Haptic: Unknown direction type."); 782 } 783 } 784 785 786 /* Clamps and converts. */ 787 #define CCONVERT(x) (((x) > 0x7FFF) ? 10000 : ((x)*10000) / 0x7FFF) 788 /* Just converts. */ 789 #define CONVERT(x) (((x)*10000) / 0x7FFF) 790 /* 791 * Creates the FFEFFECT from a SDL_HapticEffect. 792 */ 793 static int 794 SDL_SYS_ToFFEFFECT(SDL_Haptic * haptic, FFEFFECT * dest, SDL_HapticEffect * src) 795 { 796 int i; 797 FFCONSTANTFORCE *constant = NULL; 798 FFPERIODIC *periodic = NULL; 799 FFCONDITION *condition = NULL; /* Actually an array of conditions - one per axis. */ 800 FFRAMPFORCE *ramp = NULL; 801 FFCUSTOMFORCE *custom = NULL; 802 FFENVELOPE *envelope = NULL; 803 SDL_HapticConstant *hap_constant = NULL; 804 SDL_HapticPeriodic *hap_periodic = NULL; 805 SDL_HapticCondition *hap_condition = NULL; 806 SDL_HapticRamp *hap_ramp = NULL; 807 SDL_HapticCustom *hap_custom = NULL; 808 DWORD *axes = NULL; 809 810 /* Set global stuff. */ 811 SDL_memset(dest, 0, sizeof(FFEFFECT)); 812 dest->dwSize = sizeof(FFEFFECT); /* Set the structure size. */ 813 dest->dwSamplePeriod = 0; /* Not used by us. */ 814 dest->dwGain = 10000; /* Gain is set globally, not locally. */ 815 dest->dwFlags = FFEFF_OBJECTOFFSETS; /* Seems obligatory. */ 816 817 /* Envelope. */ 818 envelope = SDL_malloc(sizeof(FFENVELOPE)); 819 if (envelope == NULL) { 820 return SDL_OutOfMemory(); 821 } 822 SDL_memset(envelope, 0, sizeof(FFENVELOPE)); 823 dest->lpEnvelope = envelope; 824 envelope->dwSize = sizeof(FFENVELOPE); /* Always should be this. */ 825 826 /* Axes. */ 827 if (src->constant.direction.type == SDL_HAPTIC_STEERING_AXIS) { 828 dest->cAxes = 1; 829 } else { 830 dest->cAxes = haptic->naxes; 831 } 832 if (dest->cAxes > 0) { 833 axes = SDL_malloc(sizeof(DWORD) * dest->cAxes); 834 if (axes == NULL) { 835 return SDL_OutOfMemory(); 836 } 837 axes[0] = haptic->hwdata->axes[0]; /* Always at least one axis. */ 838 if (dest->cAxes > 1) { 839 axes[1] = haptic->hwdata->axes[1]; 840 } 841 if (dest->cAxes > 2) { 842 axes[2] = haptic->hwdata->axes[2]; 843 } 844 dest->rgdwAxes = axes; 845 } 846 847 848 /* The big type handling switch, even bigger then Linux's version. */ 849 switch (src->type) { 850 case SDL_HAPTIC_CONSTANT: 851 hap_constant = &src->constant; 852 constant = SDL_malloc(sizeof(FFCONSTANTFORCE)); 853 if (constant == NULL) { 854 return SDL_OutOfMemory(); 855 } 856 SDL_memset(constant, 0, sizeof(FFCONSTANTFORCE)); 857 858 /* Specifics */ 859 constant->lMagnitude = CONVERT(hap_constant->level); 860 dest->cbTypeSpecificParams = sizeof(FFCONSTANTFORCE); 861 dest->lpvTypeSpecificParams = constant; 862 863 /* Generics */ 864 dest->dwDuration = hap_constant->length * 1000; /* In microseconds. */ 865 dest->dwTriggerButton = FFGetTriggerButton(hap_constant->button); 866 dest->dwTriggerRepeatInterval = hap_constant->interval; 867 dest->dwStartDelay = hap_constant->delay * 1000; /* In microseconds. */ 868 869 /* Direction. */ 870 if (SDL_SYS_SetDirection(dest, &hap_constant->direction, dest->cAxes) 871 < 0) { 872 return -1; 873 } 874 875 /* Envelope */ 876 if ((hap_constant->attack_length == 0) 877 && (hap_constant->fade_length == 0)) { 878 SDL_free(envelope); 879 dest->lpEnvelope = NULL; 880 } else { 881 envelope->dwAttackLevel = CCONVERT(hap_constant->attack_level); 882 envelope->dwAttackTime = hap_constant->attack_length * 1000; 883 envelope->dwFadeLevel = CCONVERT(hap_constant->fade_level); 884 envelope->dwFadeTime = hap_constant->fade_length * 1000; 885 } 886 887 break; 888 889 case SDL_HAPTIC_SINE: 890 /* !!! FIXME: put this back when we have more bits in 2.1 */ 891 /* case SDL_HAPTIC_SQUARE: */ 892 case SDL_HAPTIC_TRIANGLE: 893 case SDL_HAPTIC_SAWTOOTHUP: 894 case SDL_HAPTIC_SAWTOOTHDOWN: 895 hap_periodic = &src->periodic; 896 periodic = SDL_malloc(sizeof(FFPERIODIC)); 897 if (periodic == NULL) { 898 return SDL_OutOfMemory(); 899 } 900 SDL_memset(periodic, 0, sizeof(FFPERIODIC)); 901 902 /* Specifics */ 903 periodic->dwMagnitude = CONVERT(SDL_abs(hap_periodic->magnitude)); 904 periodic->lOffset = CONVERT(hap_periodic->offset); 905 periodic->dwPhase = 906 (hap_periodic->phase + (hap_periodic->magnitude < 0 ? 18000 : 0)) % 36000; 907 periodic->dwPeriod = hap_periodic->period * 1000; 908 dest->cbTypeSpecificParams = sizeof(FFPERIODIC); 909 dest->lpvTypeSpecificParams = periodic; 910 911 /* Generics */ 912 dest->dwDuration = hap_periodic->length * 1000; /* In microseconds. */ 913 dest->dwTriggerButton = FFGetTriggerButton(hap_periodic->button); 914 dest->dwTriggerRepeatInterval = hap_periodic->interval; 915 dest->dwStartDelay = hap_periodic->delay * 1000; /* In microseconds. */ 916 917 /* Direction. */ 918 if (SDL_SYS_SetDirection(dest, &hap_periodic->direction, dest->cAxes) 919 < 0) { 920 return -1; 921 } 922 923 /* Envelope */ 924 if ((hap_periodic->attack_length == 0) 925 && (hap_periodic->fade_length == 0)) { 926 SDL_free(envelope); 927 dest->lpEnvelope = NULL; 928 } else { 929 envelope->dwAttackLevel = CCONVERT(hap_periodic->attack_level); 930 envelope->dwAttackTime = hap_periodic->attack_length * 1000; 931 envelope->dwFadeLevel = CCONVERT(hap_periodic->fade_level); 932 envelope->dwFadeTime = hap_periodic->fade_length * 1000; 933 } 934 935 break; 936 937 case SDL_HAPTIC_SPRING: 938 case SDL_HAPTIC_DAMPER: 939 case SDL_HAPTIC_INERTIA: 940 case SDL_HAPTIC_FRICTION: 941 hap_condition = &src->condition; 942 if (dest->cAxes > 0) { 943 condition = SDL_malloc(sizeof(FFCONDITION) * dest->cAxes); 944 if (condition == NULL) { 945 return SDL_OutOfMemory(); 946 } 947 SDL_memset(condition, 0, sizeof(FFCONDITION)); 948 949 /* Specifics */ 950 for (i = 0; i < dest->cAxes; i++) { 951 condition[i].lOffset = CONVERT(hap_condition->center[i]); 952 condition[i].lPositiveCoefficient = 953 CONVERT(hap_condition->right_coeff[i]); 954 condition[i].lNegativeCoefficient = 955 CONVERT(hap_condition->left_coeff[i]); 956 condition[i].dwPositiveSaturation = 957 CCONVERT(hap_condition->right_sat[i] / 2); 958 condition[i].dwNegativeSaturation = 959 CCONVERT(hap_condition->left_sat[i] / 2); 960 condition[i].lDeadBand = CCONVERT(hap_condition->deadband[i] / 2); 961 } 962 } 963 964 dest->cbTypeSpecificParams = sizeof(FFCONDITION) * dest->cAxes; 965 dest->lpvTypeSpecificParams = condition; 966 967 /* Generics */ 968 dest->dwDuration = hap_condition->length * 1000; /* In microseconds. */ 969 dest->dwTriggerButton = FFGetTriggerButton(hap_condition->button); 970 dest->dwTriggerRepeatInterval = hap_condition->interval; 971 dest->dwStartDelay = hap_condition->delay * 1000; /* In microseconds. */ 972 973 /* Direction. */ 974 if (SDL_SYS_SetDirection(dest, &hap_condition->direction, dest->cAxes) 975 < 0) { 976 return -1; 977 } 978 979 /* Envelope - Not actually supported by most CONDITION implementations. */ 980 SDL_free(dest->lpEnvelope); 981 dest->lpEnvelope = NULL; 982 983 break; 984 985 case SDL_HAPTIC_RAMP: 986 hap_ramp = &src->ramp; 987 ramp = SDL_malloc(sizeof(FFRAMPFORCE)); 988 if (ramp == NULL) { 989 return SDL_OutOfMemory(); 990 } 991 SDL_memset(ramp, 0, sizeof(FFRAMPFORCE)); 992 993 /* Specifics */ 994 ramp->lStart = CONVERT(hap_ramp->start); 995 ramp->lEnd = CONVERT(hap_ramp->end); 996 dest->cbTypeSpecificParams = sizeof(FFRAMPFORCE); 997 dest->lpvTypeSpecificParams = ramp; 998 999 /* Generics */ 1000 dest->dwDuration = hap_ramp->length * 1000; /* In microseconds. */ 1001 dest->dwTriggerButton = FFGetTriggerButton(hap_ramp->button); 1002 dest->dwTriggerRepeatInterval = hap_ramp->interval; 1003 dest->dwStartDelay = hap_ramp->delay * 1000; /* In microseconds. */ 1004 1005 /* Direction. */ 1006 if (SDL_SYS_SetDirection(dest, &hap_ramp->direction, dest->cAxes) < 0) { 1007 return -1; 1008 } 1009 1010 /* Envelope */ 1011 if ((hap_ramp->attack_length == 0) && (hap_ramp->fade_length == 0)) { 1012 SDL_free(envelope); 1013 dest->lpEnvelope = NULL; 1014 } else { 1015 envelope->dwAttackLevel = CCONVERT(hap_ramp->attack_level); 1016 envelope->dwAttackTime = hap_ramp->attack_length * 1000; 1017 envelope->dwFadeLevel = CCONVERT(hap_ramp->fade_level); 1018 envelope->dwFadeTime = hap_ramp->fade_length * 1000; 1019 } 1020 1021 break; 1022 1023 case SDL_HAPTIC_CUSTOM: 1024 hap_custom = &src->custom; 1025 custom = SDL_malloc(sizeof(FFCUSTOMFORCE)); 1026 if (custom == NULL) { 1027 return SDL_OutOfMemory(); 1028 } 1029 SDL_memset(custom, 0, sizeof(FFCUSTOMFORCE)); 1030 1031 /* Specifics */ 1032 custom->cChannels = hap_custom->channels; 1033 custom->dwSamplePeriod = hap_custom->period * 1000; 1034 custom->cSamples = hap_custom->samples; 1035 custom->rglForceData = 1036 SDL_malloc(sizeof(LONG) * custom->cSamples * custom->cChannels); 1037 for (i = 0; i < hap_custom->samples * hap_custom->channels; i++) { /* Copy data. */ 1038 custom->rglForceData[i] = CCONVERT(hap_custom->data[i]); 1039 } 1040 dest->cbTypeSpecificParams = sizeof(FFCUSTOMFORCE); 1041 dest->lpvTypeSpecificParams = custom; 1042 1043 /* Generics */ 1044 dest->dwDuration = hap_custom->length * 1000; /* In microseconds. */ 1045 dest->dwTriggerButton = FFGetTriggerButton(hap_custom->button); 1046 dest->dwTriggerRepeatInterval = hap_custom->interval; 1047 dest->dwStartDelay = hap_custom->delay * 1000; /* In microseconds. */ 1048 1049 /* Direction. */ 1050 if (SDL_SYS_SetDirection(dest, &hap_custom->direction, dest->cAxes) < 1051 0) { 1052 return -1; 1053 } 1054 1055 /* Envelope */ 1056 if ((hap_custom->attack_length == 0) 1057 && (hap_custom->fade_length == 0)) { 1058 SDL_free(envelope); 1059 dest->lpEnvelope = NULL; 1060 } else { 1061 envelope->dwAttackLevel = CCONVERT(hap_custom->attack_level); 1062 envelope->dwAttackTime = hap_custom->attack_length * 1000; 1063 envelope->dwFadeLevel = CCONVERT(hap_custom->fade_level); 1064 envelope->dwFadeTime = hap_custom->fade_length * 1000; 1065 } 1066 1067 break; 1068 1069 1070 default: 1071 return SDL_SetError("Haptic: Unknown effect type."); 1072 } 1073 1074 return 0; 1075 } 1076 1077 1078 /* 1079 * Frees an FFEFFECT allocated by SDL_SYS_ToFFEFFECT. 1080 */ 1081 static void 1082 SDL_SYS_HapticFreeFFEFFECT(FFEFFECT * effect, int type) 1083 { 1084 FFCUSTOMFORCE *custom; 1085 1086 SDL_free(effect->lpEnvelope); 1087 effect->lpEnvelope = NULL; 1088 SDL_free(effect->rgdwAxes); 1089 effect->rgdwAxes = NULL; 1090 if (effect->lpvTypeSpecificParams != NULL) { 1091 if (type == SDL_HAPTIC_CUSTOM) { /* Must free the custom data. */ 1092 custom = (FFCUSTOMFORCE *) effect->lpvTypeSpecificParams; 1093 SDL_free(custom->rglForceData); 1094 custom->rglForceData = NULL; 1095 } 1096 SDL_free(effect->lpvTypeSpecificParams); 1097 effect->lpvTypeSpecificParams = NULL; 1098 } 1099 SDL_free(effect->rglDirection); 1100 effect->rglDirection = NULL; 1101 } 1102 1103 1104 /* 1105 * Gets the effect type from the generic SDL haptic effect wrapper. 1106 */ 1107 CFUUIDRef 1108 SDL_SYS_HapticEffectType(Uint16 type) 1109 { 1110 switch (type) { 1111 case SDL_HAPTIC_CONSTANT: 1112 return kFFEffectType_ConstantForce_ID; 1113 1114 case SDL_HAPTIC_RAMP: 1115 return kFFEffectType_RampForce_ID; 1116 1117 /* !!! FIXME: put this back when we have more bits in 2.1 */ 1118 /* case SDL_HAPTIC_SQUARE: 1119 return kFFEffectType_Square_ID; */ 1120 1121 case SDL_HAPTIC_SINE: 1122 return kFFEffectType_Sine_ID; 1123 1124 case SDL_HAPTIC_TRIANGLE: 1125 return kFFEffectType_Triangle_ID; 1126 1127 case SDL_HAPTIC_SAWTOOTHUP: 1128 return kFFEffectType_SawtoothUp_ID; 1129 1130 case SDL_HAPTIC_SAWTOOTHDOWN: 1131 return kFFEffectType_SawtoothDown_ID; 1132 1133 case SDL_HAPTIC_SPRING: 1134 return kFFEffectType_Spring_ID; 1135 1136 case SDL_HAPTIC_DAMPER: 1137 return kFFEffectType_Damper_ID; 1138 1139 case SDL_HAPTIC_INERTIA: 1140 return kFFEffectType_Inertia_ID; 1141 1142 case SDL_HAPTIC_FRICTION: 1143 return kFFEffectType_Friction_ID; 1144 1145 case SDL_HAPTIC_CUSTOM: 1146 return kFFEffectType_CustomForce_ID; 1147 1148 default: 1149 SDL_SetError("Haptic: Unknown effect type."); 1150 return NULL; 1151 } 1152 } 1153 1154 1155 /* 1156 * Creates a new haptic effect. 1157 */ 1158 int 1159 SDL_SYS_HapticNewEffect(SDL_Haptic * haptic, struct haptic_effect *effect, 1160 SDL_HapticEffect * base) 1161 { 1162 HRESULT ret; 1163 CFUUIDRef type; 1164 1165 /* Alloc the effect. */ 1166 effect->hweffect = (struct haptic_hweffect *) 1167 SDL_malloc(sizeof(struct haptic_hweffect)); 1168 if (effect->hweffect == NULL) { 1169 SDL_OutOfMemory(); 1170 goto err_hweffect; 1171 } 1172 1173 /* Get the type. */ 1174 type = SDL_SYS_HapticEffectType(base->type); 1175 if (type == NULL) { 1176 goto err_hweffect; 1177 } 1178 1179 /* Get the effect. */ 1180 if (SDL_SYS_ToFFEFFECT(haptic, &effect->hweffect->effect, base) < 0) { 1181 goto err_effectdone; 1182 } 1183 1184 /* Create the actual effect. */ 1185 ret = FFDeviceCreateEffect(haptic->hwdata->device, type, 1186 &effect->hweffect->effect, 1187 &effect->hweffect->ref); 1188 if (ret != FF_OK) { 1189 SDL_SetError("Haptic: Unable to create effect: %s.", FFStrError(ret)); 1190 goto err_effectdone; 1191 } 1192 1193 return 0; 1194 1195 err_effectdone: 1196 SDL_SYS_HapticFreeFFEFFECT(&effect->hweffect->effect, base->type); 1197 err_hweffect: 1198 SDL_free(effect->hweffect); 1199 effect->hweffect = NULL; 1200 return -1; 1201 } 1202 1203 1204 /* 1205 * Updates an effect. 1206 */ 1207 int 1208 SDL_SYS_HapticUpdateEffect(SDL_Haptic * haptic, 1209 struct haptic_effect *effect, 1210 SDL_HapticEffect * data) 1211 { 1212 HRESULT ret; 1213 FFEffectParameterFlag flags; 1214 FFEFFECT temp; 1215 1216 /* Get the effect. */ 1217 SDL_memset(&temp, 0, sizeof(FFEFFECT)); 1218 if (SDL_SYS_ToFFEFFECT(haptic, &temp, data) < 0) { 1219 goto err_update; 1220 } 1221 1222 /* Set the flags. Might be worthwhile to diff temp with loaded effect and 1223 * only change those parameters. */ 1224 flags = FFEP_DIRECTION | 1225 FFEP_DURATION | 1226 FFEP_ENVELOPE | 1227 FFEP_STARTDELAY | 1228 FFEP_TRIGGERBUTTON | 1229 FFEP_TRIGGERREPEATINTERVAL | FFEP_TYPESPECIFICPARAMS; 1230 1231 /* Create the actual effect. */ 1232 ret = FFEffectSetParameters(effect->hweffect->ref, &temp, flags); 1233 if (ret != FF_OK) { 1234 SDL_SetError("Haptic: Unable to update effect: %s.", FFStrError(ret)); 1235 goto err_update; 1236 } 1237 1238 /* Copy it over. */ 1239 SDL_SYS_HapticFreeFFEFFECT(&effect->hweffect->effect, data->type); 1240 SDL_memcpy(&effect->hweffect->effect, &temp, sizeof(FFEFFECT)); 1241 1242 return 0; 1243 1244 err_update: 1245 SDL_SYS_HapticFreeFFEFFECT(&temp, data->type); 1246 return -1; 1247 } 1248 1249 1250 /* 1251 * Runs an effect. 1252 */ 1253 int 1254 SDL_SYS_HapticRunEffect(SDL_Haptic * haptic, struct haptic_effect *effect, 1255 Uint32 iterations) 1256 { 1257 HRESULT ret; 1258 Uint32 iter; 1259 1260 /* Check if it's infinite. */ 1261 if (iterations == SDL_HAPTIC_INFINITY) { 1262 iter = FF_INFINITE; 1263 } else 1264 iter = iterations; 1265 1266 /* Run the effect. */ 1267 ret = FFEffectStart(effect->hweffect->ref, iter, 0); 1268 if (ret != FF_OK) { 1269 return SDL_SetError("Haptic: Unable to run the effect: %s.", 1270 FFStrError(ret)); 1271 } 1272 1273 return 0; 1274 } 1275 1276 1277 /* 1278 * Stops an effect. 1279 */ 1280 int 1281 SDL_SYS_HapticStopEffect(SDL_Haptic * haptic, struct haptic_effect *effect) 1282 { 1283 HRESULT ret; 1284 1285 ret = FFEffectStop(effect->hweffect->ref); 1286 if (ret != FF_OK) { 1287 return SDL_SetError("Haptic: Unable to stop the effect: %s.", 1288 FFStrError(ret)); 1289 } 1290 1291 return 0; 1292 } 1293 1294 1295 /* 1296 * Frees the effect. 1297 */ 1298 void 1299 SDL_SYS_HapticDestroyEffect(SDL_Haptic * haptic, struct haptic_effect *effect) 1300 { 1301 HRESULT ret; 1302 1303 ret = FFDeviceReleaseEffect(haptic->hwdata->device, effect->hweffect->ref); 1304 if (ret != FF_OK) { 1305 SDL_SetError("Haptic: Error removing the effect from the device: %s.", 1306 FFStrError(ret)); 1307 } 1308 SDL_SYS_HapticFreeFFEFFECT(&effect->hweffect->effect, 1309 effect->effect.type); 1310 SDL_free(effect->hweffect); 1311 effect->hweffect = NULL; 1312 } 1313 1314 1315 /* 1316 * Gets the status of a haptic effect. 1317 */ 1318 int 1319 SDL_SYS_HapticGetEffectStatus(SDL_Haptic * haptic, 1320 struct haptic_effect *effect) 1321 { 1322 HRESULT ret; 1323 FFEffectStatusFlag status; 1324 1325 ret = FFEffectGetEffectStatus(effect->hweffect->ref, &status); 1326 if (ret != FF_OK) { 1327 SDL_SetError("Haptic: Unable to get effect status: %s.", 1328 FFStrError(ret)); 1329 return -1; 1330 } 1331 1332 if (status == 0) { 1333 return SDL_FALSE; 1334 } 1335 return SDL_TRUE; /* Assume it's playing or emulated. */ 1336 } 1337 1338 1339 /* 1340 * Sets the gain. 1341 */ 1342 int 1343 SDL_SYS_HapticSetGain(SDL_Haptic * haptic, int gain) 1344 { 1345 HRESULT ret; 1346 Uint32 val; 1347 1348 val = gain * 100; /* Mac OS X uses 0 to 10,000 */ 1349 ret = FFDeviceSetForceFeedbackProperty(haptic->hwdata->device, 1350 FFPROP_FFGAIN, &val); 1351 if (ret != FF_OK) { 1352 return SDL_SetError("Haptic: Error setting gain: %s.", FFStrError(ret)); 1353 } 1354 1355 return 0; 1356 } 1357 1358 1359 /* 1360 * Sets the autocentering. 1361 */ 1362 int 1363 SDL_SYS_HapticSetAutocenter(SDL_Haptic * haptic, int autocenter) 1364 { 1365 HRESULT ret; 1366 Uint32 val; 1367 1368 /* Mac OS X only has 0 (off) and 1 (on) */ 1369 if (autocenter == 0) { 1370 val = 0; 1371 } else { 1372 val = 1; 1373 } 1374 1375 ret = FFDeviceSetForceFeedbackProperty(haptic->hwdata->device, 1376 FFPROP_AUTOCENTER, &val); 1377 if (ret != FF_OK) { 1378 return SDL_SetError("Haptic: Error setting autocenter: %s.", 1379 FFStrError(ret)); 1380 } 1381 1382 return 0; 1383 } 1384 1385 1386 /* 1387 * Pauses the device. 1388 */ 1389 int 1390 SDL_SYS_HapticPause(SDL_Haptic * haptic) 1391 { 1392 HRESULT ret; 1393 1394 ret = FFDeviceSendForceFeedbackCommand(haptic->hwdata->device, 1395 FFSFFC_PAUSE); 1396 if (ret != FF_OK) { 1397 return SDL_SetError("Haptic: Error pausing device: %s.", FFStrError(ret)); 1398 } 1399 1400 return 0; 1401 } 1402 1403 1404 /* 1405 * Unpauses the device. 1406 */ 1407 int 1408 SDL_SYS_HapticUnpause(SDL_Haptic * haptic) 1409 { 1410 HRESULT ret; 1411 1412 ret = FFDeviceSendForceFeedbackCommand(haptic->hwdata->device, 1413 FFSFFC_CONTINUE); 1414 if (ret != FF_OK) { 1415 return SDL_SetError("Haptic: Error pausing device: %s.", FFStrError(ret)); 1416 } 1417 1418 return 0; 1419 } 1420 1421 1422 /* 1423 * Stops all currently playing effects. 1424 */ 1425 int 1426 SDL_SYS_HapticStopAll(SDL_Haptic * haptic) 1427 { 1428 HRESULT ret; 1429 1430 ret = FFDeviceSendForceFeedbackCommand(haptic->hwdata->device, 1431 FFSFFC_STOPALL); 1432 if (ret != FF_OK) { 1433 return SDL_SetError("Haptic: Error stopping device: %s.", FFStrError(ret)); 1434 } 1435 1436 return 0; 1437 } 1438 1439 #endif /* SDL_HAPTIC_IOKIT */ 1440 1441 /* vi: set ts=4 sw=4 expandtab: */