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