SDL_haptic.c 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899
  1. /*
  2. Simple DirectMedia Layer
  3. Copyright (C) 1997-2025 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_syshaptic.h"
  20. #ifdef SDL_JOYSTICK_HIDAPI
  21. #include "hidapi/SDL_hidapihaptic.h"
  22. #endif
  23. #include "SDL_haptic_c.h"
  24. #include "../joystick/SDL_joystick_c.h" // For SDL_IsJoystickValid
  25. #include "../SDL_hints_c.h"
  26. typedef struct SDL_Haptic_VIDPID_Naxes {
  27. Uint16 vid;
  28. Uint16 pid;
  29. Uint16 naxes;
  30. } SDL_Haptic_VIDPID_Naxes;
  31. static void SDL_Haptic_Load_Axes_List(SDL_Haptic_VIDPID_Naxes **entries, int *num_entries)
  32. {
  33. SDL_Haptic_VIDPID_Naxes entry;
  34. const char *spot;
  35. int length = 0;
  36. spot = SDL_GetHint(SDL_HINT_JOYSTICK_HAPTIC_AXES);
  37. if (!spot)
  38. return;
  39. while (SDL_sscanf(spot, "0x%hx/0x%hx/%hu%n", &entry.vid, &entry.pid, &entry.naxes, &length) == 3) {
  40. SDL_assert(length > 0);
  41. spot += length;
  42. length = 0;
  43. if ((*num_entries % 8) == 0) {
  44. int new_max = *num_entries + 8;
  45. SDL_Haptic_VIDPID_Naxes *new_entries =
  46. (SDL_Haptic_VIDPID_Naxes *)SDL_realloc(*entries, new_max * sizeof(**entries));
  47. // Out of memory, go with what we have already
  48. if (!new_entries)
  49. break;
  50. *entries = new_entries;
  51. }
  52. (*entries)[(*num_entries)++] = entry;
  53. if (spot[0] == ',')
  54. spot++;
  55. }
  56. }
  57. // /* Return -1 if not found */
  58. static int SDL_Haptic_Naxes_List_Index(struct SDL_Haptic_VIDPID_Naxes *entries, int num_entries, Uint16 vid, Uint16 pid)
  59. {
  60. if (!entries)
  61. return -1;
  62. int i;
  63. for (i = 0; i < num_entries; ++i) {
  64. if (entries[i].vid == vid && entries[i].pid == pid)
  65. return i;
  66. }
  67. return -1;
  68. }
  69. // Check if device needs a custom number of naxes
  70. static int SDL_Haptic_Get_Naxes(Uint16 vid, Uint16 pid)
  71. {
  72. int num_entries = 0, index = 0, naxes = -1;
  73. SDL_Haptic_VIDPID_Naxes *naxes_list = NULL;
  74. SDL_Haptic_Load_Axes_List(&naxes_list, &num_entries);
  75. if (!num_entries || !naxes_list)
  76. return -1;
  77. // Perform "wildcard" pass
  78. index = SDL_Haptic_Naxes_List_Index(naxes_list, num_entries, 0xffff, 0xffff);
  79. if (index >= 0)
  80. naxes = naxes_list[index].naxes;
  81. index = SDL_Haptic_Naxes_List_Index(naxes_list, num_entries, vid, pid);
  82. if (index >= 0)
  83. naxes = naxes_list[index].naxes;
  84. SDL_free(naxes_list);
  85. return naxes;
  86. }
  87. static SDL_Haptic *SDL_haptics = NULL;
  88. #define CHECK_HAPTIC_MAGIC(haptic, result) \
  89. CHECK_PARAM(!SDL_ObjectValid(haptic, SDL_OBJECT_TYPE_HAPTIC)) { \
  90. SDL_InvalidParamError("haptic"); \
  91. return result; \
  92. }
  93. bool SDL_InitHaptics(void)
  94. {
  95. if (!SDL_SYS_HapticInit()) {
  96. return false;
  97. }
  98. #ifdef SDL_JOYSTICK_HIDAPI
  99. if (!SDL_HIDAPI_HapticInit()) {
  100. SDL_SYS_HapticQuit();
  101. return false;
  102. }
  103. #endif
  104. return true;
  105. }
  106. static bool SDL_GetHapticIndex(SDL_HapticID instance_id, int *driver_index)
  107. {
  108. int num_haptics, device_index;
  109. if (instance_id > 0) {
  110. num_haptics = SDL_SYS_NumHaptics();
  111. for (device_index = 0; device_index < num_haptics; ++device_index) {
  112. SDL_HapticID haptic_id = SDL_SYS_HapticInstanceID(device_index);
  113. if (haptic_id == instance_id) {
  114. *driver_index = device_index;
  115. return true;
  116. }
  117. }
  118. }
  119. SDL_SetError("Haptic device %" SDL_PRIu32 " not found", instance_id);
  120. return false;
  121. }
  122. SDL_HapticID *SDL_GetHaptics(int *count)
  123. {
  124. int device_index;
  125. int haptic_index = 0, num_haptics = 0;
  126. SDL_HapticID *haptics;
  127. num_haptics = SDL_SYS_NumHaptics();
  128. haptics = (SDL_HapticID *)SDL_malloc((num_haptics + 1) * sizeof(*haptics));
  129. if (haptics) {
  130. if (count) {
  131. *count = num_haptics;
  132. }
  133. for (device_index = 0; device_index < num_haptics; ++device_index) {
  134. haptics[haptic_index] = SDL_SYS_HapticInstanceID(device_index);
  135. SDL_assert(haptics[haptic_index] > 0);
  136. ++haptic_index;
  137. }
  138. haptics[haptic_index] = 0;
  139. } else {
  140. if (count) {
  141. *count = 0;
  142. }
  143. }
  144. return haptics;
  145. }
  146. const char *SDL_GetHapticNameForID(SDL_HapticID instance_id)
  147. {
  148. int device_index;
  149. const char *name = NULL;
  150. if (SDL_GetHapticIndex(instance_id, &device_index)) {
  151. name = SDL_GetPersistentString(SDL_SYS_HapticName(device_index));
  152. }
  153. return name;
  154. }
  155. SDL_Haptic *SDL_OpenHaptic(SDL_HapticID instance_id)
  156. {
  157. SDL_Haptic *haptic;
  158. SDL_Haptic *hapticlist;
  159. const char *name;
  160. int device_index = 0;
  161. if (!SDL_GetHapticIndex(instance_id, &device_index)) {
  162. return NULL;
  163. }
  164. hapticlist = SDL_haptics;
  165. /* If the haptic device is already open, return it
  166. * it is important that we have a single haptic device for each instance id
  167. */
  168. while (hapticlist) {
  169. if (instance_id == hapticlist->instance_id) {
  170. haptic = hapticlist;
  171. ++haptic->ref_count;
  172. return haptic;
  173. }
  174. hapticlist = hapticlist->next;
  175. }
  176. // Create the haptic device
  177. haptic = (SDL_Haptic *)SDL_calloc(1, sizeof(*haptic));
  178. if (!haptic) {
  179. return NULL;
  180. }
  181. // Initialize the haptic device
  182. haptic->instance_id = instance_id;
  183. haptic->rumble_id = -1;
  184. if (!SDL_SYS_HapticOpen(haptic)) {
  185. SDL_SetObjectValid(haptic, SDL_OBJECT_TYPE_HAPTIC, false);
  186. SDL_free(haptic);
  187. return NULL;
  188. }
  189. if (!haptic->name) {
  190. name = SDL_SYS_HapticName(device_index);
  191. if (name) {
  192. haptic->name = SDL_strdup(name);
  193. }
  194. }
  195. // Add haptic to list
  196. ++haptic->ref_count;
  197. // Link the haptic in the list
  198. haptic->next = SDL_haptics;
  199. SDL_haptics = haptic;
  200. SDL_SetObjectValid(haptic, SDL_OBJECT_TYPE_HAPTIC, true);
  201. // Disable autocenter and set gain to max.
  202. if (haptic->supported & SDL_HAPTIC_GAIN) {
  203. SDL_SetHapticGain(haptic, 100);
  204. }
  205. if (haptic->supported & SDL_HAPTIC_AUTOCENTER) {
  206. SDL_SetHapticAutocenter(haptic, 0);
  207. }
  208. return haptic;
  209. }
  210. SDL_Haptic *SDL_GetHapticFromID(SDL_HapticID instance_id)
  211. {
  212. SDL_Haptic *haptic;
  213. for (haptic = SDL_haptics; haptic; haptic = haptic->next) {
  214. if (instance_id == haptic->instance_id) {
  215. break;
  216. }
  217. }
  218. return haptic;
  219. }
  220. SDL_HapticID SDL_GetHapticID(SDL_Haptic *haptic)
  221. {
  222. CHECK_HAPTIC_MAGIC(haptic, 0);
  223. return haptic->instance_id;
  224. }
  225. const char *SDL_GetHapticName(SDL_Haptic *haptic)
  226. {
  227. CHECK_HAPTIC_MAGIC(haptic, NULL);
  228. return SDL_GetPersistentString(haptic->name);
  229. }
  230. bool SDL_IsMouseHaptic(void)
  231. {
  232. if (SDL_SYS_HapticMouse() < 0) {
  233. return false;
  234. }
  235. return true;
  236. }
  237. SDL_Haptic *SDL_OpenHapticFromMouse(void)
  238. {
  239. int device_index;
  240. device_index = SDL_SYS_HapticMouse();
  241. if (device_index < 0) {
  242. SDL_SetError("Haptic: Mouse isn't a haptic device.");
  243. return NULL;
  244. }
  245. return SDL_OpenHaptic(device_index);
  246. }
  247. bool SDL_IsJoystickHaptic(SDL_Joystick *joystick)
  248. {
  249. bool result = false;
  250. SDL_LockJoysticks();
  251. {
  252. // Must be a valid joystick
  253. if (SDL_IsJoystickValid(joystick) &&
  254. !SDL_IsGamepad(SDL_GetJoystickID(joystick))) {
  255. #ifdef SDL_JOYSTICK_HIDAPI
  256. result = SDL_SYS_JoystickIsHaptic(joystick) || SDL_HIDAPI_JoystickIsHaptic(joystick);
  257. #else
  258. result = SDL_SYS_JoystickIsHaptic(joystick);
  259. #endif
  260. }
  261. }
  262. SDL_UnlockJoysticks();
  263. return result;
  264. }
  265. SDL_Haptic *SDL_OpenHapticFromJoystick(SDL_Joystick *joystick)
  266. {
  267. SDL_Haptic *haptic;
  268. SDL_Haptic *hapticlist;
  269. SDL_LockJoysticks();
  270. {
  271. // Joystick must be valid and haptic
  272. if (!SDL_IsJoystickHaptic(joystick)) {
  273. SDL_SetError("Haptic: Joystick isn't a haptic device.");
  274. SDL_UnlockJoysticks();
  275. return NULL;
  276. }
  277. hapticlist = SDL_haptics;
  278. // Check to see if joystick's haptic is already open
  279. while (hapticlist) {
  280. #ifdef SDL_JOYSTICK_HIDAPI
  281. if (SDL_SYS_JoystickSameHaptic(hapticlist, joystick) || SDL_HIDAPI_JoystickSameHaptic(hapticlist, joystick)) {
  282. #else
  283. if (SDL_SYS_JoystickSameHaptic(hapticlist, joystick)) {
  284. #endif
  285. haptic = hapticlist;
  286. ++haptic->ref_count;
  287. SDL_UnlockJoysticks();
  288. return haptic;
  289. }
  290. hapticlist = hapticlist->next;
  291. }
  292. // Create the haptic device
  293. haptic = (SDL_Haptic *)SDL_calloc(1, sizeof(*haptic));
  294. if (!haptic) {
  295. SDL_UnlockJoysticks();
  296. return NULL;
  297. }
  298. /* Initialize the haptic device
  299. * This function should fill in the instance ID and name.
  300. */
  301. SDL_SetObjectValid(haptic, SDL_OBJECT_TYPE_HAPTIC, true);
  302. haptic->rumble_id = -1;
  303. #ifdef SDL_JOYSTICK_HIDAPI
  304. if (SDL_HIDAPI_JoystickIsHaptic(joystick)) {
  305. if (!SDL_HIDAPI_HapticOpenFromJoystick(haptic, joystick)) {
  306. SDL_SetError("Haptic: SDL_HIDAPI_HapticOpenFromJoystick failed.");
  307. SDL_free(haptic);
  308. SDL_UnlockJoysticks();
  309. return NULL;
  310. }
  311. } else
  312. #endif
  313. if (!SDL_SYS_HapticOpenFromJoystick(haptic, joystick)) {
  314. SDL_SetError("Haptic: SDL_SYS_HapticOpenFromJoystick failed.");
  315. SDL_SetObjectValid(haptic, SDL_OBJECT_TYPE_HAPTIC, false);
  316. SDL_free(haptic);
  317. SDL_UnlockJoysticks();
  318. return NULL;
  319. }
  320. SDL_assert(haptic->instance_id != 0);
  321. }
  322. SDL_UnlockJoysticks();
  323. // Check if custom number of haptic axes was defined
  324. Uint16 vid = SDL_GetJoystickVendor(joystick);
  325. Uint16 pid = SDL_GetJoystickProduct(joystick);
  326. int general_axes = SDL_GetNumJoystickAxes(joystick);
  327. int naxes = SDL_Haptic_Get_Naxes(vid, pid);
  328. if (naxes > 0)
  329. haptic->naxes = naxes;
  330. // Limit to the actual number of axes found on the device
  331. if (general_axes >= 0 && naxes > general_axes)
  332. haptic->naxes = general_axes;
  333. // Add haptic to list
  334. ++haptic->ref_count;
  335. // Link the haptic in the list
  336. haptic->next = SDL_haptics;
  337. SDL_haptics = haptic;
  338. SDL_SetObjectValid(haptic, SDL_OBJECT_TYPE_HAPTIC, true);
  339. // Disable autocenter and set gain to max.
  340. if (haptic->supported & SDL_HAPTIC_GAIN) {
  341. SDL_SetHapticGain(haptic, 100);
  342. }
  343. if (haptic->supported & SDL_HAPTIC_AUTOCENTER) {
  344. SDL_SetHapticAutocenter(haptic, 0);
  345. }
  346. return haptic;
  347. }
  348. void SDL_CloseHaptic(SDL_Haptic *haptic)
  349. {
  350. SDL_HapticEffectID i;
  351. SDL_Haptic *hapticlist;
  352. SDL_Haptic *hapticlistprev;
  353. CHECK_HAPTIC_MAGIC(haptic,);
  354. // Check if it's still in use
  355. if (--haptic->ref_count > 0) {
  356. return;
  357. }
  358. #ifdef SDL_JOYSTICK_HIDAPI
  359. if (SDL_HIDAPI_HapticIsHidapi(haptic)) {
  360. SDL_HIDAPI_HapticClose(haptic);
  361. } else
  362. #endif
  363. {
  364. // Close it, properly removing effects if needed
  365. for (i = 0; i < haptic->neffects; i++) {
  366. if (haptic->effects[i].hweffect != NULL) {
  367. SDL_DestroyHapticEffect(haptic, i);
  368. }
  369. }
  370. SDL_SYS_HapticClose(haptic);
  371. }
  372. SDL_SetObjectValid(haptic, SDL_OBJECT_TYPE_HAPTIC, false);
  373. // Remove from the list
  374. hapticlist = SDL_haptics;
  375. hapticlistprev = NULL;
  376. while (hapticlist) {
  377. if (haptic == hapticlist) {
  378. if (hapticlistprev) {
  379. // unlink this entry
  380. hapticlistprev->next = hapticlist->next;
  381. } else {
  382. SDL_haptics = haptic->next;
  383. }
  384. break;
  385. }
  386. hapticlistprev = hapticlist;
  387. hapticlist = hapticlist->next;
  388. }
  389. // Free the data associated with this device
  390. SDL_free(haptic->name);
  391. SDL_free(haptic);
  392. }
  393. void SDL_QuitHaptics(void)
  394. {
  395. while (SDL_haptics) {
  396. SDL_CloseHaptic(SDL_haptics);
  397. }
  398. #ifdef SDL_JOYSTICK_HIDAPI
  399. SDL_HIDAPI_HapticQuit();
  400. #endif
  401. SDL_SYS_HapticQuit();
  402. }
  403. int SDL_GetMaxHapticEffects(SDL_Haptic *haptic)
  404. {
  405. CHECK_HAPTIC_MAGIC(haptic, -1);
  406. return haptic->neffects;
  407. }
  408. int SDL_GetMaxHapticEffectsPlaying(SDL_Haptic *haptic)
  409. {
  410. CHECK_HAPTIC_MAGIC(haptic, -1);
  411. return haptic->nplaying;
  412. }
  413. Uint32 SDL_GetHapticFeatures(SDL_Haptic *haptic)
  414. {
  415. CHECK_HAPTIC_MAGIC(haptic, 0);
  416. return haptic->supported;
  417. }
  418. int SDL_GetNumHapticAxes(SDL_Haptic *haptic)
  419. {
  420. CHECK_HAPTIC_MAGIC(haptic, -1);
  421. return haptic->naxes;
  422. }
  423. bool SDL_HapticEffectSupported(SDL_Haptic *haptic, const SDL_HapticEffect *effect)
  424. {
  425. CHECK_HAPTIC_MAGIC(haptic, false);
  426. CHECK_PARAM(!effect) {
  427. return false;
  428. }
  429. if ((haptic->supported & effect->type) != 0) {
  430. return true;
  431. }
  432. return false;
  433. }
  434. SDL_HapticEffectID SDL_CreateHapticEffect(SDL_Haptic *haptic, const SDL_HapticEffect *effect)
  435. {
  436. SDL_HapticEffectID i;
  437. CHECK_HAPTIC_MAGIC(haptic, -1);
  438. CHECK_PARAM(!effect) {
  439. SDL_InvalidParamError("effect");
  440. return -1;
  441. }
  442. // Check to see if effect is supported
  443. if (SDL_HapticEffectSupported(haptic, effect) == false) {
  444. SDL_SetError("Haptic: Effect not supported by haptic device.");
  445. return -1;
  446. }
  447. #ifdef SDL_JOYSTICK_HIDAPI
  448. if (SDL_HIDAPI_HapticIsHidapi(haptic)) {
  449. return SDL_HIDAPI_HapticNewEffect(haptic, effect);
  450. }
  451. #endif
  452. // See if there's a free slot
  453. for (i = 0; i < haptic->neffects; i++) {
  454. if (haptic->effects[i].hweffect == NULL) {
  455. // Now let the backend create the real effect
  456. if (!SDL_SYS_HapticNewEffect(haptic, &haptic->effects[i], effect)) {
  457. return -1; // Backend failed to create effect
  458. }
  459. SDL_memcpy(&haptic->effects[i].effect, effect,
  460. sizeof(SDL_HapticEffect));
  461. return i;
  462. }
  463. }
  464. SDL_SetError("Haptic: Device has no free space left.");
  465. return -1;
  466. }
  467. static bool ValidEffect(SDL_Haptic *haptic, SDL_HapticEffectID effect)
  468. {
  469. if ((effect < 0) || (effect >= haptic->neffects)) {
  470. SDL_SetError("Haptic: Invalid effect identifier.");
  471. return false;
  472. }
  473. return true;
  474. }
  475. bool SDL_UpdateHapticEffect(SDL_Haptic *haptic, SDL_HapticEffectID effect, const SDL_HapticEffect *data)
  476. {
  477. CHECK_HAPTIC_MAGIC(haptic, false);
  478. CHECK_PARAM(!ValidEffect(haptic, effect)) {
  479. return false;
  480. }
  481. CHECK_PARAM(!data) {
  482. return SDL_InvalidParamError("data");
  483. }
  484. // Can't change type dynamically.
  485. CHECK_PARAM(data->type != haptic->effects[effect].effect.type) {
  486. return SDL_SetError("Haptic: Updating effect type is illegal.");
  487. }
  488. #ifdef SDL_JOYSTICK_HIDAPI
  489. if (SDL_HIDAPI_HapticIsHidapi(haptic)) {
  490. return SDL_HIDAPI_HapticUpdateEffect(haptic, effect, data);
  491. }
  492. #endif
  493. // Updates the effect
  494. if (!SDL_SYS_HapticUpdateEffect(haptic, &haptic->effects[effect], data)) {
  495. return false;
  496. }
  497. SDL_memcpy(&haptic->effects[effect].effect, data,
  498. sizeof(SDL_HapticEffect));
  499. return true;
  500. }
  501. bool SDL_RunHapticEffect(SDL_Haptic *haptic, SDL_HapticEffectID effect, Uint32 iterations)
  502. {
  503. CHECK_HAPTIC_MAGIC(haptic, false);
  504. #ifdef SDL_JOYSTICK_HIDAPI
  505. if (SDL_HIDAPI_HapticIsHidapi(haptic)) {
  506. return SDL_HIDAPI_HapticRunEffect(haptic, effect, iterations);
  507. }
  508. #endif
  509. if (!ValidEffect(haptic, effect)) {
  510. return false;
  511. }
  512. // Run the effect
  513. if (!SDL_SYS_HapticRunEffect(haptic, &haptic->effects[effect], iterations)) {
  514. return false;
  515. }
  516. return true;
  517. }
  518. bool SDL_StopHapticEffect(SDL_Haptic *haptic, SDL_HapticEffectID effect)
  519. {
  520. CHECK_HAPTIC_MAGIC(haptic, false);
  521. #ifdef SDL_JOYSTICK_HIDAPI
  522. if (SDL_HIDAPI_HapticIsHidapi(haptic)) {
  523. return SDL_HIDAPI_HapticStopEffect(haptic, effect);
  524. }
  525. #endif
  526. if (!ValidEffect(haptic, effect)) {
  527. return false;
  528. }
  529. // Stop the effect
  530. if (!SDL_SYS_HapticStopEffect(haptic, &haptic->effects[effect])) {
  531. return false;
  532. }
  533. return true;
  534. }
  535. void SDL_DestroyHapticEffect(SDL_Haptic *haptic, SDL_HapticEffectID effect)
  536. {
  537. CHECK_HAPTIC_MAGIC(haptic,);
  538. #ifdef SDL_JOYSTICK_HIDAPI
  539. if (SDL_HIDAPI_HapticIsHidapi(haptic)) {
  540. SDL_HIDAPI_HapticDestroyEffect(haptic, effect);
  541. return;
  542. }
  543. #endif
  544. if (!ValidEffect(haptic, effect)) {
  545. return;
  546. }
  547. // Not allocated
  548. if (haptic->effects[effect].hweffect == NULL) {
  549. return;
  550. }
  551. SDL_SYS_HapticDestroyEffect(haptic, &haptic->effects[effect]);
  552. }
  553. bool SDL_GetHapticEffectStatus(SDL_Haptic *haptic, SDL_HapticEffectID effect)
  554. {
  555. CHECK_HAPTIC_MAGIC(haptic, false);
  556. #ifdef SDL_JOYSTICK_HIDAPI
  557. if (SDL_HIDAPI_HapticIsHidapi(haptic)) {
  558. return SDL_HIDAPI_HapticGetEffectStatus(haptic, effect);
  559. }
  560. #endif
  561. if (!ValidEffect(haptic, effect)) {
  562. return false;
  563. }
  564. if (!(haptic->supported & SDL_HAPTIC_STATUS)) {
  565. return SDL_SetError("Haptic: Device does not support status queries.");
  566. }
  567. SDL_ClearError();
  568. return (SDL_SYS_HapticGetEffectStatus(haptic, &haptic->effects[effect]) > 0);
  569. }
  570. bool SDL_SetHapticGain(SDL_Haptic *haptic, int gain)
  571. {
  572. const char *env;
  573. int real_gain, max_gain;
  574. CHECK_HAPTIC_MAGIC(haptic, false);
  575. if (!(haptic->supported & SDL_HAPTIC_GAIN)) {
  576. return SDL_SetError("Haptic: Device does not support setting gain.");
  577. }
  578. if ((gain < 0) || (gain > 100)) {
  579. return SDL_SetError("Haptic: Gain must be between 0 and 100.");
  580. }
  581. // The user can use an environment variable to override the max gain.
  582. env = SDL_getenv("SDL_HAPTIC_GAIN_MAX");
  583. if (env) {
  584. max_gain = SDL_atoi(env);
  585. // Check for sanity.
  586. if (max_gain < 0) {
  587. max_gain = 0;
  588. } else if (max_gain > 100) {
  589. max_gain = 100;
  590. }
  591. // We'll scale it linearly with SDL_HAPTIC_GAIN_MAX
  592. real_gain = (gain * max_gain) / 100;
  593. } else {
  594. real_gain = gain;
  595. }
  596. #ifdef SDL_JOYSTICK_HIDAPI
  597. if (SDL_HIDAPI_HapticIsHidapi(haptic)) {
  598. return SDL_HIDAPI_HapticSetGain(haptic, real_gain);
  599. }
  600. #endif
  601. return SDL_SYS_HapticSetGain(haptic, real_gain);
  602. }
  603. bool SDL_SetHapticAutocenter(SDL_Haptic *haptic, int autocenter)
  604. {
  605. CHECK_HAPTIC_MAGIC(haptic, false);
  606. if (!(haptic->supported & SDL_HAPTIC_AUTOCENTER)) {
  607. return SDL_SetError("Haptic: Device does not support setting autocenter.");
  608. }
  609. if ((autocenter < 0) || (autocenter > 100)) {
  610. return SDL_SetError("Haptic: Autocenter must be between 0 and 100.");
  611. }
  612. #ifdef SDL_JOYSTICK_HIDAPI
  613. if (SDL_HIDAPI_HapticIsHidapi(haptic)) {
  614. return SDL_HIDAPI_HapticSetAutocenter(haptic, autocenter);
  615. }
  616. #endif
  617. return SDL_SYS_HapticSetAutocenter(haptic, autocenter);
  618. }
  619. bool SDL_PauseHaptic(SDL_Haptic *haptic)
  620. {
  621. CHECK_HAPTIC_MAGIC(haptic, false);
  622. if (!(haptic->supported & SDL_HAPTIC_PAUSE)) {
  623. return SDL_SetError("Haptic: Device does not support setting pausing.");
  624. }
  625. #ifdef SDL_JOYSTICK_HIDAPI
  626. if (SDL_HIDAPI_HapticIsHidapi(haptic)) {
  627. return SDL_HIDAPI_HapticPause(haptic);
  628. }
  629. #endif
  630. return SDL_SYS_HapticPause(haptic);
  631. }
  632. bool SDL_ResumeHaptic(SDL_Haptic *haptic)
  633. {
  634. CHECK_HAPTIC_MAGIC(haptic, false);
  635. if (!(haptic->supported & SDL_HAPTIC_PAUSE)) {
  636. return true; // Not going to be paused, so we pretend it's unpaused.
  637. }
  638. #ifdef SDL_JOYSTICK_HIDAPI
  639. if (SDL_HIDAPI_HapticIsHidapi(haptic)) {
  640. return SDL_HIDAPI_HapticResume(haptic);
  641. }
  642. #endif
  643. return SDL_SYS_HapticResume(haptic);
  644. }
  645. bool SDL_StopHapticEffects(SDL_Haptic *haptic)
  646. {
  647. CHECK_HAPTIC_MAGIC(haptic, false);
  648. #ifdef SDL_JOYSTICK_HIDAPI
  649. if (SDL_HIDAPI_HapticIsHidapi(haptic)) {
  650. return SDL_HIDAPI_HapticStopAll(haptic);
  651. }
  652. #endif
  653. return SDL_SYS_HapticStopAll(haptic);
  654. }
  655. bool SDL_HapticRumbleSupported(SDL_Haptic *haptic)
  656. {
  657. CHECK_HAPTIC_MAGIC(haptic, false);
  658. // Most things can use SINE, but XInput only has LEFTRIGHT.
  659. return (haptic->supported & (SDL_HAPTIC_SINE | SDL_HAPTIC_LEFTRIGHT)) != 0;
  660. }
  661. bool SDL_InitHapticRumble(SDL_Haptic *haptic)
  662. {
  663. SDL_HapticEffect *efx = &haptic->rumble_effect;
  664. CHECK_HAPTIC_MAGIC(haptic, false);
  665. // Already allocated.
  666. if (haptic->rumble_id >= 0) {
  667. return true;
  668. }
  669. SDL_zerop(efx);
  670. if (haptic->supported & SDL_HAPTIC_SINE) {
  671. efx->type = SDL_HAPTIC_SINE;
  672. efx->periodic.direction.type = SDL_HAPTIC_CARTESIAN;
  673. efx->periodic.period = 1000;
  674. efx->periodic.magnitude = 0x4000;
  675. efx->periodic.length = 5000;
  676. efx->periodic.attack_length = 0;
  677. efx->periodic.fade_length = 0;
  678. } else if (haptic->supported & SDL_HAPTIC_LEFTRIGHT) { // XInput?
  679. efx->type = SDL_HAPTIC_LEFTRIGHT;
  680. efx->leftright.length = 5000;
  681. efx->leftright.large_magnitude = 0x4000;
  682. efx->leftright.small_magnitude = 0x4000;
  683. } else {
  684. return SDL_SetError("Device doesn't support rumble");
  685. }
  686. haptic->rumble_id = SDL_CreateHapticEffect(haptic, &haptic->rumble_effect);
  687. if (haptic->rumble_id >= 0) {
  688. return true;
  689. }
  690. return false;
  691. }
  692. bool SDL_PlayHapticRumble(SDL_Haptic *haptic, float strength, Uint32 length)
  693. {
  694. SDL_HapticEffect *efx;
  695. Sint16 magnitude;
  696. CHECK_HAPTIC_MAGIC(haptic, false);
  697. if (haptic->rumble_id < 0) {
  698. return SDL_SetError("Haptic: Rumble effect not initialized on haptic device");
  699. }
  700. // Clamp strength.
  701. if (strength > 1.0f) {
  702. strength = 1.0f;
  703. } else if (strength < 0.0f) {
  704. strength = 0.0f;
  705. }
  706. magnitude = (Sint16)(32767.0f * strength);
  707. efx = &haptic->rumble_effect;
  708. if (efx->type == SDL_HAPTIC_SINE) {
  709. efx->periodic.magnitude = magnitude;
  710. efx->periodic.length = length;
  711. } else if (efx->type == SDL_HAPTIC_LEFTRIGHT) {
  712. efx->leftright.small_magnitude = efx->leftright.large_magnitude = magnitude;
  713. efx->leftright.length = length;
  714. } else {
  715. SDL_assert(!"This should have been caught elsewhere");
  716. }
  717. if (!SDL_UpdateHapticEffect(haptic, haptic->rumble_id, &haptic->rumble_effect)) {
  718. return false;
  719. }
  720. return SDL_RunHapticEffect(haptic, haptic->rumble_id, 1);
  721. }
  722. bool SDL_StopHapticRumble(SDL_Haptic *haptic)
  723. {
  724. CHECK_HAPTIC_MAGIC(haptic, false);
  725. if (haptic->rumble_id < 0) {
  726. return SDL_SetError("Haptic: Rumble effect not initialized on haptic device");
  727. }
  728. return SDL_StopHapticEffect(haptic, haptic->rumble_id);
  729. }