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