Atlas - SDL_syshaptic.c

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