Atlas - SDL_dinputhaptic.c

Home / ext / SDL / src / haptic / windows Lines: 1 | Size: 38660 bytes [Download] [Show on GitHub] [Search similar files] [Raw] [Raw (proxy)]
[FILE BEGIN]
1/* 2 Simple DirectMedia Layer 3 Copyright (C) 1997-2025 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_memset(dest, 0, sizeof(DIEFFECT)); 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 SDL_DINPUT_HapticUpdateEffect(SDL_Haptic *haptic, struct haptic_effect *effect, const SDL_HapticEffect *data) 950{ 951 HRESULT ret; 952 DWORD flags; 953 DIEFFECT temp; 954 955 // Get the effect. 956 SDL_memset(&temp, 0, sizeof(DIEFFECT)); 957 if (!SDL_SYS_ToDIEFFECT(haptic, &temp, data)) { 958 goto err_update; 959 } 960 961 /* Set the flags. Might be worthwhile to diff temp with loaded effect and 962 * only change those parameters. */ 963 flags = DIEP_DIRECTION | 964 DIEP_DURATION | 965 DIEP_ENVELOPE | 966 DIEP_STARTDELAY | 967 DIEP_TRIGGERBUTTON | 968 DIEP_TRIGGERREPEATINTERVAL | DIEP_TYPESPECIFICPARAMS; 969 970 // Create the actual effect. 971 ret = 972 IDirectInputEffect_SetParameters(effect->hweffect->ref, &temp, flags); 973 if (ret == DIERR_NOTEXCLUSIVEACQUIRED) { 974 IDirectInputDevice8_Unacquire(haptic->hwdata->device); 975 ret = IDirectInputDevice8_SetCooperativeLevel(haptic->hwdata->device, SDL_HelperWindow, DISCL_EXCLUSIVE | DISCL_BACKGROUND); 976 if (SUCCEEDED(ret)) { 977 ret = DIERR_NOTACQUIRED; 978 } 979 } 980 if (ret == DIERR_INPUTLOST || ret == DIERR_NOTACQUIRED) { 981 ret = IDirectInputDevice8_Acquire(haptic->hwdata->device); 982 if (SUCCEEDED(ret)) { 983 ret = IDirectInputEffect_SetParameters(effect->hweffect->ref, &temp, flags); 984 } 985 } 986 if (FAILED(ret)) { 987 DI_SetError("Unable to update effect", ret); 988 goto err_update; 989 } 990 991 // Copy it over. 992 SDL_SYS_HapticFreeDIEFFECT(&effect->hweffect->effect, data->type); 993 SDL_memcpy(&effect->hweffect->effect, &temp, sizeof(DIEFFECT)); 994 995 return true; 996 997err_update: 998 SDL_SYS_HapticFreeDIEFFECT(&temp, data->type); 999 return false; 1000} 1001 1002bool SDL_DINPUT_HapticRunEffect(SDL_Haptic *haptic, struct haptic_effect *effect, Uint32 iterations) 1003{ 1004 HRESULT ret; 1005 DWORD iter; 1006 1007 // Check if it's infinite. 1008 if (iterations == SDL_HAPTIC_INFINITY) { 1009 iter = INFINITE; 1010 } else { 1011 iter = iterations; 1012 } 1013 1014 // Run the effect. 1015 ret = IDirectInputEffect_Start(effect->hweffect->ref, iter, 0); 1016 if (FAILED(ret)) { 1017 return DI_SetError("Running the effect", ret); 1018 } 1019 return true; 1020} 1021 1022bool SDL_DINPUT_HapticStopEffect(SDL_Haptic *haptic, struct haptic_effect *effect) 1023{ 1024 HRESULT ret; 1025 1026 ret = IDirectInputEffect_Stop(effect->hweffect->ref); 1027 if (FAILED(ret)) { 1028 return DI_SetError("Unable to stop effect", ret); 1029 } 1030 return true; 1031} 1032 1033void SDL_DINPUT_HapticDestroyEffect(SDL_Haptic *haptic, struct haptic_effect *effect) 1034{ 1035 HRESULT ret; 1036 1037 ret = IDirectInputEffect_Unload(effect->hweffect->ref); 1038 if (FAILED(ret)) { 1039 DI_SetError("Removing effect from the device", ret); 1040 } 1041 SDL_SYS_HapticFreeDIEFFECT(&effect->hweffect->effect, effect->effect.type); 1042} 1043 1044int SDL_DINPUT_HapticGetEffectStatus(SDL_Haptic *haptic, struct haptic_effect *effect) 1045{ 1046 HRESULT ret; 1047 DWORD status; 1048 1049 ret = IDirectInputEffect_GetEffectStatus(effect->hweffect->ref, &status); 1050 if (FAILED(ret)) { 1051 DI_SetError("Getting effect status", ret); 1052 return -1; 1053 } 1054 1055 if (status == 0) { 1056 return 0; 1057 } 1058 return 1; 1059} 1060 1061bool SDL_DINPUT_HapticSetGain(SDL_Haptic *haptic, int gain) 1062{ 1063 HRESULT ret; 1064 DIPROPDWORD dipdw; 1065 1066 // Create the weird structure thingy. 1067 dipdw.diph.dwSize = sizeof(DIPROPDWORD); 1068 dipdw.diph.dwHeaderSize = sizeof(DIPROPHEADER); 1069 dipdw.diph.dwObj = 0; 1070 dipdw.diph.dwHow = DIPH_DEVICE; 1071 dipdw.dwData = (DWORD)gain * 100; // 0 to 10,000 1072 1073 // Try to set the autocenter. 1074 ret = IDirectInputDevice8_SetProperty(haptic->hwdata->device, 1075 DIPROP_FFGAIN, &dipdw.diph); 1076 if (FAILED(ret)) { 1077 return DI_SetError("Setting gain", ret); 1078 } 1079 return true; 1080} 1081 1082bool SDL_DINPUT_HapticSetAutocenter(SDL_Haptic *haptic, int autocenter) 1083{ 1084 HRESULT ret; 1085 DIPROPDWORD dipdw; 1086 1087 // Create the weird structure thingy. 1088 dipdw.diph.dwSize = sizeof(DIPROPDWORD); 1089 dipdw.diph.dwHeaderSize = sizeof(DIPROPHEADER); 1090 dipdw.diph.dwObj = 0; 1091 dipdw.diph.dwHow = DIPH_DEVICE; 1092 dipdw.dwData = (autocenter == 0) ? DIPROPAUTOCENTER_OFF : DIPROPAUTOCENTER_ON; 1093 1094 // Try to set the autocenter. 1095 ret = IDirectInputDevice8_SetProperty(haptic->hwdata->device, 1096 DIPROP_AUTOCENTER, &dipdw.diph); 1097 if (FAILED(ret)) { 1098 return DI_SetError("Setting autocenter", ret); 1099 } 1100 return true; 1101} 1102 1103bool SDL_DINPUT_HapticPause(SDL_Haptic *haptic) 1104{ 1105 HRESULT ret; 1106 1107 // Pause the device. 1108 ret = IDirectInputDevice8_SendForceFeedbackCommand(haptic->hwdata->device, 1109 DISFFC_PAUSE); 1110 if (FAILED(ret)) { 1111 return DI_SetError("Pausing the device", ret); 1112 } 1113 return true; 1114} 1115 1116bool SDL_DINPUT_HapticResume(SDL_Haptic *haptic) 1117{ 1118 HRESULT ret; 1119 1120 // Unpause the device. 1121 ret = IDirectInputDevice8_SendForceFeedbackCommand(haptic->hwdata->device, 1122 DISFFC_CONTINUE); 1123 if (FAILED(ret)) { 1124 return DI_SetError("Pausing the device", ret); 1125 } 1126 return true; 1127} 1128 1129bool SDL_DINPUT_HapticStopAll(SDL_Haptic *haptic) 1130{ 1131 HRESULT ret; 1132 1133 // Try to stop the effects. 1134 ret = IDirectInputDevice8_SendForceFeedbackCommand(haptic->hwdata->device, 1135 DISFFC_STOPALL); 1136 if (FAILED(ret)) { 1137 return DI_SetError("Stopping the device", ret); 1138 } 1139 return true; 1140} 1141 1142#else // !SDL_HAPTIC_DINPUT 1143 1144typedef struct DIDEVICEINSTANCE DIDEVICEINSTANCE; 1145typedef struct SDL_hapticlist_item SDL_hapticlist_item; 1146 1147bool SDL_DINPUT_HapticInit(void) 1148{ 1149 return true; 1150} 1151 1152bool SDL_DINPUT_HapticMaybeAddDevice(const DIDEVICEINSTANCE *pdidInstance) 1153{ 1154 return SDL_Unsupported(); 1155} 1156 1157bool SDL_DINPUT_HapticMaybeRemoveDevice(const DIDEVICEINSTANCE *pdidInstance) 1158{ 1159 return SDL_Unsupported(); 1160} 1161 1162bool SDL_DINPUT_HapticOpen(SDL_Haptic *haptic, SDL_hapticlist_item *item) 1163{ 1164 return SDL_Unsupported(); 1165} 1166 1167bool SDL_DINPUT_JoystickSameHaptic(SDL_Haptic *haptic, SDL_Joystick *joystick) 1168{ 1169 return false; 1170} 1171 1172bool SDL_DINPUT_HapticOpenFromJoystick(SDL_Haptic *haptic, SDL_Joystick *joystick) 1173{ 1174 return SDL_Unsupported(); 1175} 1176 1177void SDL_DINPUT_HapticClose(SDL_Haptic *haptic) 1178{ 1179} 1180 1181void SDL_DINPUT_HapticQuit(void) 1182{ 1183} 1184 1185bool SDL_DINPUT_HapticNewEffect(SDL_Haptic *haptic, struct haptic_effect *effect, const SDL_HapticEffect *base) 1186{ 1187 return SDL_Unsupported(); 1188} 1189 1190bool SDL_DINPUT_HapticUpdateEffect(SDL_Haptic *haptic, struct haptic_effect *effect, const SDL_HapticEffect *data) 1191{ 1192 return SDL_Unsupported(); 1193} 1194 1195bool SDL_DINPUT_HapticRunEffect(SDL_Haptic *haptic, struct haptic_effect *effect, Uint32 iterations) 1196{ 1197 return SDL_Unsupported(); 1198} 1199 1200bool SDL_DINPUT_HapticStopEffect(SDL_Haptic *haptic, struct haptic_effect *effect) 1201{ 1202 return SDL_Unsupported(); 1203} 1204 1205void SDL_DINPUT_HapticDestroyEffect(SDL_Haptic *haptic, struct haptic_effect *effect) 1206{ 1207} 1208 1209int SDL_DINPUT_HapticGetEffectStatus(SDL_Haptic *haptic, struct haptic_effect *effect) 1210{ 1211 SDL_Unsupported(); 1212 return -1; 1213} 1214 1215bool SDL_DINPUT_HapticSetGain(SDL_Haptic *haptic, int gain) 1216{ 1217 return SDL_Unsupported(); 1218} 1219 1220bool SDL_DINPUT_HapticSetAutocenter(SDL_Haptic *haptic, int autocenter) 1221{ 1222 return SDL_Unsupported(); 1223} 1224 1225bool SDL_DINPUT_HapticPause(SDL_Haptic *haptic) 1226{ 1227 return SDL_Unsupported(); 1228} 1229 1230bool SDL_DINPUT_HapticResume(SDL_Haptic *haptic) 1231{ 1232 return SDL_Unsupported(); 1233} 1234 1235bool SDL_DINPUT_HapticStopAll(SDL_Haptic *haptic) 1236{ 1237 return SDL_Unsupported(); 1238} 1239 1240#endif // SDL_HAPTIC_DINPUT 1241
[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.