SDL_xinputhaptic.c 13 KB

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