SDL_dinputhaptic.c (38343B)
1/* 2 Simple DirectMedia Layer 3 Copyright (C) 1997-2014 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#include "SDL_error.h" 24#include "SDL_stdinc.h" 25#include "SDL_haptic.h" 26#include "SDL_timer.h" 27#include "SDL_windowshaptic_c.h" 28#include "SDL_dinputhaptic_c.h" 29#include "../SDL_syshaptic.h" 30#include "../../joystick/windows/SDL_windowsjoystick_c.h" 31 32 33#if SDL_HAPTIC_DINPUT 34 35/* 36 * External stuff. 37 */ 38extern HWND SDL_HelperWindow; 39 40 41/* 42 * Internal stuff. 43 */ 44static SDL_bool coinitialized = SDL_FALSE; 45static LPDIRECTINPUT8 dinput = NULL; 46 47 48/* 49 * Like SDL_SetError but for DX error codes. 50 */ 51static int 52DI_SetError(const char *str, HRESULT err) 53{ 54 /* 55 SDL_SetError("Haptic: %s - %s: %s", str, 56 DXGetErrorString8A(err), DXGetErrorDescription8A(err)); 57 */ 58 return SDL_SetError("Haptic error %s", str); 59} 60 61/* 62 * Checks to see if two GUID are the same. 63 */ 64static int 65DI_GUIDIsSame(const GUID * a, const GUID * b) 66{ 67 return (SDL_memcmp(a, b, sizeof (GUID)) == 0); 68} 69 70/* 71 * Callback to find the haptic devices. 72 */ 73static BOOL CALLBACK 74EnumHapticsCallback(const DIDEVICEINSTANCE * pdidInstance, VOID * pContext) 75{ 76 (void) pContext; 77 SDL_DINPUT_MaybeAddDevice(pdidInstance); 78 return DIENUM_CONTINUE; /* continue enumerating */ 79} 80 81int 82SDL_DINPUT_HapticInit(void) 83{ 84 HRESULT ret; 85 HINSTANCE instance; 86 87 if (dinput != NULL) { /* Already open. */ 88 return SDL_SetError("Haptic: SubSystem already open."); 89 } 90 91 ret = WIN_CoInitialize(); 92 if (FAILED(ret)) { 93 return DI_SetError("Coinitialize", ret); 94 } 95 96 coinitialized = SDL_TRUE; 97 98 ret = CoCreateInstance(&CLSID_DirectInput8, NULL, CLSCTX_INPROC_SERVER, 99 &IID_IDirectInput8, (LPVOID)& dinput); 100 if (FAILED(ret)) { 101 SDL_SYS_HapticQuit(); 102 return DI_SetError("CoCreateInstance", ret); 103 } 104 105 /* Because we used CoCreateInstance, we need to Initialize it, first. */ 106 instance = GetModuleHandle(NULL); 107 if (instance == NULL) { 108 SDL_SYS_HapticQuit(); 109 return SDL_SetError("GetModuleHandle() failed with error code %lu.", 110 GetLastError()); 111 } 112 ret = IDirectInput8_Initialize(dinput, instance, DIRECTINPUT_VERSION); 113 if (FAILED(ret)) { 114 SDL_SYS_HapticQuit(); 115 return DI_SetError("Initializing DirectInput device", ret); 116 } 117 118 /* Look for haptic devices. */ 119 ret = IDirectInput8_EnumDevices(dinput, 120 0, 121 EnumHapticsCallback, 122 NULL, 123 DIEDFL_FORCEFEEDBACK | 124 DIEDFL_ATTACHEDONLY); 125 if (FAILED(ret)) { 126 SDL_SYS_HapticQuit(); 127 return DI_SetError("Enumerating DirectInput devices", ret); 128 } 129 return 0; 130} 131 132int 133SDL_DINPUT_MaybeAddDevice(const DIDEVICEINSTANCE * pdidInstance) 134{ 135 HRESULT ret; 136 LPDIRECTINPUTDEVICE8 device; 137 const DWORD needflags = DIDC_ATTACHED | DIDC_FORCEFEEDBACK; 138 DIDEVCAPS capabilities; 139 SDL_hapticlist_item *item = NULL; 140 141 if (dinput == NULL) { 142 return -1; /* not initialized. We'll pick these up on enumeration if we init later. */ 143 } 144 145 /* Make sure we don't already have it */ 146 for (item = SDL_hapticlist; item; item = item->next) { 147 if ((!item->bXInputHaptic) && (SDL_memcmp(&item->instance, pdidInstance, sizeof(*pdidInstance)) == 0)) { 148 return -1; /* Already added */ 149 } 150 } 151 152 /* Open the device */ 153 ret = IDirectInput8_CreateDevice(dinput, &pdidInstance->guidInstance, &device, NULL); 154 if (FAILED(ret)) { 155 /* DI_SetError("Creating DirectInput device",ret); */ 156 return -1; 157 } 158 159 /* Get capabilities. */ 160 SDL_zero(capabilities); 161 capabilities.dwSize = sizeof(DIDEVCAPS); 162 ret = IDirectInputDevice8_GetCapabilities(device, &capabilities); 163 IDirectInputDevice8_Release(device); 164 if (FAILED(ret)) { 165 /* DI_SetError("Getting device capabilities",ret); */ 166 return -1; 167 } 168 169 if ((capabilities.dwFlags & needflags) != needflags) { 170 return -1; /* not a device we can use. */ 171 } 172 173 item = (SDL_hapticlist_item *)SDL_calloc(1, sizeof(SDL_hapticlist_item)); 174 if (item == NULL) { 175 return SDL_OutOfMemory(); 176 } 177 178 item->name = WIN_StringToUTF8(pdidInstance->tszProductName); 179 if (!item->name) { 180 SDL_free(item); 181 return -1; 182 } 183 184 /* Copy the instance over, useful for creating devices. */ 185 SDL_memcpy(&item->instance, pdidInstance, sizeof(DIDEVICEINSTANCE)); 186 SDL_memcpy(&item->capabilities, &capabilities, sizeof(capabilities)); 187 188 return SDL_SYS_AddHapticDevice(item); 189} 190 191int 192SDL_DINPUT_MaybeRemoveDevice(const DIDEVICEINSTANCE * pdidInstance) 193{ 194 SDL_hapticlist_item *item; 195 SDL_hapticlist_item *prev = NULL; 196 197 if (dinput == NULL) { 198 return -1; /* not initialized, ignore this. */ 199 } 200 201 for (item = SDL_hapticlist; item != NULL; item = item->next) { 202 if (!item->bXInputHaptic && SDL_memcmp(&item->instance, pdidInstance, sizeof(*pdidInstance)) == 0) { 203 /* found it, remove it. */ 204 return SDL_SYS_RemoveHapticDevice(prev, item); 205 } 206 prev = item; 207 } 208 return -1; 209} 210 211/* 212 * Callback to get supported axes. 213 */ 214static BOOL CALLBACK 215DI_DeviceObjectCallback(LPCDIDEVICEOBJECTINSTANCE dev, LPVOID pvRef) 216{ 217 SDL_Haptic *haptic = (SDL_Haptic *) pvRef; 218 219 if ((dev->dwType & DIDFT_AXIS) && (dev->dwFlags & DIDOI_FFACTUATOR)) { 220 const GUID *guid = &dev->guidType; 221 DWORD offset = 0; 222 if (DI_GUIDIsSame(guid, &GUID_XAxis)) { 223 offset = DIJOFS_X; 224 } else if (DI_GUIDIsSame(guid, &GUID_YAxis)) { 225 offset = DIJOFS_Y; 226 } else if (DI_GUIDIsSame(guid, &GUID_ZAxis)) { 227 offset = DIJOFS_Z; 228 } else if (DI_GUIDIsSame(guid, &GUID_RxAxis)) { 229 offset = DIJOFS_RX; 230 } else if (DI_GUIDIsSame(guid, &GUID_RyAxis)) { 231 offset = DIJOFS_RY; 232 } else if (DI_GUIDIsSame(guid, &GUID_RzAxis)) { 233 offset = DIJOFS_RZ; 234 } else { 235 return DIENUM_CONTINUE; /* can't use this, go on. */ 236 } 237 238 haptic->hwdata->axes[haptic->naxes] = offset; 239 haptic->naxes++; 240 241 /* Currently using the artificial limit of 3 axes. */ 242 if (haptic->naxes >= 3) { 243 return DIENUM_STOP; 244 } 245 } 246 247 return DIENUM_CONTINUE; 248} 249 250/* 251 * Callback to get all supported effects. 252 */ 253#define EFFECT_TEST(e,s) \ 254if (DI_GUIDIsSame(&pei->guid, &(e))) \ 255 haptic->supported |= (s) 256static BOOL CALLBACK 257DI_EffectCallback(LPCDIEFFECTINFO pei, LPVOID pv) 258{ 259 /* Prepare the haptic device. */ 260 SDL_Haptic *haptic = (SDL_Haptic *) pv; 261 262 /* Get supported. */ 263 EFFECT_TEST(GUID_Spring, SDL_HAPTIC_SPRING); 264 EFFECT_TEST(GUID_Damper, SDL_HAPTIC_DAMPER); 265 EFFECT_TEST(GUID_Inertia, SDL_HAPTIC_INERTIA); 266 EFFECT_TEST(GUID_Friction, SDL_HAPTIC_FRICTION); 267 EFFECT_TEST(GUID_ConstantForce, SDL_HAPTIC_CONSTANT); 268 EFFECT_TEST(GUID_CustomForce, SDL_HAPTIC_CUSTOM); 269 EFFECT_TEST(GUID_Sine, SDL_HAPTIC_SINE); 270 /* !!! FIXME: put this back when we have more bits in 2.1 */ 271 /* EFFECT_TEST(GUID_Square, SDL_HAPTIC_SQUARE); */ 272 EFFECT_TEST(GUID_Triangle, SDL_HAPTIC_TRIANGLE); 273 EFFECT_TEST(GUID_SawtoothUp, SDL_HAPTIC_SAWTOOTHUP); 274 EFFECT_TEST(GUID_SawtoothDown, SDL_HAPTIC_SAWTOOTHDOWN); 275 EFFECT_TEST(GUID_RampForce, SDL_HAPTIC_RAMP); 276 277 /* Check for more. */ 278 return DIENUM_CONTINUE; 279} 280 281/* 282 * Opens the haptic device. 283 * 284 * Steps: 285 * - Set cooperative level. 286 * - Set data format. 287 * - Acquire exclusiveness. 288 * - Reset actuators. 289 * - Get supported features. 290 */ 291static int 292SDL_DINPUT_HapticOpenFromDevice(SDL_Haptic * haptic, LPDIRECTINPUTDEVICE8 device8, SDL_bool is_joystick) 293{ 294 HRESULT ret; 295 DIPROPDWORD dipdw; 296 297 /* Allocate the hwdata */ 298 haptic->hwdata = (struct haptic_hwdata *)SDL_malloc(sizeof(*haptic->hwdata)); 299 if (haptic->hwdata == NULL) { 300 return SDL_OutOfMemory(); 301 } 302 SDL_memset(haptic->hwdata, 0, sizeof(*haptic->hwdata)); 303 304 /* We'll use the device8 from now on. */ 305 haptic->hwdata->device = device8; 306 haptic->hwdata->is_joystick = is_joystick; 307 308 /* !!! FIXME: opening a haptic device here first will make an attempt to 309 !!! FIXME: SDL_JoystickOpen() that same device fail later, since we 310 !!! FIXME: have it open in exclusive mode. But this will allow 311 !!! FIXME: SDL_JoystickOpen() followed by SDL_HapticOpenFromJoystick() 312 !!! FIXME: to work, and that's probably the common case. Still, 313 !!! FIXME: ideally, We need to unify the opening code. */ 314 315 if (!is_joystick) { /* if is_joystick, we already set this up elsewhere. */ 316 /* Grab it exclusively to use force feedback stuff. */ 317 ret = IDirectInputDevice8_SetCooperativeLevel(haptic->hwdata->device, 318 SDL_HelperWindow, 319 DISCL_EXCLUSIVE | 320 DISCL_BACKGROUND); 321 if (FAILED(ret)) { 322 DI_SetError("Setting cooperative level to exclusive", ret); 323 goto acquire_err; 324 } 325 326 /* Set data format. */ 327 ret = IDirectInputDevice8_SetDataFormat(haptic->hwdata->device, 328 &c_dfDIJoystick2); 329 if (FAILED(ret)) { 330 DI_SetError("Setting data format", ret); 331 goto acquire_err; 332 } 333 334 /* Get number of axes. */ 335 ret = IDirectInputDevice8_EnumObjects(haptic->hwdata->device, 336 DI_DeviceObjectCallback, 337 haptic, DIDFT_AXIS); 338 if (FAILED(ret)) { 339 DI_SetError("Getting device axes", ret); 340 goto acquire_err; 341 } 342 343 /* Acquire the device. */ 344 ret = IDirectInputDevice8_Acquire(haptic->hwdata->device); 345 if (FAILED(ret)) { 346 DI_SetError("Acquiring DirectInput device", ret); 347 goto acquire_err; 348 } 349 } 350 351 /* Reset all actuators - just in case. */ 352 ret = IDirectInputDevice8_SendForceFeedbackCommand(haptic->hwdata->device, 353 DISFFC_RESET); 354 if (FAILED(ret)) { 355 DI_SetError("Resetting device", ret); 356 goto acquire_err; 357 } 358 359 /* Enabling actuators. */ 360 ret = IDirectInputDevice8_SendForceFeedbackCommand(haptic->hwdata->device, 361 DISFFC_SETACTUATORSON); 362 if (FAILED(ret)) { 363 DI_SetError("Enabling actuators", ret); 364 goto acquire_err; 365 } 366 367 /* Get supported effects. */ 368 ret = IDirectInputDevice8_EnumEffects(haptic->hwdata->device, 369 DI_EffectCallback, haptic, 370 DIEFT_ALL); 371 if (FAILED(ret)) { 372 DI_SetError("Enumerating supported effects", ret); 373 goto acquire_err; 374 } 375 if (haptic->supported == 0) { /* Error since device supports nothing. */ 376 SDL_SetError("Haptic: Internal error on finding supported effects."); 377 goto acquire_err; 378 } 379 380 /* Check autogain and autocenter. */ 381 dipdw.diph.dwSize = sizeof(DIPROPDWORD); 382 dipdw.diph.dwHeaderSize = sizeof(DIPROPHEADER); 383 dipdw.diph.dwObj = 0; 384 dipdw.diph.dwHow = DIPH_DEVICE; 385 dipdw.dwData = 10000; 386 ret = IDirectInputDevice8_SetProperty(haptic->hwdata->device, 387 DIPROP_FFGAIN, &dipdw.diph); 388 if (!FAILED(ret)) { /* Gain is supported. */ 389 haptic->supported |= SDL_HAPTIC_GAIN; 390 } 391 dipdw.diph.dwObj = 0; 392 dipdw.diph.dwHow = DIPH_DEVICE; 393 dipdw.dwData = DIPROPAUTOCENTER_OFF; 394 ret = IDirectInputDevice8_SetProperty(haptic->hwdata->device, 395 DIPROP_AUTOCENTER, &dipdw.diph); 396 if (!FAILED(ret)) { /* Autocenter is supported. */ 397 haptic->supported |= SDL_HAPTIC_AUTOCENTER; 398 } 399 400 /* Status is always supported. */ 401 haptic->supported |= SDL_HAPTIC_STATUS | SDL_HAPTIC_PAUSE; 402 403 /* Check maximum effects. */ 404 haptic->neffects = 128; /* This is not actually supported as thus under windows, 405 there is no way to tell the number of EFFECTS that a 406 device can hold, so we'll just use a "random" number 407 instead and put warnings in SDL_haptic.h */ 408 haptic->nplaying = 128; /* Even more impossible to get this then neffects. */ 409 410 /* Prepare effects memory. */ 411 haptic->effects = (struct haptic_effect *) 412 SDL_malloc(sizeof(struct haptic_effect) * haptic->neffects); 413 if (haptic->effects == NULL) { 414 SDL_OutOfMemory(); 415 goto acquire_err; 416 } 417 /* Clear the memory */ 418 SDL_memset(haptic->effects, 0, 419 sizeof(struct haptic_effect) * haptic->neffects); 420 421 return 0; 422 423 /* Error handling */ 424 acquire_err: 425 IDirectInputDevice8_Unacquire(haptic->hwdata->device); 426 return -1; 427} 428 429int 430SDL_DINPUT_HapticOpen(SDL_Haptic * haptic, SDL_hapticlist_item *item) 431{ 432 HRESULT ret; 433 LPDIRECTINPUTDEVICE8 device; 434 LPDIRECTINPUTDEVICE8 device8; 435 436 /* Open the device */ 437 ret = IDirectInput8_CreateDevice(dinput, &item->instance.guidInstance, 438 &device, NULL); 439 if (FAILED(ret)) { 440 DI_SetError("Creating DirectInput device", ret); 441 return -1; 442 } 443 444 /* Now get the IDirectInputDevice8 interface, instead. */ 445 ret = IDirectInputDevice8_QueryInterface(device, 446 &IID_IDirectInputDevice8, 447 (LPVOID *)&device8); 448 /* Done with the temporary one now. */ 449 IDirectInputDevice8_Release(device); 450 if (FAILED(ret)) { 451 DI_SetError("Querying DirectInput interface", ret); 452 return -1; 453 } 454 455 if (SDL_DINPUT_HapticOpenFromDevice(haptic, device8, SDL_FALSE) < 0) { 456 IDirectInputDevice8_Release(device8); 457 return -1; 458 } 459 return 0; 460} 461 462int 463SDL_DINPUT_JoystickSameHaptic(SDL_Haptic * haptic, SDL_Joystick * joystick) 464{ 465 HRESULT ret; 466 DIDEVICEINSTANCE hap_instance, joy_instance; 467 468 hap_instance.dwSize = sizeof(DIDEVICEINSTANCE); 469 joy_instance.dwSize = sizeof(DIDEVICEINSTANCE); 470 471 /* Get the device instances. */ 472 ret = IDirectInputDevice8_GetDeviceInfo(haptic->hwdata->device, 473 &hap_instance); 474 if (FAILED(ret)) { 475 return 0; 476 } 477 ret = IDirectInputDevice8_GetDeviceInfo(joystick->hwdata->InputDevice, 478 &joy_instance); 479 if (FAILED(ret)) { 480 return 0; 481 } 482 483 return DI_GUIDIsSame(&hap_instance.guidInstance, &joy_instance.guidInstance); 484} 485 486int 487SDL_DINPUT_HapticOpenFromJoystick(SDL_Haptic * haptic, SDL_Joystick * joystick) 488{ 489 SDL_hapticlist_item *item; 490 int index = 0; 491 HRESULT ret; 492 DIDEVICEINSTANCE joy_instance; 493 494 joy_instance.dwSize = sizeof(DIDEVICEINSTANCE); 495 ret = IDirectInputDevice8_GetDeviceInfo(joystick->hwdata->InputDevice, &joy_instance); 496 if (FAILED(ret)) { 497 return -1; 498 } 499 500 /* Since it comes from a joystick we have to try to match it with a haptic device on our haptic list. */ 501 for (item = SDL_hapticlist; item != NULL; item = item->next) { 502 if (!item->bXInputHaptic && DI_GUIDIsSame(&item->instance.guidInstance, &joy_instance.guidInstance)) { 503 haptic->index = index; 504 return SDL_DINPUT_HapticOpenFromDevice(haptic, joystick->hwdata->InputDevice, SDL_TRUE); 505 } 506 ++index; 507 } 508 509 SDL_SetError("Couldn't find joystick in haptic device list"); 510 return -1; 511} 512 513void 514SDL_DINPUT_HapticClose(SDL_Haptic * haptic) 515{ 516 IDirectInputDevice8_Unacquire(haptic->hwdata->device); 517 518 /* Only release if isn't grabbed by a joystick. */ 519 if (haptic->hwdata->is_joystick == 0) { 520 IDirectInputDevice8_Release(haptic->hwdata->device); 521 } 522} 523 524void 525SDL_DINPUT_HapticQuit(void) 526{ 527 if (dinput != NULL) { 528 IDirectInput8_Release(dinput); 529 dinput = NULL; 530 } 531 532 if (coinitialized) { 533 WIN_CoUninitialize(); 534 coinitialized = SDL_FALSE; 535 } 536} 537 538/* 539 * Converts an SDL trigger button to an DIEFFECT trigger button. 540 */ 541static DWORD 542DIGetTriggerButton(Uint16 button) 543{ 544 DWORD dwTriggerButton; 545 546 dwTriggerButton = DIEB_NOTRIGGER; 547 548 if (button != 0) { 549 dwTriggerButton = DIJOFS_BUTTON(button - 1); 550 } 551 552 return dwTriggerButton; 553} 554 555 556/* 557 * Sets the direction. 558 */ 559static int 560SDL_SYS_SetDirection(DIEFFECT * effect, SDL_HapticDirection * dir, int naxes) 561{ 562 LONG *rglDir; 563 564 /* Handle no axes a part. */ 565 if (naxes == 0) { 566 effect->dwFlags |= DIEFF_SPHERICAL; /* Set as default. */ 567 effect->rglDirection = NULL; 568 return 0; 569 } 570 571 /* Has axes. */ 572 rglDir = SDL_malloc(sizeof(LONG) * naxes); 573 if (rglDir == NULL) { 574 return SDL_OutOfMemory(); 575 } 576 SDL_memset(rglDir, 0, sizeof(LONG) * naxes); 577 effect->rglDirection = rglDir; 578 579 switch (dir->type) { 580 case SDL_HAPTIC_POLAR: 581 effect->dwFlags |= DIEFF_POLAR; 582 rglDir[0] = dir->dir[0]; 583 return 0; 584 case SDL_HAPTIC_CARTESIAN: 585 effect->dwFlags |= DIEFF_CARTESIAN; 586 rglDir[0] = dir->dir[0]; 587 if (naxes > 1) 588 rglDir[1] = dir->dir[1]; 589 if (naxes > 2) 590 rglDir[2] = dir->dir[2]; 591 return 0; 592 case SDL_HAPTIC_SPHERICAL: 593 effect->dwFlags |= DIEFF_SPHERICAL; 594 rglDir[0] = dir->dir[0]; 595 if (naxes > 1) 596 rglDir[1] = dir->dir[1]; 597 if (naxes > 2) 598 rglDir[2] = dir->dir[2]; 599 return 0; 600 601 default: 602 return SDL_SetError("Haptic: Unknown direction type."); 603 } 604} 605 606/* Clamps and converts. */ 607#define CCONVERT(x) (((x) > 0x7FFF) ? 10000 : ((x)*10000) / 0x7FFF) 608/* Just converts. */ 609#define CONVERT(x) (((x)*10000) / 0x7FFF) 610/* 611 * Creates the DIEFFECT from a SDL_HapticEffect. 612 */ 613static int 614SDL_SYS_ToDIEFFECT(SDL_Haptic * haptic, DIEFFECT * dest, 615 SDL_HapticEffect * src) 616{ 617 int i; 618 DICONSTANTFORCE *constant; 619 DIPERIODIC *periodic; 620 DICONDITION *condition; /* Actually an array of conditions - one per axis. */ 621 DIRAMPFORCE *ramp; 622 DICUSTOMFORCE *custom; 623 DIENVELOPE *envelope; 624 SDL_HapticConstant *hap_constant; 625 SDL_HapticPeriodic *hap_periodic; 626 SDL_HapticCondition *hap_condition; 627 SDL_HapticRamp *hap_ramp; 628 SDL_HapticCustom *hap_custom; 629 DWORD *axes; 630 631 /* Set global stuff. */ 632 SDL_memset(dest, 0, sizeof(DIEFFECT)); 633 dest->dwSize = sizeof(DIEFFECT); /* Set the structure size. */ 634 dest->dwSamplePeriod = 0; /* Not used by us. */ 635 dest->dwGain = 10000; /* Gain is set globally, not locally. */ 636 dest->dwFlags = DIEFF_OBJECTOFFSETS; /* Seems obligatory. */ 637 638 /* Envelope. */ 639 envelope = SDL_malloc(sizeof(DIENVELOPE)); 640 if (envelope == NULL) { 641 return SDL_OutOfMemory(); 642 } 643 SDL_memset(envelope, 0, sizeof(DIENVELOPE)); 644 dest->lpEnvelope = envelope; 645 envelope->dwSize = sizeof(DIENVELOPE); /* Always should be this. */ 646 647 /* Axes. */ 648 dest->cAxes = haptic->naxes; 649 if (dest->cAxes > 0) { 650 axes = SDL_malloc(sizeof(DWORD) * dest->cAxes); 651 if (axes == NULL) { 652 return SDL_OutOfMemory(); 653 } 654 axes[0] = haptic->hwdata->axes[0]; /* Always at least one axis. */ 655 if (dest->cAxes > 1) { 656 axes[1] = haptic->hwdata->axes[1]; 657 } 658 if (dest->cAxes > 2) { 659 axes[2] = haptic->hwdata->axes[2]; 660 } 661 dest->rgdwAxes = axes; 662 } 663 664 /* The big type handling switch, even bigger than Linux's version. */ 665 switch (src->type) { 666 case SDL_HAPTIC_CONSTANT: 667 hap_constant = &src->constant; 668 constant = SDL_malloc(sizeof(DICONSTANTFORCE)); 669 if (constant == NULL) { 670 return SDL_OutOfMemory(); 671 } 672 SDL_memset(constant, 0, sizeof(DICONSTANTFORCE)); 673 674 /* Specifics */ 675 constant->lMagnitude = CONVERT(hap_constant->level); 676 dest->cbTypeSpecificParams = sizeof(DICONSTANTFORCE); 677 dest->lpvTypeSpecificParams = constant; 678 679 /* Generics */ 680 dest->dwDuration = hap_constant->length * 1000; /* In microseconds. */ 681 dest->dwTriggerButton = DIGetTriggerButton(hap_constant->button); 682 dest->dwTriggerRepeatInterval = hap_constant->interval; 683 dest->dwStartDelay = hap_constant->delay * 1000; /* In microseconds. */ 684 685 /* Direction. */ 686 if (SDL_SYS_SetDirection(dest, &hap_constant->direction, dest->cAxes) < 0) { 687 return -1; 688 } 689 690 /* Envelope */ 691 if ((hap_constant->attack_length == 0) 692 && (hap_constant->fade_length == 0)) { 693 SDL_free(dest->lpEnvelope); 694 dest->lpEnvelope = NULL; 695 } else { 696 envelope->dwAttackLevel = CCONVERT(hap_constant->attack_level); 697 envelope->dwAttackTime = hap_constant->attack_length * 1000; 698 envelope->dwFadeLevel = CCONVERT(hap_constant->fade_level); 699 envelope->dwFadeTime = hap_constant->fade_length * 1000; 700 } 701 702 break; 703 704 case SDL_HAPTIC_SINE: 705 /* !!! FIXME: put this back when we have more bits in 2.1 */ 706 /* case SDL_HAPTIC_SQUARE: */ 707 case SDL_HAPTIC_TRIANGLE: 708 case SDL_HAPTIC_SAWTOOTHUP: 709 case SDL_HAPTIC_SAWTOOTHDOWN: 710 hap_periodic = &src->periodic; 711 periodic = SDL_malloc(sizeof(DIPERIODIC)); 712 if (periodic == NULL) { 713 return SDL_OutOfMemory(); 714 } 715 SDL_memset(periodic, 0, sizeof(DIPERIODIC)); 716 717 /* Specifics */ 718 periodic->dwMagnitude = CONVERT(SDL_abs(hap_periodic->magnitude)); 719 periodic->lOffset = CONVERT(hap_periodic->offset); 720 periodic->dwPhase = 721 (hap_periodic->phase + (hap_periodic->magnitude < 0 ? 18000 : 0)) % 36000; 722 periodic->dwPeriod = hap_periodic->period * 1000; 723 dest->cbTypeSpecificParams = sizeof(DIPERIODIC); 724 dest->lpvTypeSpecificParams = periodic; 725 726 /* Generics */ 727 dest->dwDuration = hap_periodic->length * 1000; /* In microseconds. */ 728 dest->dwTriggerButton = DIGetTriggerButton(hap_periodic->button); 729 dest->dwTriggerRepeatInterval = hap_periodic->interval; 730 dest->dwStartDelay = hap_periodic->delay * 1000; /* In microseconds. */ 731 732 /* Direction. */ 733 if (SDL_SYS_SetDirection(dest, &hap_periodic->direction, dest->cAxes) 734 < 0) { 735 return -1; 736 } 737 738 /* Envelope */ 739 if ((hap_periodic->attack_length == 0) 740 && (hap_periodic->fade_length == 0)) { 741 SDL_free(dest->lpEnvelope); 742 dest->lpEnvelope = NULL; 743 } else { 744 envelope->dwAttackLevel = CCONVERT(hap_periodic->attack_level); 745 envelope->dwAttackTime = hap_periodic->attack_length * 1000; 746 envelope->dwFadeLevel = CCONVERT(hap_periodic->fade_level); 747 envelope->dwFadeTime = hap_periodic->fade_length * 1000; 748 } 749 750 break; 751 752 case SDL_HAPTIC_SPRING: 753 case SDL_HAPTIC_DAMPER: 754 case SDL_HAPTIC_INERTIA: 755 case SDL_HAPTIC_FRICTION: 756 hap_condition = &src->condition; 757 condition = SDL_malloc(sizeof(DICONDITION) * dest->cAxes); 758 if (condition == NULL) { 759 return SDL_OutOfMemory(); 760 } 761 SDL_memset(condition, 0, sizeof(DICONDITION)); 762 763 /* Specifics */ 764 for (i = 0; i < (int) dest->cAxes; i++) { 765 condition[i].lOffset = CONVERT(hap_condition->center[i]); 766 condition[i].lPositiveCoefficient = 767 CONVERT(hap_condition->right_coeff[i]); 768 condition[i].lNegativeCoefficient = 769 CONVERT(hap_condition->left_coeff[i]); 770 condition[i].dwPositiveSaturation = 771 CCONVERT(hap_condition->right_sat[i] / 2); 772 condition[i].dwNegativeSaturation = 773 CCONVERT(hap_condition->left_sat[i] / 2); 774 condition[i].lDeadBand = CCONVERT(hap_condition->deadband[i] / 2); 775 } 776 dest->cbTypeSpecificParams = sizeof(DICONDITION) * dest->cAxes; 777 dest->lpvTypeSpecificParams = condition; 778 779 /* Generics */ 780 dest->dwDuration = hap_condition->length * 1000; /* In microseconds. */ 781 dest->dwTriggerButton = DIGetTriggerButton(hap_condition->button); 782 dest->dwTriggerRepeatInterval = hap_condition->interval; 783 dest->dwStartDelay = hap_condition->delay * 1000; /* In microseconds. */ 784 785 /* Direction. */ 786 if (SDL_SYS_SetDirection(dest, &hap_condition->direction, dest->cAxes) 787 < 0) { 788 return -1; 789 } 790 791 /* Envelope - Not actually supported by most CONDITION implementations. */ 792 SDL_free(dest->lpEnvelope); 793 dest->lpEnvelope = NULL; 794 795 break; 796 797 case SDL_HAPTIC_RAMP: 798 hap_ramp = &src->ramp; 799 ramp = SDL_malloc(sizeof(DIRAMPFORCE)); 800 if (ramp == NULL) { 801 return SDL_OutOfMemory(); 802 } 803 SDL_memset(ramp, 0, sizeof(DIRAMPFORCE)); 804 805 /* Specifics */ 806 ramp->lStart = CONVERT(hap_ramp->start); 807 ramp->lEnd = CONVERT(hap_ramp->end); 808 dest->cbTypeSpecificParams = sizeof(DIRAMPFORCE); 809 dest->lpvTypeSpecificParams = ramp; 810 811 /* Generics */ 812 dest->dwDuration = hap_ramp->length * 1000; /* In microseconds. */ 813 dest->dwTriggerButton = DIGetTriggerButton(hap_ramp->button); 814 dest->dwTriggerRepeatInterval = hap_ramp->interval; 815 dest->dwStartDelay = hap_ramp->delay * 1000; /* In microseconds. */ 816 817 /* Direction. */ 818 if (SDL_SYS_SetDirection(dest, &hap_ramp->direction, dest->cAxes) < 0) { 819 return -1; 820 } 821 822 /* Envelope */ 823 if ((hap_ramp->attack_length == 0) && (hap_ramp->fade_length == 0)) { 824 SDL_free(dest->lpEnvelope); 825 dest->lpEnvelope = NULL; 826 } else { 827 envelope->dwAttackLevel = CCONVERT(hap_ramp->attack_level); 828 envelope->dwAttackTime = hap_ramp->attack_length * 1000; 829 envelope->dwFadeLevel = CCONVERT(hap_ramp->fade_level); 830 envelope->dwFadeTime = hap_ramp->fade_length * 1000; 831 } 832 833 break; 834 835 case SDL_HAPTIC_CUSTOM: 836 hap_custom = &src->custom; 837 custom = SDL_malloc(sizeof(DICUSTOMFORCE)); 838 if (custom == NULL) { 839 return SDL_OutOfMemory(); 840 } 841 SDL_memset(custom, 0, sizeof(DICUSTOMFORCE)); 842 843 /* Specifics */ 844 custom->cChannels = hap_custom->channels; 845 custom->dwSamplePeriod = hap_custom->period * 1000; 846 custom->cSamples = hap_custom->samples; 847 custom->rglForceData = 848 SDL_malloc(sizeof(LONG) * custom->cSamples * custom->cChannels); 849 for (i = 0; i < hap_custom->samples * hap_custom->channels; i++) { /* Copy data. */ 850 custom->rglForceData[i] = CCONVERT(hap_custom->data[i]); 851 } 852 dest->cbTypeSpecificParams = sizeof(DICUSTOMFORCE); 853 dest->lpvTypeSpecificParams = custom; 854 855 /* Generics */ 856 dest->dwDuration = hap_custom->length * 1000; /* In microseconds. */ 857 dest->dwTriggerButton = DIGetTriggerButton(hap_custom->button); 858 dest->dwTriggerRepeatInterval = hap_custom->interval; 859 dest->dwStartDelay = hap_custom->delay * 1000; /* In microseconds. */ 860 861 /* Direction. */ 862 if (SDL_SYS_SetDirection(dest, &hap_custom->direction, dest->cAxes) < 0) { 863 return -1; 864 } 865 866 /* Envelope */ 867 if ((hap_custom->attack_length == 0) 868 && (hap_custom->fade_length == 0)) { 869 SDL_free(dest->lpEnvelope); 870 dest->lpEnvelope = NULL; 871 } else { 872 envelope->dwAttackLevel = CCONVERT(hap_custom->attack_level); 873 envelope->dwAttackTime = hap_custom->attack_length * 1000; 874 envelope->dwFadeLevel = CCONVERT(hap_custom->fade_level); 875 envelope->dwFadeTime = hap_custom->fade_length * 1000; 876 } 877 878 break; 879 880 default: 881 return SDL_SetError("Haptic: Unknown effect type."); 882 } 883 884 return 0; 885} 886 887 888/* 889 * Frees an DIEFFECT allocated by SDL_SYS_ToDIEFFECT. 890 */ 891static void 892SDL_SYS_HapticFreeDIEFFECT(DIEFFECT * effect, int type) 893{ 894 DICUSTOMFORCE *custom; 895 896 SDL_free(effect->lpEnvelope); 897 effect->lpEnvelope = NULL; 898 SDL_free(effect->rgdwAxes); 899 effect->rgdwAxes = NULL; 900 if (effect->lpvTypeSpecificParams != NULL) { 901 if (type == SDL_HAPTIC_CUSTOM) { /* Must free the custom data. */ 902 custom = (DICUSTOMFORCE *) effect->lpvTypeSpecificParams; 903 SDL_free(custom->rglForceData); 904 custom->rglForceData = NULL; 905 } 906 SDL_free(effect->lpvTypeSpecificParams); 907 effect->lpvTypeSpecificParams = NULL; 908 } 909 SDL_free(effect->rglDirection); 910 effect->rglDirection = NULL; 911} 912 913/* 914 * Gets the effect type from the generic SDL haptic effect wrapper. 915 */ 916static REFGUID 917SDL_SYS_HapticEffectType(SDL_HapticEffect * effect) 918{ 919 switch (effect->type) { 920 case SDL_HAPTIC_CONSTANT: 921 return &GUID_ConstantForce; 922 923 case SDL_HAPTIC_RAMP: 924 return &GUID_RampForce; 925 926 /* !!! FIXME: put this back when we have more bits in 2.1 */ 927 /* case SDL_HAPTIC_SQUARE: 928 return &GUID_Square; */ 929 930 case SDL_HAPTIC_SINE: 931 return &GUID_Sine; 932 933 case SDL_HAPTIC_TRIANGLE: 934 return &GUID_Triangle; 935 936 case SDL_HAPTIC_SAWTOOTHUP: 937 return &GUID_SawtoothUp; 938 939 case SDL_HAPTIC_SAWTOOTHDOWN: 940 return &GUID_SawtoothDown; 941 942 case SDL_HAPTIC_SPRING: 943 return &GUID_Spring; 944 945 case SDL_HAPTIC_DAMPER: 946 return &GUID_Damper; 947 948 case SDL_HAPTIC_INERTIA: 949 return &GUID_Inertia; 950 951 case SDL_HAPTIC_FRICTION: 952 return &GUID_Friction; 953 954 case SDL_HAPTIC_CUSTOM: 955 return &GUID_CustomForce; 956 957 default: 958 return NULL; 959 } 960} 961int 962SDL_DINPUT_HapticNewEffect(SDL_Haptic * haptic, struct haptic_effect *effect, SDL_HapticEffect * base) 963{ 964 HRESULT ret; 965 REFGUID type = SDL_SYS_HapticEffectType(base); 966 967 if (type == NULL) { 968 SDL_SetError("Haptic: Unknown effect type."); 969 return -1; 970 } 971 972 /* Get the effect. */ 973 if (SDL_SYS_ToDIEFFECT(haptic, &effect->hweffect->effect, base) < 0) { 974 goto err_effectdone; 975 } 976 977 /* Create the actual effect. */ 978 ret = IDirectInputDevice8_CreateEffect(haptic->hwdata->device, type, 979 &effect->hweffect->effect, 980 &effect->hweffect->ref, NULL); 981 if (FAILED(ret)) { 982 DI_SetError("Unable to create effect", ret); 983 goto err_effectdone; 984 } 985 986 return 0; 987 988err_effectdone: 989 SDL_SYS_HapticFreeDIEFFECT(&effect->hweffect->effect, base->type); 990 return -1; 991} 992 993int 994SDL_DINPUT_HapticUpdateEffect(SDL_Haptic * haptic, struct haptic_effect *effect, SDL_HapticEffect * data) 995{ 996 HRESULT ret; 997 DWORD flags; 998 DIEFFECT temp; 999 1000 /* Get the effect. */ 1001 SDL_memset(&temp, 0, sizeof(DIEFFECT)); 1002 if (SDL_SYS_ToDIEFFECT(haptic, &temp, data) < 0) { 1003 goto err_update; 1004 } 1005 1006 /* Set the flags. Might be worthwhile to diff temp with loaded effect and 1007 * only change those parameters. */ 1008 flags = DIEP_DIRECTION | 1009 DIEP_DURATION | 1010 DIEP_ENVELOPE | 1011 DIEP_STARTDELAY | 1012 DIEP_TRIGGERBUTTON | 1013 DIEP_TRIGGERREPEATINTERVAL | DIEP_TYPESPECIFICPARAMS; 1014 1015 /* Create the actual effect. */ 1016 ret = 1017 IDirectInputEffect_SetParameters(effect->hweffect->ref, &temp, flags); 1018 if (FAILED(ret)) { 1019 DI_SetError("Unable to update effect", ret); 1020 goto err_update; 1021 } 1022 1023 /* Copy it over. */ 1024 SDL_SYS_HapticFreeDIEFFECT(&effect->hweffect->effect, data->type); 1025 SDL_memcpy(&effect->hweffect->effect, &temp, sizeof(DIEFFECT)); 1026 1027 return 0; 1028 1029err_update: 1030 SDL_SYS_HapticFreeDIEFFECT(&temp, data->type); 1031 return -1; 1032} 1033 1034int 1035SDL_DINPUT_HapticRunEffect(SDL_Haptic * haptic, struct haptic_effect *effect, Uint32 iterations) 1036{ 1037 HRESULT ret; 1038 DWORD iter; 1039 1040 /* Check if it's infinite. */ 1041 if (iterations == SDL_HAPTIC_INFINITY) { 1042 iter = INFINITE; 1043 } else { 1044 iter = iterations; 1045 } 1046 1047 /* Run the effect. */ 1048 ret = IDirectInputEffect_Start(effect->hweffect->ref, iter, 0); 1049 if (FAILED(ret)) { 1050 return DI_SetError("Running the effect", ret); 1051 } 1052 return 0; 1053} 1054 1055int 1056SDL_DINPUT_HapticStopEffect(SDL_Haptic * haptic, struct haptic_effect *effect) 1057{ 1058 HRESULT ret; 1059 1060 ret = IDirectInputEffect_Stop(effect->hweffect->ref); 1061 if (FAILED(ret)) { 1062 return DI_SetError("Unable to stop effect", ret); 1063 } 1064 return 0; 1065} 1066 1067void 1068SDL_DINPUT_HapticDestroyEffect(SDL_Haptic * haptic, struct haptic_effect *effect) 1069{ 1070 HRESULT ret; 1071 1072 ret = IDirectInputEffect_Unload(effect->hweffect->ref); 1073 if (FAILED(ret)) { 1074 DI_SetError("Removing effect from the device", ret); 1075 } 1076 SDL_SYS_HapticFreeDIEFFECT(&effect->hweffect->effect, effect->effect.type); 1077} 1078 1079int 1080SDL_DINPUT_HapticGetEffectStatus(SDL_Haptic * haptic, struct haptic_effect *effect) 1081{ 1082 HRESULT ret; 1083 DWORD status; 1084 1085 ret = IDirectInputEffect_GetEffectStatus(effect->hweffect->ref, &status); 1086 if (FAILED(ret)) { 1087 return DI_SetError("Getting effect status", ret); 1088 } 1089 1090 if (status == 0) 1091 return SDL_FALSE; 1092 return SDL_TRUE; 1093} 1094 1095int 1096SDL_DINPUT_HapticSetGain(SDL_Haptic * haptic, int gain) 1097{ 1098 HRESULT ret; 1099 DIPROPDWORD dipdw; 1100 1101 /* Create the weird structure thingy. */ 1102 dipdw.diph.dwSize = sizeof(DIPROPDWORD); 1103 dipdw.diph.dwHeaderSize = sizeof(DIPROPHEADER); 1104 dipdw.diph.dwObj = 0; 1105 dipdw.diph.dwHow = DIPH_DEVICE; 1106 dipdw.dwData = gain * 100; /* 0 to 10,000 */ 1107 1108 /* Try to set the autocenter. */ 1109 ret = IDirectInputDevice8_SetProperty(haptic->hwdata->device, 1110 DIPROP_FFGAIN, &dipdw.diph); 1111 if (FAILED(ret)) { 1112 return DI_SetError("Setting gain", ret); 1113 } 1114 return 0; 1115} 1116 1117int 1118SDL_DINPUT_HapticSetAutocenter(SDL_Haptic * haptic, int autocenter) 1119{ 1120 HRESULT ret; 1121 DIPROPDWORD dipdw; 1122 1123 /* Create the weird structure thingy. */ 1124 dipdw.diph.dwSize = sizeof(DIPROPDWORD); 1125 dipdw.diph.dwHeaderSize = sizeof(DIPROPHEADER); 1126 dipdw.diph.dwObj = 0; 1127 dipdw.diph.dwHow = DIPH_DEVICE; 1128 dipdw.dwData = (autocenter == 0) ? DIPROPAUTOCENTER_OFF : 1129 DIPROPAUTOCENTER_ON; 1130 1131 /* Try to set the autocenter. */ 1132 ret = IDirectInputDevice8_SetProperty(haptic->hwdata->device, 1133 DIPROP_AUTOCENTER, &dipdw.diph); 1134 if (FAILED(ret)) { 1135 return DI_SetError("Setting autocenter", ret); 1136 } 1137 return 0; 1138} 1139 1140int 1141SDL_DINPUT_HapticPause(SDL_Haptic * haptic) 1142{ 1143 HRESULT ret; 1144 1145 /* Pause the device. */ 1146 ret = IDirectInputDevice8_SendForceFeedbackCommand(haptic->hwdata->device, 1147 DISFFC_PAUSE); 1148 if (FAILED(ret)) { 1149 return DI_SetError("Pausing the device", ret); 1150 } 1151 return 0; 1152} 1153 1154int 1155SDL_DINPUT_HapticUnpause(SDL_Haptic * haptic) 1156{ 1157 HRESULT ret; 1158 1159 /* Unpause the device. */ 1160 ret = IDirectInputDevice8_SendForceFeedbackCommand(haptic->hwdata->device, 1161 DISFFC_CONTINUE); 1162 if (FAILED(ret)) { 1163 return DI_SetError("Pausing the device", ret); 1164 } 1165 return 0; 1166} 1167 1168int 1169SDL_DINPUT_HapticStopAll(SDL_Haptic * haptic) 1170{ 1171 HRESULT ret; 1172 1173 /* Try to stop the effects. */ 1174 ret = IDirectInputDevice8_SendForceFeedbackCommand(haptic->hwdata->device, 1175 DISFFC_STOPALL); 1176 if (FAILED(ret)) { 1177 return DI_SetError("Stopping the device", ret); 1178 } 1179 return 0; 1180} 1181 1182#else /* !SDL_HAPTIC_DINPUT */ 1183 1184 1185int 1186SDL_DINPUT_HapticInit(void) 1187{ 1188 return 0; 1189} 1190 1191int 1192SDL_DINPUT_MaybeAddDevice(const DIDEVICEINSTANCE * pdidInstance) 1193{ 1194 return SDL_Unsupported(); 1195} 1196 1197int 1198SDL_DINPUT_MaybeRemoveDevice(const DIDEVICEINSTANCE * pdidInstance) 1199{ 1200 return SDL_Unsupported(); 1201} 1202 1203int 1204SDL_DINPUT_HapticOpen(SDL_Haptic * haptic, SDL_hapticlist_item *item) 1205{ 1206 return SDL_Unsupported(); 1207} 1208 1209int 1210SDL_DINPUT_JoystickSameHaptic(SDL_Haptic * haptic, SDL_Joystick * joystick) 1211{ 1212 return SDL_Unsupported(); 1213} 1214 1215int 1216SDL_DINPUT_HapticOpenFromJoystick(SDL_Haptic * haptic, SDL_Joystick * joystick) 1217{ 1218 return SDL_Unsupported(); 1219} 1220 1221void 1222SDL_DINPUT_HapticClose(SDL_Haptic * haptic) 1223{ 1224} 1225 1226void 1227SDL_DINPUT_HapticQuit(void) 1228{ 1229} 1230 1231int 1232SDL_DINPUT_HapticNewEffect(SDL_Haptic * haptic, struct haptic_effect *effect, SDL_HapticEffect * base) 1233{ 1234 return SDL_Unsupported(); 1235} 1236 1237int 1238SDL_DINPUT_HapticUpdateEffect(SDL_Haptic * haptic, struct haptic_effect *effect, SDL_HapticEffect * data) 1239{ 1240 return SDL_Unsupported(); 1241} 1242 1243int 1244SDL_DINPUT_HapticRunEffect(SDL_Haptic * haptic, struct haptic_effect *effect, Uint32 iterations) 1245{ 1246 return SDL_Unsupported(); 1247} 1248 1249int 1250SDL_DINPUT_HapticStopEffect(SDL_Haptic * haptic, struct haptic_effect *effect) 1251{ 1252 return SDL_Unsupported(); 1253} 1254 1255void 1256SDL_DINPUT_HapticDestroyEffect(SDL_Haptic * haptic, struct haptic_effect *effect) 1257{ 1258} 1259 1260int 1261SDL_DINPUT_HapticGetEffectStatus(SDL_Haptic * haptic, struct haptic_effect *effect) 1262{ 1263 return SDL_Unsupported(); 1264} 1265 1266int 1267SDL_DINPUT_HapticSetGain(SDL_Haptic * haptic, int gain) 1268{ 1269 return SDL_Unsupported(); 1270} 1271 1272int 1273SDL_DINPUT_HapticSetAutocenter(SDL_Haptic * haptic, int autocenter) 1274{ 1275 return SDL_Unsupported(); 1276} 1277 1278int 1279SDL_DINPUT_HapticPause(SDL_Haptic * haptic) 1280{ 1281 return SDL_Unsupported(); 1282} 1283 1284int 1285SDL_DINPUT_HapticUnpause(SDL_Haptic * haptic) 1286{ 1287 return SDL_Unsupported(); 1288} 1289 1290int 1291SDL_DINPUT_HapticStopAll(SDL_Haptic * haptic) 1292{ 1293 return SDL_Unsupported(); 1294} 1295 1296#endif /* SDL_HAPTIC_DINPUT */ 1297 1298/* vi: set ts=4 sw=4 expandtab: */