SDL_xinputhaptic.c (13434B)
1/* 2 Simple DirectMedia Layer 3 Copyright (C) 1997-2014 Sam Lantinga <slouken@libsdl.org> 4 5 This software is provided 'as-is', without any express or implied 6 warranty. In no event will the authors be held liable for any damages 7 arising from the use of this software. 8 9 Permission is granted to anyone to use this software for any purpose, 10 including commercial applications, and to alter it and redistribute it 11 freely, subject to the following restrictions: 12 13 1. The origin of this software must not be misrepresented; you must not 14 claim that you wrote the original software. If you use this software 15 in a product, an acknowledgment in the product documentation would be 16 appreciated but is not required. 17 2. Altered source versions must be plainly marked as such, and must not be 18 misrepresented as being the original software. 19 3. This notice may not be removed or altered from any source distribution. 20*/ 21#include "../../SDL_internal.h" 22 23#include "SDL_assert.h" 24#include "SDL_error.h" 25#include "SDL_haptic.h" 26#include "SDL_hints.h" 27#include "SDL_timer.h" 28#include "SDL_windowshaptic_c.h" 29#include "SDL_xinputhaptic_c.h" 30#include "../SDL_syshaptic.h" 31#include "../../core/windows/SDL_xinput.h" 32#include "../../joystick/windows/SDL_windowsjoystick_c.h" 33 34 35#if SDL_HAPTIC_XINPUT 36 37/* 38 * Internal stuff. 39 */ 40static SDL_bool loaded_xinput = SDL_FALSE; 41 42 43int 44SDL_XINPUT_HapticInit(void) 45{ 46 const char *env = SDL_GetHint(SDL_HINT_XINPUT_ENABLED); 47 if (!env || SDL_atoi(env)) { 48 loaded_xinput = (WIN_LoadXInputDLL() == 0); 49 } 50 51 if (loaded_xinput) { 52 DWORD i; 53 for (i = 0; i < XUSER_MAX_COUNT; i++) { 54 SDL_XINPUT_MaybeAddDevice(i); 55 } 56 } 57 return 0; 58} 59 60int 61SDL_XINPUT_MaybeAddDevice(const DWORD dwUserid) 62{ 63 const Uint8 userid = (Uint8)dwUserid; 64 SDL_hapticlist_item *item; 65 XINPUT_VIBRATION state; 66 67 if ((!loaded_xinput) || (dwUserid >= XUSER_MAX_COUNT)) { 68 return -1; 69 } 70 71 /* Make sure we don't already have it */ 72 for (item = SDL_hapticlist; item; item = item->next) { 73 if (item->bXInputHaptic && item->userid == userid) { 74 return -1; /* Already added */ 75 } 76 } 77 78 SDL_zero(state); 79 if (XINPUTSETSTATE(dwUserid, &state) != ERROR_SUCCESS) { 80 return -1; /* no force feedback on this device. */ 81 } 82 83 item = (SDL_hapticlist_item *)SDL_malloc(sizeof(SDL_hapticlist_item)); 84 if (item == NULL) { 85 return SDL_OutOfMemory(); 86 } 87 88 SDL_zerop(item); 89 90 /* !!! FIXME: I'm not bothering to query for a real name right now (can we even?) */ 91 { 92 char buf[64]; 93 SDL_snprintf(buf, sizeof(buf), "XInput Controller #%u", (unsigned int)(userid + 1)); 94 item->name = SDL_strdup(buf); 95 } 96 97 if (!item->name) { 98 SDL_free(item); 99 return -1; 100 } 101 102 /* Copy the instance over, useful for creating devices. */ 103 item->bXInputHaptic = SDL_TRUE; 104 item->userid = userid; 105 106 return SDL_SYS_AddHapticDevice(item); 107} 108 109int 110SDL_XINPUT_MaybeRemoveDevice(const DWORD dwUserid) 111{ 112 const Uint8 userid = (Uint8)dwUserid; 113 SDL_hapticlist_item *item; 114 SDL_hapticlist_item *prev = NULL; 115 116 if ((!loaded_xinput) || (dwUserid >= XUSER_MAX_COUNT)) { 117 return -1; 118 } 119 120 for (item = SDL_hapticlist; item != NULL; item = item->next) { 121 if (item->bXInputHaptic && item->userid == userid) { 122 /* found it, remove it. */ 123 return SDL_SYS_RemoveHapticDevice(prev, item); 124 } 125 prev = item; 126 } 127 return -1; 128} 129 130/* !!! FIXME: this is a hack, remove this later. */ 131/* Since XInput doesn't offer a way to vibrate for X time, we hook into 132 * SDL_PumpEvents() to check if it's time to stop vibrating with some 133 * frequency. 134 * In practice, this works for 99% of use cases. But in an ideal world, 135 * we do this in a separate thread so that: 136 * - we aren't bound to when the app chooses to pump the event queue. 137 * - we aren't adding more polling to the event queue 138 * - we can emulate all the haptic effects correctly (start on a delay, 139 * mix multiple effects, etc). 140 * 141 * Mostly, this is here to get rumbling to work, and all the other features 142 * are absent in the XInput path for now. :( 143 */ 144static int SDLCALL 145SDL_RunXInputHaptic(void *arg) 146{ 147 struct haptic_hwdata *hwdata = (struct haptic_hwdata *) arg; 148 149 while (!hwdata->stopThread) { 150 SDL_Delay(50); 151 SDL_LockMutex(hwdata->mutex); 152 /* If we're currently running and need to stop... */ 153 if (hwdata->stopTicks) { 154 if ((hwdata->stopTicks != SDL_HAPTIC_INFINITY) && SDL_TICKS_PASSED(SDL_GetTicks(), hwdata->stopTicks)) { 155 XINPUT_VIBRATION vibration = { 0, 0 }; 156 hwdata->stopTicks = 0; 157 XINPUTSETSTATE(hwdata->userid, &vibration); 158 } 159 } 160 SDL_UnlockMutex(hwdata->mutex); 161 } 162 163 return 0; 164} 165 166static int 167SDL_XINPUT_HapticOpenFromUserIndex(SDL_Haptic *haptic, const Uint8 userid) 168{ 169 char threadName[32]; 170 XINPUT_VIBRATION vibration = { 0, 0 }; /* stop any current vibration */ 171 XINPUTSETSTATE(userid, &vibration); 172 173 haptic->supported = SDL_HAPTIC_LEFTRIGHT; 174 175 haptic->neffects = 1; 176 haptic->nplaying = 1; 177 178 /* Prepare effects memory. */ 179 haptic->effects = (struct haptic_effect *) 180 SDL_malloc(sizeof(struct haptic_effect) * haptic->neffects); 181 if (haptic->effects == NULL) { 182 return SDL_OutOfMemory(); 183 } 184 /* Clear the memory */ 185 SDL_memset(haptic->effects, 0, 186 sizeof(struct haptic_effect) * haptic->neffects); 187 188 haptic->hwdata = (struct haptic_hwdata *) SDL_malloc(sizeof(*haptic->hwdata)); 189 if (haptic->hwdata == NULL) { 190 SDL_free(haptic->effects); 191 haptic->effects = NULL; 192 return SDL_OutOfMemory(); 193 } 194 SDL_memset(haptic->hwdata, 0, sizeof(*haptic->hwdata)); 195 196 haptic->hwdata->bXInputHaptic = 1; 197 haptic->hwdata->userid = userid; 198 199 haptic->hwdata->mutex = SDL_CreateMutex(); 200 if (haptic->hwdata->mutex == NULL) { 201 SDL_free(haptic->effects); 202 SDL_free(haptic->hwdata); 203 haptic->effects = NULL; 204 return SDL_SetError("Couldn't create XInput haptic mutex"); 205 } 206 207 SDL_snprintf(threadName, sizeof(threadName), "SDLXInputDev%d", (int)userid); 208 209#if defined(__WIN32__) && !defined(HAVE_LIBC) /* !!! FIXME: this is nasty. */ 210#undef SDL_CreateThread 211#if SDL_DYNAMIC_API 212 haptic->hwdata->thread = SDL_CreateThread_REAL(SDL_RunXInputHaptic, threadName, haptic->hwdata, NULL, NULL); 213#else 214 haptic->hwdata->thread = SDL_CreateThread(SDL_RunXInputHaptic, threadName, haptic->hwdata, NULL, NULL); 215#endif 216#else 217 haptic->hwdata->thread = SDL_CreateThread(SDL_RunXInputHaptic, threadName, haptic->hwdata); 218#endif 219 if (haptic->hwdata->thread == NULL) { 220 SDL_DestroyMutex(haptic->hwdata->mutex); 221 SDL_free(haptic->effects); 222 SDL_free(haptic->hwdata); 223 haptic->effects = NULL; 224 return SDL_SetError("Couldn't create XInput haptic thread"); 225 } 226 227 return 0; 228} 229 230int 231SDL_XINPUT_HapticOpen(SDL_Haptic * haptic, SDL_hapticlist_item *item) 232{ 233 return SDL_XINPUT_HapticOpenFromUserIndex(haptic, item->userid); 234} 235 236int 237SDL_XINPUT_JoystickSameHaptic(SDL_Haptic * haptic, SDL_Joystick * joystick) 238{ 239 return (haptic->hwdata->userid == joystick->hwdata->userid); 240} 241 242int 243SDL_XINPUT_HapticOpenFromJoystick(SDL_Haptic * haptic, SDL_Joystick * joystick) 244{ 245 SDL_hapticlist_item *item; 246 int index = 0; 247 248 /* Since it comes from a joystick we have to try to match it with a haptic device on our haptic list. */ 249 for (item = SDL_hapticlist; item != NULL; item = item->next) { 250 if (item->bXInputHaptic && item->userid == joystick->hwdata->userid) { 251 haptic->index = index; 252 return SDL_XINPUT_HapticOpenFromUserIndex(haptic, joystick->hwdata->userid); 253 } 254 ++index; 255 } 256 257 SDL_SetError("Couldn't find joystick in haptic device list"); 258 return -1; 259} 260 261void 262SDL_XINPUT_HapticClose(SDL_Haptic * haptic) 263{ 264 haptic->hwdata->stopThread = 1; 265 SDL_WaitThread(haptic->hwdata->thread, NULL); 266 SDL_DestroyMutex(haptic->hwdata->mutex); 267} 268 269void 270SDL_XINPUT_HapticQuit(void) 271{ 272 if (loaded_xinput) { 273 WIN_UnloadXInputDLL(); 274 loaded_xinput = SDL_FALSE; 275 } 276} 277 278int 279SDL_XINPUT_HapticNewEffect(SDL_Haptic * haptic, struct haptic_effect *effect, SDL_HapticEffect * base) 280{ 281 SDL_assert(base->type == SDL_HAPTIC_LEFTRIGHT); /* should catch this at higher level */ 282 return SDL_XINPUT_HapticUpdateEffect(haptic, effect, base); 283} 284 285int 286SDL_XINPUT_HapticUpdateEffect(SDL_Haptic * haptic, struct haptic_effect *effect, SDL_HapticEffect * data) 287{ 288 XINPUT_VIBRATION *vib = &effect->hweffect->vibration; 289 SDL_assert(data->type == SDL_HAPTIC_LEFTRIGHT); 290 vib->wLeftMotorSpeed = data->leftright.large_magnitude; 291 vib->wRightMotorSpeed = data->leftright.small_magnitude; 292 SDL_LockMutex(haptic->hwdata->mutex); 293 if (haptic->hwdata->stopTicks) { /* running right now? Update it. */ 294 XINPUTSETSTATE(haptic->hwdata->userid, vib); 295 } 296 SDL_UnlockMutex(haptic->hwdata->mutex); 297 return 0; 298} 299 300int 301SDL_XINPUT_HapticRunEffect(SDL_Haptic * haptic, struct haptic_effect *effect, Uint32 iterations) 302{ 303 XINPUT_VIBRATION *vib = &effect->hweffect->vibration; 304 SDL_assert(effect->effect.type == SDL_HAPTIC_LEFTRIGHT); /* should catch this at higher level */ 305 SDL_LockMutex(haptic->hwdata->mutex); 306 if (effect->effect.leftright.length == SDL_HAPTIC_INFINITY || iterations == SDL_HAPTIC_INFINITY) { 307 haptic->hwdata->stopTicks = SDL_HAPTIC_INFINITY; 308 } else if ((!effect->effect.leftright.length) || (!iterations)) { 309 /* do nothing. Effect runs for zero milliseconds. */ 310 } else { 311 haptic->hwdata->stopTicks = SDL_GetTicks() + (effect->effect.leftright.length * iterations); 312 if ((haptic->hwdata->stopTicks == SDL_HAPTIC_INFINITY) || (haptic->hwdata->stopTicks == 0)) { 313 haptic->hwdata->stopTicks = 1; /* fix edge cases. */ 314 } 315 } 316 SDL_UnlockMutex(haptic->hwdata->mutex); 317 return (XINPUTSETSTATE(haptic->hwdata->userid, vib) == ERROR_SUCCESS) ? 0 : -1; 318} 319 320int 321SDL_XINPUT_HapticStopEffect(SDL_Haptic * haptic, struct haptic_effect *effect) 322{ 323 XINPUT_VIBRATION vibration = { 0, 0 }; 324 SDL_LockMutex(haptic->hwdata->mutex); 325 haptic->hwdata->stopTicks = 0; 326 SDL_UnlockMutex(haptic->hwdata->mutex); 327 return (XINPUTSETSTATE(haptic->hwdata->userid, &vibration) == ERROR_SUCCESS) ? 0 : -1; 328} 329 330void 331SDL_XINPUT_HapticDestroyEffect(SDL_Haptic * haptic, struct haptic_effect *effect) 332{ 333 SDL_XINPUT_HapticStopEffect(haptic, effect); 334} 335 336int 337SDL_XINPUT_HapticGetEffectStatus(SDL_Haptic * haptic, struct haptic_effect *effect) 338{ 339 return SDL_Unsupported(); 340} 341 342int 343SDL_XINPUT_HapticSetGain(SDL_Haptic * haptic, int gain) 344{ 345 return SDL_Unsupported(); 346} 347 348int 349SDL_XINPUT_HapticSetAutocenter(SDL_Haptic * haptic, int autocenter) 350{ 351 return SDL_Unsupported(); 352} 353 354int 355SDL_XINPUT_HapticPause(SDL_Haptic * haptic) 356{ 357 return SDL_Unsupported(); 358} 359 360int 361SDL_XINPUT_HapticUnpause(SDL_Haptic * haptic) 362{ 363 return SDL_Unsupported(); 364} 365 366int 367SDL_XINPUT_HapticStopAll(SDL_Haptic * haptic) 368{ 369 XINPUT_VIBRATION vibration = { 0, 0 }; 370 SDL_LockMutex(haptic->hwdata->mutex); 371 haptic->hwdata->stopTicks = 0; 372 SDL_UnlockMutex(haptic->hwdata->mutex); 373 return (XINPUTSETSTATE(haptic->hwdata->userid, &vibration) == ERROR_SUCCESS) ? 0 : -1; 374} 375 376#else /* !SDL_HAPTIC_XINPUT */ 377 378 379int 380SDL_XINPUT_HapticInit(void) 381{ 382 return 0; 383} 384 385int 386SDL_XINPUT_MaybeAddDevice(const DWORD dwUserid) 387{ 388 return SDL_Unsupported(); 389} 390 391int 392SDL_XINPUT_MaybeRemoveDevice(const DWORD dwUserid) 393{ 394 return SDL_Unsupported(); 395} 396 397int 398SDL_XINPUT_HapticOpen(SDL_Haptic * haptic, SDL_hapticlist_item *item) 399{ 400 return SDL_Unsupported(); 401} 402 403int 404SDL_XINPUT_JoystickSameHaptic(SDL_Haptic * haptic, SDL_Joystick * joystick) 405{ 406 return SDL_Unsupported(); 407} 408 409int 410SDL_XINPUT_HapticOpenFromJoystick(SDL_Haptic * haptic, SDL_Joystick * joystick) 411{ 412 return SDL_Unsupported(); 413} 414 415void 416SDL_XINPUT_HapticClose(SDL_Haptic * haptic) 417{ 418} 419 420void 421SDL_XINPUT_HapticQuit(void) 422{ 423} 424 425int 426SDL_XINPUT_HapticNewEffect(SDL_Haptic * haptic, struct haptic_effect *effect, SDL_HapticEffect * base) 427{ 428 return SDL_Unsupported(); 429} 430 431int 432SDL_XINPUT_HapticUpdateEffect(SDL_Haptic * haptic, struct haptic_effect *effect, SDL_HapticEffect * data) 433{ 434 return SDL_Unsupported(); 435} 436 437int 438SDL_XINPUT_HapticRunEffect(SDL_Haptic * haptic, struct haptic_effect *effect, Uint32 iterations) 439{ 440 return SDL_Unsupported(); 441} 442 443int 444SDL_XINPUT_HapticStopEffect(SDL_Haptic * haptic, struct haptic_effect *effect) 445{ 446 return SDL_Unsupported(); 447} 448 449void 450SDL_XINPUT_HapticDestroyEffect(SDL_Haptic * haptic, struct haptic_effect *effect) 451{ 452} 453 454int 455SDL_XINPUT_HapticGetEffectStatus(SDL_Haptic * haptic, struct haptic_effect *effect) 456{ 457 return SDL_Unsupported(); 458} 459 460int 461SDL_XINPUT_HapticSetGain(SDL_Haptic * haptic, int gain) 462{ 463 return SDL_Unsupported(); 464} 465 466int 467SDL_XINPUT_HapticSetAutocenter(SDL_Haptic * haptic, int autocenter) 468{ 469 return SDL_Unsupported(); 470} 471 472int 473SDL_XINPUT_HapticPause(SDL_Haptic * haptic) 474{ 475 return SDL_Unsupported(); 476} 477 478int 479SDL_XINPUT_HapticUnpause(SDL_Haptic * haptic) 480{ 481 return SDL_Unsupported(); 482} 483 484int 485SDL_XINPUT_HapticStopAll(SDL_Haptic * haptic) 486{ 487 return SDL_Unsupported(); 488} 489 490#endif /* SDL_HAPTIC_XINPUT */ 491/* vi: set ts=4 sw=4 expandtab: */