SDL_hidapihaptic_lg4ff.c 39 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265
  1. /*
  2. Simple DirectMedia Layer
  3. Copyright (C) 2025 Simon Wood <[email protected]>
  4. Copyright (C) 2025 Michal Malý <[email protected]>
  5. Copyright (C) 2025 Bernat Arlandis <[email protected]>
  6. Copyright (C) 2025 Katharine Chui <[email protected]>
  7. This software is provided 'as-is', without any express or implied
  8. warranty. In no event will the authors be held liable for any damages
  9. arising from the use of this software.
  10. Permission is granted to anyone to use this software for any purpose,
  11. including commercial applications, and to alter it and redistribute it
  12. freely, subject to the following restrictions:
  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. #ifdef SDL_JOYSTICK_HIDAPI
  23. #include "SDL_hidapihaptic_c.h"
  24. #ifdef SDL_HAPTIC_HIDAPI_LG4FF
  25. #include "SDL3/SDL_thread.h"
  26. #include "SDL3/SDL_mutex.h"
  27. #include "SDL3/SDL_timer.h"
  28. #include <math.h>
  29. #define USB_VENDOR_ID_LOGITECH 0x046d
  30. #define USB_DEVICE_ID_LOGITECH_G29_WHEEL 0xc24f
  31. #define USB_DEVICE_ID_LOGITECH_G27_WHEEL 0xc29b
  32. #define USB_DEVICE_ID_LOGITECH_G25_WHEEL 0xc299
  33. #define USB_DEVICE_ID_LOGITECH_DFGT_WHEEL 0xc29a
  34. #define USB_DEVICE_ID_LOGITECH_DFP_WHEEL 0xc298
  35. #define USB_DEVICE_ID_LOGITECH_WHEEL 0xc294
  36. static Uint32 supported_device_ids[] = {
  37. USB_DEVICE_ID_LOGITECH_G29_WHEEL,
  38. USB_DEVICE_ID_LOGITECH_G27_WHEEL,
  39. USB_DEVICE_ID_LOGITECH_G25_WHEEL,
  40. USB_DEVICE_ID_LOGITECH_DFGT_WHEEL,
  41. USB_DEVICE_ID_LOGITECH_DFP_WHEEL,
  42. USB_DEVICE_ID_LOGITECH_WHEEL
  43. };
  44. #define LG4FF_MAX_EFFECTS 16
  45. #define FF_EFFECT_STARTED 0
  46. #define FF_EFFECT_ALLSET 1
  47. #define FF_EFFECT_PLAYING 2
  48. #define FF_EFFECT_UPDATING 3
  49. struct lg4ff_effect_state {
  50. SDL_HapticEffect effect;
  51. Uint64 start_at;
  52. Uint64 play_at;
  53. Uint64 stop_at;
  54. Uint32 flags;
  55. Uint64 time_playing;
  56. Uint64 updated_at;
  57. Uint32 phase;
  58. Uint32 phase_adj;
  59. Uint32 count;
  60. double direction_gain;
  61. Sint32 slope;
  62. bool allocated;
  63. };
  64. struct lg4ff_effect_parameters {
  65. Sint32 level;
  66. Sint32 d1;
  67. Sint32 d2;
  68. Sint32 k1;
  69. Sint32 k2;
  70. Uint32 clip;
  71. };
  72. struct lg4ff_slot {
  73. Sint32 id;
  74. struct lg4ff_effect_parameters parameters;
  75. Uint8 current_cmd[7];
  76. Uint32 cmd_op;
  77. bool is_updated;
  78. Uint32 effect_type;
  79. };
  80. typedef struct lg4ff_device {
  81. Uint16 product_id;
  82. Uint16 release_number;
  83. struct lg4ff_effect_state states[LG4FF_MAX_EFFECTS];
  84. struct lg4ff_slot slots[4];
  85. Sint32 effects_used;
  86. Sint32 gain;
  87. Sint32 app_gain;
  88. Sint32 spring_level;
  89. Sint32 damper_level;
  90. Sint32 friction_level;
  91. Sint32 peak_ffb_level;
  92. SDL_Joystick *hid_handle;
  93. bool stop_thread;
  94. SDL_Thread *thread;
  95. char thread_name_buf[256];
  96. SDL_Mutex *mutex;
  97. bool is_ffex;
  98. } lg4ff_device;
  99. static SDL_INLINE Uint64 get_time_ms(void) {
  100. return SDL_GetTicks();
  101. }
  102. #define test_bit(bit, field) (*(field) & (1 << bit))
  103. #define __set_bit(bit, field) {*(field) = *(field) | (1 << bit);}
  104. #define __clear_bit(bit, field) {*(field) = *(field) & ~(1 << bit);}
  105. #define sin_deg(in) (double)(SDL_sin((double)(in) * SDL_PI_D / 180.0))
  106. #define time_after_eq(a, b) (a >= b)
  107. #define time_before(a, b) (a < b)
  108. #define time_diff(a, b) (a - b)
  109. #define STOP_EFFECT(state) ((state)->flags = 0)
  110. #define CLAMP_VALUE_U16(x) ((Uint16)((x) > 0xffff ? 0xffff : (x)))
  111. #define SCALE_VALUE_U16(x, bits) (CLAMP_VALUE_U16(x) >> (16 - bits))
  112. #define CLAMP_VALUE_S16(x) ((Uint16)((x) <= -0x8000 ? -0x8000 : ((x) > 0x7fff ? 0x7fff : (x))))
  113. #define TRANSLATE_FORCE(x) ((CLAMP_VALUE_S16(x) + 0x8000) >> 8)
  114. #define SCALE_COEFF(x, bits) SCALE_VALUE_U16(abs32(x) * 2, bits)
  115. static SDL_INLINE Sint32 abs32(Sint32 x) {
  116. return x < 0 ? -x : x;
  117. }
  118. static SDL_INLINE Sint64 abs64(Sint64 x) {
  119. return x < 0 ? -x : x;
  120. }
  121. static SDL_INLINE bool effect_is_periodic(const SDL_HapticEffect *effect)
  122. {
  123. return effect->type == SDL_HAPTIC_SINE ||
  124. effect->type == SDL_HAPTIC_TRIANGLE ||
  125. effect->type == SDL_HAPTIC_SAWTOOTHUP ||
  126. effect->type == SDL_HAPTIC_SAWTOOTHDOWN ||
  127. effect->type == SDL_HAPTIC_SQUARE;
  128. }
  129. static SDL_INLINE bool effect_is_condition(const SDL_HapticEffect *effect)
  130. {
  131. return effect->type == SDL_HAPTIC_SPRING ||
  132. effect->type == SDL_HAPTIC_DAMPER ||
  133. effect->type == SDL_HAPTIC_FRICTION;
  134. }
  135. // linux SDL_syshaptic.c SDL_SYS_ToDirection
  136. static Uint16 to_linux_direction(SDL_HapticDirection *src)
  137. {
  138. Uint32 tmp;
  139. switch (src->type) {
  140. case SDL_HAPTIC_POLAR:
  141. tmp = ((src->dir[0] % 36000) * 0x8000) / 18000; /* convert to range [0,0xFFFF] */
  142. return (Uint16)tmp;
  143. case SDL_HAPTIC_SPHERICAL:
  144. /*
  145. We convert to polar, because that's the only supported direction on Linux.
  146. The first value of a spherical direction is practically the same as a
  147. Polar direction, except that we have to add 90 degrees. It is the angle
  148. from EAST {1,0} towards SOUTH {0,1}.
  149. --> add 9000
  150. --> finally convert to [0,0xFFFF] as in case SDL_HAPTIC_POLAR.
  151. */
  152. tmp = ((src->dir[0]) + 9000) % 36000; /* Convert to polars */
  153. tmp = (tmp * 0x8000) / 18000; /* convert to range [0,0xFFFF] */
  154. return (Uint16)tmp;
  155. case SDL_HAPTIC_CARTESIAN:
  156. if (!src->dir[1]) {
  157. return (Uint16) (src->dir[0] >= 0 ? 0x4000 : 0xC000);
  158. } else if (!src->dir[0]) {
  159. return (Uint16) (src->dir[1] >= 0 ? 0x8000 : 0);
  160. } else {
  161. float f = (float)SDL_atan2(src->dir[1], src->dir[0]); /* Ideally we'd use fixed point math instead of floats... */
  162. /*
  163. SDL_atan2 takes the parameters: Y-axis-value and X-axis-value (in that order)
  164. - Y-axis-value is the second coordinate (from center to SOUTH)
  165. - X-axis-value is the first coordinate (from center to EAST)
  166. We add 36000, because SDL_atan2 also returns negative values. Then we practically
  167. have the first spherical value. Therefore we proceed as in case
  168. SDL_HAPTIC_SPHERICAL and add another 9000 to get the polar value.
  169. --> add 45000 in total
  170. --> finally convert to [0,0xFFFF] as in case SDL_HAPTIC_POLAR.
  171. */
  172. tmp = (((Sint32) (f * 18000. / SDL_PI_D)) + 45000) % 36000;
  173. tmp = (tmp * 0x8000) / 18000; /* convert to range [0,0xFFFF] */
  174. return (Uint16)tmp;
  175. }
  176. break;
  177. case SDL_HAPTIC_STEERING_AXIS:
  178. return 0x4000;
  179. default:
  180. SDL_assert(0);
  181. }
  182. return 0;
  183. }
  184. static Uint16 get_effect_direction(SDL_HapticEffect *effect)
  185. {
  186. Uint16 direction = 0;
  187. if (effect_is_periodic(effect)) {
  188. direction = to_linux_direction(&effect->periodic.direction);
  189. } else if (effect_is_condition(effect)) {
  190. direction = to_linux_direction(&effect->condition.direction);
  191. } else {
  192. switch(effect->type) {
  193. case SDL_HAPTIC_CONSTANT:
  194. direction = to_linux_direction(&effect->constant.direction);
  195. break;
  196. case SDL_HAPTIC_RAMP:
  197. direction = to_linux_direction(&effect->ramp.direction);
  198. break;
  199. default:
  200. SDL_assert(0);
  201. }
  202. }
  203. return direction;
  204. }
  205. static Uint32 get_effect_replay_length(SDL_HapticEffect *effect)
  206. {
  207. Uint32 length = 0;
  208. if (effect_is_periodic(effect)) {
  209. length = effect->periodic.length;
  210. } else if (effect_is_condition(effect)) {
  211. length = effect->condition.length;
  212. } else {
  213. switch(effect->type) {
  214. case SDL_HAPTIC_CONSTANT:
  215. length = effect->constant.length;
  216. break;
  217. case SDL_HAPTIC_RAMP:
  218. length = effect->ramp.length;
  219. break;
  220. default:
  221. SDL_assert(0);
  222. }
  223. }
  224. if (length == SDL_HAPTIC_INFINITY) {
  225. length = 0;
  226. }
  227. return length;
  228. }
  229. static Uint16 get_effect_replay_delay(SDL_HapticEffect *effect)
  230. {
  231. Uint16 delay = 0;
  232. if (effect_is_periodic(effect)) {
  233. delay = effect->periodic.delay;
  234. } else if (effect_is_condition(effect)) {
  235. delay = effect->condition.delay;
  236. } else {
  237. switch(effect->type) {
  238. case SDL_HAPTIC_CONSTANT:
  239. delay = effect->constant.delay;
  240. break;
  241. case SDL_HAPTIC_RAMP:
  242. delay = effect->ramp.delay;
  243. break;
  244. default:
  245. SDL_assert(0);
  246. }
  247. }
  248. return delay;
  249. }
  250. /*
  251. *Ported*
  252. Original function by:
  253. Bernat Arlandis <[email protected]>
  254. `git blame 1a2d5727876dd7befce23d9695924e9446b31c4b hid-lg4ff.c`, https://github.com/berarma/new-lg4ff.git
  255. */
  256. static int lg4ff_play_effect(struct lg4ff_device *device, int effect_id, int value)
  257. {
  258. struct lg4ff_effect_state *state;
  259. Uint64 now = get_time_ms();
  260. state = &device->states[effect_id];
  261. if (value > 0) {
  262. if (test_bit(FF_EFFECT_STARTED, &state->flags)) {
  263. STOP_EFFECT(state);
  264. } else {
  265. device->effects_used++;
  266. }
  267. __set_bit(FF_EFFECT_STARTED, &state->flags);
  268. state->start_at = now;
  269. state->count = value;
  270. } else {
  271. if (test_bit(FF_EFFECT_STARTED, &state->flags)) {
  272. STOP_EFFECT(state);
  273. device->effects_used--;
  274. }
  275. }
  276. return 0;
  277. }
  278. /*
  279. *Ported*
  280. Original function by:
  281. Bernat Arlandis <[email protected]>
  282. `git blame 1a2d5727876dd7befce23d9695924e9446b31c4b hid-lg4ff.c`, https://github.com/berarma/new-lg4ff.git
  283. */
  284. static int lg4ff_upload_effect(struct lg4ff_device *device, const SDL_HapticEffect *effect, int id)
  285. {
  286. struct lg4ff_effect_state *state;
  287. Uint64 now = get_time_ms();
  288. if (effect_is_periodic(effect) && effect->periodic.period == 0) {
  289. return -1;
  290. }
  291. state = &device->states[id];
  292. if (test_bit(FF_EFFECT_STARTED, &state->flags) && effect->type != state->effect.type) {
  293. return -1;
  294. }
  295. state->effect = *effect;
  296. if (test_bit(FF_EFFECT_STARTED, &state->flags)) {
  297. __set_bit(FF_EFFECT_UPDATING, &state->flags);
  298. state->updated_at = now;
  299. }
  300. return 0;
  301. }
  302. /*
  303. *Ported*
  304. Original function by:
  305. Bernat Arlandis <[email protected]>
  306. `git blame 1a2d5727876dd7befce23d9695924e9446b31c4b hid-lg4ff.c`, https://github.com/berarma/new-lg4ff.git
  307. */
  308. static void lg4ff_update_state(struct lg4ff_effect_state *state, const Uint64 now)
  309. {
  310. SDL_HapticEffect *effect = &state->effect;
  311. Uint64 phase_time;
  312. Uint16 effect_direction = get_effect_direction(effect);
  313. if (!test_bit(FF_EFFECT_ALLSET, &state->flags)) {
  314. state->play_at = state->start_at + get_effect_replay_delay(effect);
  315. if (!test_bit(FF_EFFECT_UPDATING, &state->flags)) {
  316. state->updated_at = state->play_at;
  317. }
  318. state->direction_gain = sin_deg(effect_direction * 360 / 0x10000);
  319. if (effect_is_periodic(effect)) {
  320. state->phase_adj = effect->periodic.phase * 360 / effect->periodic.period;
  321. }
  322. if (get_effect_replay_length(effect)) {
  323. state->stop_at = state->play_at + get_effect_replay_length(effect);
  324. }
  325. }
  326. __set_bit(FF_EFFECT_ALLSET, &state->flags);
  327. if (test_bit(FF_EFFECT_UPDATING, &state->flags)) {
  328. __clear_bit(FF_EFFECT_PLAYING, &state->flags);
  329. state->play_at = state->updated_at + get_effect_replay_delay(effect);
  330. state->direction_gain = sin_deg(effect_direction * 360 / 0x10000);
  331. if (get_effect_replay_length(effect)) {
  332. state->stop_at = state->updated_at + get_effect_replay_length(effect);
  333. }
  334. if (effect_is_periodic(effect)) {
  335. state->phase_adj = state->phase;
  336. }
  337. }
  338. __clear_bit(FF_EFFECT_UPDATING, &state->flags);
  339. state->slope = 0;
  340. if (effect->type == SDL_HAPTIC_RAMP && effect->ramp.length && (effect->ramp.length - effect->ramp.attack_length - effect->ramp.fade_length) != 0) {
  341. state->slope = ((effect->ramp.end - effect->ramp.start) << 16) / (effect->ramp.length - effect->ramp.attack_length - effect->ramp.fade_length);
  342. }
  343. if (!test_bit(FF_EFFECT_PLAYING, &state->flags) && time_after_eq(now,
  344. state->play_at) && (get_effect_replay_length(effect) == 0 ||
  345. time_before(now, state->stop_at))) {
  346. __set_bit(FF_EFFECT_PLAYING, &state->flags);
  347. }
  348. if (test_bit(FF_EFFECT_PLAYING, &state->flags)) {
  349. state->time_playing = time_diff(now, state->play_at);
  350. if (effect_is_periodic(effect)) {
  351. phase_time = time_diff(now, state->updated_at);
  352. state->phase = (phase_time % effect->periodic.period) * 360 / effect->periodic.period;
  353. state->phase += state->phase_adj % 360;
  354. }
  355. }
  356. }
  357. /*
  358. *Ported*
  359. Original function by:
  360. Bernat Arlandis <[email protected]>
  361. `git blame 1a2d5727876dd7befce23d9695924e9446b31c4b hid-lg4ff.c`, https://github.com/berarma/new-lg4ff.git
  362. */
  363. static Sint32 lg4ff_calculate_constant(struct lg4ff_effect_state *state)
  364. {
  365. SDL_HapticConstant *constant = (SDL_HapticConstant *)&state->effect;
  366. Sint32 level_sign;
  367. Sint32 level = constant->level;
  368. Sint32 d, t;
  369. if (state->time_playing < constant->attack_length) {
  370. level_sign = level < 0 ? -1 : 1;
  371. d = level - level_sign * constant->attack_level;
  372. level = (Sint32) (level_sign * constant->attack_level + d * state->time_playing / constant->attack_length);
  373. } else if (constant->length && constant->fade_length) {
  374. t = (Sint32) (state->time_playing - constant->length + constant->fade_length);
  375. if (t > 0) {
  376. level_sign = level < 0 ? -1 : 1;
  377. d = level - level_sign * constant->fade_level;
  378. level = level - d * t / constant->fade_length;
  379. }
  380. }
  381. return (Sint32)(state->direction_gain * level);
  382. }
  383. /*
  384. *Ported*
  385. Original function by:
  386. Bernat Arlandis <[email protected]>
  387. `git blame 1a2d5727876dd7befce23d9695924e9446b31c4b hid-lg4ff.c`, https://github.com/berarma/new-lg4ff.git
  388. */
  389. static Sint32 lg4ff_calculate_ramp(struct lg4ff_effect_state *state)
  390. {
  391. SDL_HapticRamp *ramp = (SDL_HapticRamp *)&state->effect;
  392. Sint32 level_sign;
  393. Sint32 level;
  394. Sint32 d, t;
  395. if (state->time_playing < ramp->attack_length) {
  396. level = ramp->start;
  397. level_sign = level < 0 ? -1 : 1;
  398. t = (Sint32) (ramp->attack_length - state->time_playing);
  399. d = level - level_sign * ramp->attack_level;
  400. level = level_sign * ramp->attack_level + d * t / ramp->attack_length;
  401. } else if (ramp->length && state->time_playing >= ramp->length - ramp->fade_length && ramp->fade_length) {
  402. level = ramp->end;
  403. level_sign = level < 0 ? -1 : 1;
  404. t = (Sint32) (state->time_playing - ramp->length + ramp->fade_length);
  405. d = level_sign * ramp->fade_level - level;
  406. level = level - d * t / ramp->fade_length;
  407. } else {
  408. t = (Sint32) (state->time_playing - ramp->attack_length);
  409. level = ramp->start + ((t * state->slope) >> 16);
  410. }
  411. return (Sint32)(state->direction_gain * level);
  412. }
  413. /*
  414. *Ported*
  415. Original function by:
  416. Bernat Arlandis <[email protected]>
  417. `git blame 1a2d5727876dd7befce23d9695924e9446b31c4b hid-lg4ff.c`, https://github.com/berarma/new-lg4ff.git
  418. */
  419. static Sint32 lg4ff_calculate_periodic(struct lg4ff_effect_state *state)
  420. {
  421. SDL_HapticPeriodic *periodic = (SDL_HapticPeriodic *)&state->effect;
  422. Sint32 magnitude = periodic->magnitude;
  423. Sint32 magnitude_sign = magnitude < 0 ? -1 : 1;
  424. Sint32 level = periodic->offset;
  425. Sint32 d, t;
  426. if (state->time_playing < periodic->attack_length) {
  427. d = magnitude - magnitude_sign * periodic->attack_level;
  428. magnitude = (Sint32) (magnitude_sign * periodic->attack_level + d * state->time_playing / periodic->attack_length);
  429. } else if (periodic->length && periodic->fade_length) {
  430. t = (Sint32) (state->time_playing - get_effect_replay_length(&state->effect) + periodic->fade_length);
  431. if (t > 0) {
  432. d = magnitude - magnitude_sign * periodic->fade_level;
  433. magnitude = magnitude - d * t / periodic->fade_length;
  434. }
  435. }
  436. switch (periodic->type) {
  437. case SDL_HAPTIC_SINE:
  438. level += (Sint32)(sin_deg(state->phase) * magnitude);
  439. break;
  440. case SDL_HAPTIC_SQUARE:
  441. level += (state->phase < 180 ? 1 : -1) * magnitude;
  442. break;
  443. case SDL_HAPTIC_TRIANGLE:
  444. level += (Sint32) (abs64((Sint64)state->phase * magnitude * 2 / 360 - magnitude) * 2 - magnitude);
  445. break;
  446. case SDL_HAPTIC_SAWTOOTHUP:
  447. level += state->phase * magnitude * 2 / 360 - magnitude;
  448. break;
  449. case SDL_HAPTIC_SAWTOOTHDOWN:
  450. level += magnitude - state->phase * magnitude * 2 / 360;
  451. break;
  452. default:
  453. SDL_assert(0);
  454. }
  455. return (Sint32)(state->direction_gain * level);
  456. }
  457. /*
  458. *Ported*
  459. Original function by:
  460. Bernat Arlandis <[email protected]>
  461. `git blame 1a2d5727876dd7befce23d9695924e9446b31c4b hid-lg4ff.c`, https://github.com/berarma/new-lg4ff.git
  462. */
  463. static void lg4ff_calculate_spring(struct lg4ff_effect_state *state, struct lg4ff_effect_parameters *parameters)
  464. {
  465. SDL_HapticCondition *condition = (SDL_HapticCondition *)&state->effect;
  466. parameters->d1 = ((Sint32)condition->center[0]) - condition->deadband[0] / 2;
  467. parameters->d2 = ((Sint32)condition->center[0]) + condition->deadband[0] / 2;
  468. parameters->k1 = condition->left_coeff[0];
  469. parameters->k2 = condition->right_coeff[0];
  470. parameters->clip = (Uint16)condition->right_sat[0];
  471. }
  472. /*
  473. *Ported*
  474. Original function by:
  475. Bernat Arlandis <[email protected]>
  476. `git blame 1a2d5727876dd7befce23d9695924e9446b31c4b hid-lg4ff.c`, https://github.com/berarma/new-lg4ff.git
  477. */
  478. static void lg4ff_calculate_resistance(struct lg4ff_effect_state *state, struct lg4ff_effect_parameters *parameters)
  479. {
  480. SDL_HapticCondition *condition = (SDL_HapticCondition *)&state->effect;
  481. parameters->k1 = condition->left_coeff[0];
  482. parameters->k2 = condition->right_coeff[0];
  483. parameters->clip = (Uint16)condition->right_sat[0];
  484. }
  485. /*
  486. *Ported*
  487. Original function by:
  488. Bernat Arlandis <[email protected]>
  489. `git blame 1a2d5727876dd7befce23d9695924e9446b31c4b hid-lg4ff.c`, https://github.com/berarma/new-lg4ff.git
  490. */
  491. static void lg4ff_update_slot(struct lg4ff_slot *slot, struct lg4ff_effect_parameters *parameters)
  492. {
  493. Uint8 original_cmd[7];
  494. Sint32 d1;
  495. Sint32 d2;
  496. Sint32 k1;
  497. Sint32 k2;
  498. Sint32 s1;
  499. Sint32 s2;
  500. SDL_memcpy(original_cmd, slot->current_cmd, sizeof(original_cmd));
  501. if ((original_cmd[0] & 0xf) == 1) {
  502. original_cmd[0] = (original_cmd[0] & 0xf0) + 0xc;
  503. }
  504. if (slot->effect_type == SDL_HAPTIC_CONSTANT) {
  505. if (slot->cmd_op == 0) {
  506. slot->cmd_op = 1;
  507. } else {
  508. slot->cmd_op = 0xc;
  509. }
  510. } else {
  511. if (parameters->clip == 0) {
  512. slot->cmd_op = 3;
  513. } else if (slot->cmd_op == 3) {
  514. slot->cmd_op = 1;
  515. } else {
  516. slot->cmd_op = 0xc;
  517. }
  518. }
  519. slot->current_cmd[0] = (Uint8)((0x10 << slot->id) + slot->cmd_op);
  520. if (slot->cmd_op == 3) {
  521. slot->current_cmd[1] = 0;
  522. slot->current_cmd[2] = 0;
  523. slot->current_cmd[3] = 0;
  524. slot->current_cmd[4] = 0;
  525. slot->current_cmd[5] = 0;
  526. slot->current_cmd[6] = 0;
  527. } else {
  528. switch (slot->effect_type) {
  529. case SDL_HAPTIC_CONSTANT:
  530. slot->current_cmd[1] = 0x00;
  531. slot->current_cmd[2] = 0;
  532. slot->current_cmd[3] = 0;
  533. slot->current_cmd[4] = 0;
  534. slot->current_cmd[5] = 0;
  535. slot->current_cmd[6] = 0;
  536. slot->current_cmd[2 + slot->id] = TRANSLATE_FORCE(parameters->level);
  537. break;
  538. case SDL_HAPTIC_SPRING:
  539. d1 = SCALE_VALUE_U16(((parameters->d1) + 0x8000) & 0xffff, 11);
  540. d2 = SCALE_VALUE_U16(((parameters->d2) + 0x8000) & 0xffff, 11);
  541. s1 = parameters->k1 < 0;
  542. s2 = parameters->k2 < 0;
  543. k1 = abs32(parameters->k1);
  544. k2 = abs32(parameters->k2);
  545. if (k1 < 2048) {
  546. d1 = 0;
  547. } else {
  548. k1 -= 2048;
  549. }
  550. if (k2 < 2048) {
  551. d2 = 2047;
  552. } else {
  553. k2 -= 2048;
  554. }
  555. slot->current_cmd[1] = 0x0b;
  556. slot->current_cmd[2] = (Uint8)(d1 >> 3);
  557. slot->current_cmd[3] = (Uint8)(d2 >> 3);
  558. slot->current_cmd[4] = (SCALE_COEFF(k2, 4) << 4) + SCALE_COEFF(k1, 4);
  559. slot->current_cmd[5] = (Uint8)(((d2 & 7) << 5) + ((d1 & 7) << 1) + (s2 << 4) + s1);
  560. slot->current_cmd[6] = SCALE_VALUE_U16(parameters->clip, 8);
  561. break;
  562. case SDL_HAPTIC_DAMPER:
  563. s1 = parameters->k1 < 0;
  564. s2 = parameters->k2 < 0;
  565. slot->current_cmd[1] = 0x0c;
  566. slot->current_cmd[2] = SCALE_COEFF(parameters->k1, 4);
  567. slot->current_cmd[3] = (Uint8)s1;
  568. slot->current_cmd[4] = SCALE_COEFF(parameters->k2, 4);
  569. slot->current_cmd[5] = (Uint8)s2;
  570. slot->current_cmd[6] = SCALE_VALUE_U16(parameters->clip, 8);
  571. break;
  572. case SDL_HAPTIC_FRICTION:
  573. s1 = parameters->k1 < 0;
  574. s2 = parameters->k2 < 0;
  575. slot->current_cmd[1] = 0x0e;
  576. slot->current_cmd[2] = SCALE_COEFF(parameters->k1, 8);
  577. slot->current_cmd[3] = SCALE_COEFF(parameters->k2, 8);
  578. slot->current_cmd[4] = SCALE_VALUE_U16(parameters->clip, 8);
  579. slot->current_cmd[5] = (Uint8)((s2 << 4) + s1);
  580. slot->current_cmd[6] = 0;
  581. break;
  582. }
  583. }
  584. if (SDL_memcmp(original_cmd, slot->current_cmd, sizeof(original_cmd))) {
  585. slot->is_updated = 1;
  586. }
  587. }
  588. /*
  589. *Ported*
  590. Original function by:
  591. Bernat Arlandis <[email protected]>
  592. `git blame 1a2d5727876dd7befce23d9695924e9446b31c4b hid-lg4ff.c`, https://github.com/berarma/new-lg4ff.git
  593. */
  594. static int lg4ff_init_slots(struct lg4ff_device *device)
  595. {
  596. struct lg4ff_effect_parameters parameters;
  597. Uint8 cmd[7] = {0};
  598. int i;
  599. bool ret;
  600. // Set/unset fixed loop mode
  601. cmd[0] = 0x0d;
  602. //cmd[1] = fixed_loop ? 1 : 0;
  603. cmd[1] = 0;
  604. ret = SDL_SendJoystickEffect(device->hid_handle, cmd, 7);
  605. if (!ret) {
  606. return -1;
  607. }
  608. SDL_memset(&device->states, 0, sizeof(device->states));
  609. SDL_memset(&device->slots, 0, sizeof(device->slots));
  610. SDL_memset(&parameters, 0, sizeof(parameters));
  611. device->slots[0].effect_type = SDL_HAPTIC_CONSTANT;
  612. device->slots[1].effect_type = SDL_HAPTIC_SPRING;
  613. device->slots[2].effect_type = SDL_HAPTIC_DAMPER;
  614. device->slots[3].effect_type = SDL_HAPTIC_FRICTION;
  615. for (i = 0; i < 4; i++) {
  616. device->slots[i].id = i;
  617. lg4ff_update_slot(&device->slots[i], &parameters);
  618. ret = SDL_SendJoystickEffect(device->hid_handle, cmd, 7);
  619. if (!ret) {
  620. return -1;
  621. }
  622. device->slots[i].is_updated = 0;
  623. }
  624. return 0;
  625. }
  626. /*
  627. *Ported*
  628. Original function by:
  629. Bernat Arlandis <[email protected]>
  630. `git blame 1a2d5727876dd7befce23d9695924e9446b31c4b hid-lg4ff.c`, https://github.com/berarma/new-lg4ff.git
  631. */
  632. static int lg4ff_timer(struct lg4ff_device *device)
  633. {
  634. struct lg4ff_slot *slot;
  635. struct lg4ff_effect_state *state;
  636. struct lg4ff_effect_parameters parameters[4];
  637. Uint64 now = get_time_ms();
  638. Uint16 gain;
  639. Sint32 count;
  640. Sint32 effect_id;
  641. int i;
  642. Sint32 ffb_level;
  643. int status = 0;
  644. // XXX how to detect stacked up effects here?
  645. SDL_memset(parameters, 0, sizeof(parameters));
  646. gain = (Uint16)((Uint32)device->gain * device->app_gain / 0xffff);
  647. count = device->effects_used;
  648. for (effect_id = 0; effect_id < LG4FF_MAX_EFFECTS; effect_id++) {
  649. if (!count) {
  650. break;
  651. }
  652. state = &device->states[effect_id];
  653. if (!test_bit(FF_EFFECT_STARTED, &state->flags)) {
  654. continue;
  655. }
  656. count--;
  657. if (test_bit(FF_EFFECT_ALLSET, &state->flags)) {
  658. if (get_effect_replay_length(&state->effect) && time_after_eq(now, state->stop_at)) {
  659. STOP_EFFECT(state);
  660. if (!--state->count) {
  661. device->effects_used--;
  662. continue;
  663. }
  664. __set_bit(FF_EFFECT_STARTED, &state->flags);
  665. state->start_at = state->stop_at;
  666. }
  667. }
  668. lg4ff_update_state(state, now);
  669. if (!test_bit(FF_EFFECT_PLAYING, &state->flags)) {
  670. continue;
  671. }
  672. if (effect_is_periodic(&state->effect)) {
  673. parameters[0].level += lg4ff_calculate_periodic(state);
  674. } else {
  675. switch (state->effect.type) {
  676. case SDL_HAPTIC_CONSTANT:
  677. parameters[0].level += lg4ff_calculate_constant(state);
  678. break;
  679. case SDL_HAPTIC_RAMP:
  680. parameters[0].level += lg4ff_calculate_ramp(state);
  681. break;
  682. case SDL_HAPTIC_SPRING:
  683. lg4ff_calculate_spring(state, &parameters[1]);
  684. break;
  685. case SDL_HAPTIC_DAMPER:
  686. lg4ff_calculate_resistance(state, &parameters[2]);
  687. break;
  688. case SDL_HAPTIC_FRICTION:
  689. lg4ff_calculate_resistance(state, &parameters[3]);
  690. break;
  691. }
  692. }
  693. }
  694. parameters[0].level = (Sint64)parameters[0].level * gain / 0xffff;
  695. parameters[1].clip = parameters[1].clip * device->spring_level / 100;
  696. parameters[2].clip = parameters[2].clip * device->damper_level / 100;
  697. parameters[3].clip = parameters[3].clip * device->friction_level / 100;
  698. ffb_level = abs32(parameters[0].level);
  699. for (i = 1; i < 4; i++) {
  700. parameters[i].k1 = (Sint64)parameters[i].k1 * gain / 0xffff;
  701. parameters[i].k2 = (Sint64)parameters[i].k2 * gain / 0xffff;
  702. parameters[i].clip = parameters[i].clip * gain / 0xffff;
  703. ffb_level = (Sint32)(ffb_level + parameters[i].clip * 0x7fff / 0xffff);
  704. }
  705. if (ffb_level > device->peak_ffb_level) {
  706. device->peak_ffb_level = ffb_level;
  707. }
  708. for (i = 0; i < 4; i++) {
  709. slot = &device->slots[i];
  710. lg4ff_update_slot(slot, &parameters[i]);
  711. if (slot->is_updated) {
  712. bool ret = SDL_SendJoystickEffect(device->hid_handle, slot->current_cmd, 7);
  713. if (!ret) {
  714. status = -1;
  715. }
  716. slot->is_updated = 0;
  717. }
  718. }
  719. return status;
  720. }
  721. static bool SDL_HIDAPI_HapticDriverLg4ff_JoystickSupported(SDL_Joystick *joystick)
  722. {
  723. Uint16 vendor_id = SDL_GetJoystickVendor(joystick);
  724. Uint16 product_id = SDL_GetJoystickProduct(joystick);
  725. if (vendor_id != USB_VENDOR_ID_LOGITECH) {
  726. return false;
  727. }
  728. for (int i = 0;i < sizeof(supported_device_ids) / sizeof(Uint32);i++) {
  729. if (supported_device_ids[i] == product_id) {
  730. return true;
  731. }
  732. }
  733. return false;
  734. }
  735. static int SDLCALL SDL_HIDAPI_HapticDriverLg4ff_ThreadFunction(void *ctx_in)
  736. {
  737. lg4ff_device *ctx = (lg4ff_device *)ctx_in;
  738. while (true) {
  739. if (ctx->stop_thread) {
  740. return 0;
  741. }
  742. SDL_LockMutex(ctx->mutex);
  743. lg4ff_timer(ctx);
  744. SDL_UnlockMutex(ctx->mutex);
  745. SDL_Delay(2);
  746. }
  747. }
  748. static int SDL_HIDAPI_HapticDriverLg4ff_GetEnvInt(const char *env_name, int min, int max, int def)
  749. {
  750. const char *env = SDL_getenv(env_name);
  751. int value = 0;
  752. if (env == NULL) {
  753. return def;
  754. }
  755. value = SDL_atoi(env);
  756. if (value < min) {
  757. value = min;
  758. }
  759. if (value > max) {
  760. value = max;
  761. }
  762. return value;
  763. }
  764. /*
  765. ffex identification method by:
  766. Simon Wood <[email protected]>
  767. Michal Malý <[email protected]> <[email protected]>
  768. lg4ff_init
  769. `git blame v6.12 drivers/hid/hid-lg4ff.c`, https://github.com/torvalds/linux.git
  770. */
  771. static void *SDL_HIDAPI_HapticDriverLg4ff_Open(SDL_Joystick *joystick)
  772. {
  773. lg4ff_device *ctx;
  774. if (!SDL_HIDAPI_HapticDriverLg4ff_JoystickSupported(joystick)) {
  775. SDL_SetError("Device not supported by the lg4ff hidapi haptic driver");
  776. return NULL;
  777. }
  778. ctx = SDL_malloc(sizeof(lg4ff_device));
  779. if (ctx == NULL) {
  780. SDL_OutOfMemory();
  781. return NULL;
  782. }
  783. SDL_memset(ctx, 0, sizeof(lg4ff_device));
  784. ctx->hid_handle = joystick;
  785. if (lg4ff_init_slots(ctx) != 0) {
  786. SDL_SetError("lg4ff hidapi driver failed initializing effect slots");
  787. SDL_free(ctx);
  788. return NULL;
  789. }
  790. ctx->mutex = SDL_CreateMutex();
  791. if (ctx->mutex == NULL) {
  792. SDL_free(ctx);
  793. return NULL;
  794. }
  795. ctx->spring_level = SDL_HIDAPI_HapticDriverLg4ff_GetEnvInt("SDL_HAPTIC_LG4FF_SPRING", 0, 100, 30);
  796. ctx->damper_level = SDL_HIDAPI_HapticDriverLg4ff_GetEnvInt("SDL_HAPTIC_LG4FF_DAMPER", 0, 100, 30);
  797. ctx->friction_level = SDL_HIDAPI_HapticDriverLg4ff_GetEnvInt("SDL_HAPTIC_LG4FF_FRICTION", 0, 100, 30);
  798. ctx->gain = SDL_HIDAPI_HapticDriverLg4ff_GetEnvInt("SDL_HAPTIC_LG4FF_GAIN", 0, 65535, 65535);
  799. ctx->app_gain = 65535;
  800. ctx->product_id = SDL_GetJoystickProduct(joystick);
  801. ctx->release_number = SDL_GetJoystickProductVersion(joystick);
  802. SDL_snprintf(ctx->thread_name_buf, sizeof(ctx->thread_name_buf), "SDL_hidapihaptic_lg4ff %d %04x:%04x", SDL_GetJoystickID(joystick), USB_VENDOR_ID_LOGITECH, ctx->product_id);
  803. ctx->stop_thread = false;
  804. ctx->thread = SDL_CreateThread(SDL_HIDAPI_HapticDriverLg4ff_ThreadFunction, ctx->thread_name_buf, ctx);
  805. if (ctx->product_id == USB_DEVICE_ID_LOGITECH_WHEEL &&
  806. (ctx->release_number >> 8) == 0x21 &&
  807. (ctx->release_number & 0xff) == 0x00) {
  808. ctx->is_ffex = true;
  809. } else {
  810. ctx->is_ffex = false;
  811. }
  812. return ctx;
  813. }
  814. static bool SDL_HIDAPI_HapticDriverLg4ff_StopEffects(SDL_HIDAPI_HapticDevice *device)
  815. {
  816. lg4ff_device *ctx = (lg4ff_device *)device->ctx;
  817. int i;
  818. SDL_LockMutex(ctx->mutex);
  819. for (i = 0;i < LG4FF_MAX_EFFECTS;i++) {
  820. struct lg4ff_effect_state *state = &ctx->states[i];
  821. STOP_EFFECT(state);
  822. }
  823. SDL_UnlockMutex(ctx->mutex);
  824. return true;
  825. }
  826. static void SDL_HIDAPI_HapticDriverLg4ff_Close(SDL_HIDAPI_HapticDevice *device)
  827. {
  828. lg4ff_device *ctx = (lg4ff_device *)device->ctx;
  829. SDL_HIDAPI_HapticDriverLg4ff_StopEffects(device);
  830. // let effects finish in lg4ff_timer
  831. SDL_Delay(50);
  832. ctx->stop_thread = true;
  833. SDL_WaitThread(ctx->thread, NULL);
  834. SDL_DestroyMutex(ctx->mutex);
  835. }
  836. static int SDL_HIDAPI_HapticDriverLg4ff_NumEffects(SDL_HIDAPI_HapticDevice *device)
  837. {
  838. return LG4FF_MAX_EFFECTS;
  839. }
  840. static Uint32 SDL_HIDAPI_HapticDriverLg4ff_GetFeatures(SDL_HIDAPI_HapticDevice *device)
  841. {
  842. return SDL_HAPTIC_CONSTANT |
  843. SDL_HAPTIC_SPRING |
  844. SDL_HAPTIC_DAMPER |
  845. SDL_HAPTIC_AUTOCENTER |
  846. SDL_HAPTIC_SINE |
  847. SDL_HAPTIC_SQUARE |
  848. SDL_HAPTIC_TRIANGLE |
  849. SDL_HAPTIC_SAWTOOTHUP |
  850. SDL_HAPTIC_SAWTOOTHDOWN |
  851. SDL_HAPTIC_RAMP |
  852. SDL_HAPTIC_FRICTION |
  853. SDL_HAPTIC_STATUS |
  854. SDL_HAPTIC_GAIN;
  855. }
  856. static bool SDL_HIDAPI_HapticDriverLg4ff_EffectSupported(SDL_HIDAPI_HapticDevice *device, const SDL_HapticEffect *effect) {
  857. Uint32 features = SDL_HIDAPI_HapticDriverLg4ff_GetFeatures(device);
  858. return (features & effect->type)? true : false;
  859. }
  860. static int SDL_HIDAPI_HapticDriverLg4ff_NumAxes(SDL_HIDAPI_HapticDevice *device)
  861. {
  862. return 1;
  863. }
  864. static int SDL_HIDAPI_HapticDriverLg4ff_CreateEffect(SDL_HIDAPI_HapticDevice *device, const SDL_HapticEffect *data)
  865. {
  866. lg4ff_device *ctx = (lg4ff_device *)device->ctx;
  867. int i;
  868. int state_slot = -1;
  869. int ret;
  870. if (!SDL_HIDAPI_HapticDriverLg4ff_EffectSupported(device, data)) {
  871. SDL_SetError("Unsupported effect");
  872. return -1;
  873. }
  874. SDL_LockMutex(ctx->mutex);
  875. for (i = 0;i < LG4FF_MAX_EFFECTS;i++) {
  876. if (!ctx->states[i].allocated) {
  877. state_slot = i;
  878. break;
  879. }
  880. }
  881. if (state_slot == -1) {
  882. SDL_UnlockMutex(ctx->mutex);
  883. SDL_SetError("All effect slots in-use");
  884. return -1;
  885. }
  886. ret = lg4ff_upload_effect(ctx, data, state_slot);
  887. SDL_UnlockMutex(ctx->mutex);
  888. if (ret == 0) {
  889. ctx->states[state_slot].allocated = true;
  890. return state_slot;
  891. } else {
  892. SDL_SetError("Bad effect parameters");
  893. return -1;
  894. }
  895. }
  896. // assumes ctx->mutex locked
  897. static bool lg4ff_effect_slot_valid_active(lg4ff_device *ctx, int id)
  898. {
  899. if (id >= LG4FF_MAX_EFFECTS || id < 0) {
  900. return false;
  901. }
  902. if (!ctx->states[id].allocated) {
  903. return false;
  904. }
  905. return true;
  906. }
  907. static bool SDL_HIDAPI_HapticDriverLg4ff_UpdateEffect(SDL_HIDAPI_HapticDevice *device, int id, const SDL_HapticEffect *data)
  908. {
  909. lg4ff_device *ctx = (lg4ff_device *)device->ctx;
  910. int ret;
  911. SDL_LockMutex(ctx->mutex);
  912. if (!lg4ff_effect_slot_valid_active(ctx, id)) {
  913. SDL_UnlockMutex(ctx->mutex);
  914. SDL_SetError("Bad effect id");
  915. return false;
  916. }
  917. ret = lg4ff_upload_effect(ctx, data, id);
  918. SDL_UnlockMutex(ctx->mutex);
  919. return ret == 0;
  920. }
  921. static bool SDL_HIDAPI_HapticDriverLg4ff_RunEffect(SDL_HIDAPI_HapticDevice *device, int id, Uint32 iterations)
  922. {
  923. lg4ff_device *ctx = (lg4ff_device *)device->ctx;
  924. int ret;
  925. SDL_LockMutex(ctx->mutex);
  926. if (!lg4ff_effect_slot_valid_active(ctx, id)) {
  927. SDL_UnlockMutex(ctx->mutex);
  928. SDL_SetError("Bad effect id");
  929. return false;
  930. }
  931. ret = lg4ff_play_effect(ctx, id, iterations);
  932. SDL_UnlockMutex(ctx->mutex);
  933. return ret == 0;
  934. }
  935. static bool SDL_HIDAPI_HapticDriverLg4ff_StopEffect(SDL_HIDAPI_HapticDevice *device, int id)
  936. {
  937. return SDL_HIDAPI_HapticDriverLg4ff_RunEffect(device, id, 0);
  938. }
  939. static void SDL_HIDAPI_HapticDriverLg4ff_DestroyEffect(SDL_HIDAPI_HapticDevice *device, int id)
  940. {
  941. lg4ff_device *ctx = (lg4ff_device *)device->ctx;
  942. struct lg4ff_effect_state *state;
  943. SDL_LockMutex(ctx->mutex);
  944. if (!lg4ff_effect_slot_valid_active(ctx, id)) {
  945. SDL_UnlockMutex(ctx->mutex);
  946. return;
  947. }
  948. state = &ctx->states[id];
  949. STOP_EFFECT(state);
  950. state->allocated = false;
  951. SDL_UnlockMutex(ctx->mutex);
  952. }
  953. static bool SDL_HIDAPI_HapticDriverLg4ff_GetEffectStatus(SDL_HIDAPI_HapticDevice *device, int id)
  954. {
  955. lg4ff_device *ctx = (lg4ff_device *)device->ctx;
  956. bool ret = false;
  957. SDL_LockMutex(ctx->mutex);
  958. if (!lg4ff_effect_slot_valid_active(ctx, id)) {
  959. SDL_UnlockMutex(ctx->mutex);
  960. return false;
  961. }
  962. if (test_bit(FF_EFFECT_STARTED, &ctx->states[id].flags)) {
  963. ret = true;
  964. }
  965. SDL_UnlockMutex(ctx->mutex);
  966. return ret;
  967. }
  968. static bool SDL_HIDAPI_HapticDriverLg4ff_SetGain(SDL_HIDAPI_HapticDevice *device, int gain)
  969. {
  970. lg4ff_device *ctx = (lg4ff_device *)device->ctx;
  971. if (gain > 100) {
  972. gain = 100;
  973. }
  974. if (gain < 0) {
  975. gain = 0;
  976. }
  977. ctx->app_gain = (65535 * gain) / 100;
  978. return true;
  979. }
  980. /*
  981. *Ported*
  982. Original functions by:
  983. Simon Wood <[email protected]>
  984. Michal Malý <[email protected]> <[email protected]>
  985. lg4ff_set_autocenter_default lg4ff_set_autocenter_ffex
  986. `git blame v6.12 drivers/hid/hid-lg4ff.c`, https://github.com/torvalds/linux.git
  987. */
  988. static bool SDL_HIDAPI_HapticDriverLg4ff_SetAutocenter(SDL_HIDAPI_HapticDevice *device, int autocenter)
  989. {
  990. lg4ff_device *ctx = (lg4ff_device *)device->ctx;
  991. Uint8 cmd[7] = {0};
  992. bool ret;
  993. if (autocenter < 0) {
  994. autocenter = 0;
  995. }
  996. if (autocenter > 100) {
  997. autocenter = 100;
  998. }
  999. SDL_LockMutex(ctx->mutex);
  1000. if (ctx->is_ffex) {
  1001. int magnitude = (90 * autocenter) / 100;
  1002. cmd[0] = 0xfe;
  1003. cmd[1] = 0x03;
  1004. cmd[2] = (Uint8)((Uint16)magnitude >> 14);
  1005. cmd[3] = (Uint8)((Uint16)magnitude >> 14);
  1006. cmd[4] = (Uint8)magnitude;
  1007. ret = SDL_SendJoystickEffect(ctx->hid_handle, cmd, sizeof(cmd));
  1008. if (!ret) {
  1009. SDL_UnlockMutex(ctx->mutex);
  1010. SDL_SetError("Failed sending autocenter command");
  1011. return false;
  1012. }
  1013. } else {
  1014. Uint32 expand_a;
  1015. Uint32 expand_b;
  1016. int magnitude = (65535 * autocenter) / 100;
  1017. // first disable
  1018. cmd[0] = 0xf5;
  1019. ret = SDL_SendJoystickEffect(ctx->hid_handle, cmd, sizeof(cmd));
  1020. if (!ret) {
  1021. SDL_UnlockMutex(ctx->mutex);
  1022. SDL_SetError("Failed sending autocenter disable command");
  1023. return false;
  1024. }
  1025. if (magnitude == 0) {
  1026. SDL_UnlockMutex(ctx->mutex);
  1027. return true;
  1028. }
  1029. // set strength
  1030. if (magnitude <= 0xaaaa) {
  1031. expand_a = 0x0c * magnitude;
  1032. expand_b = 0x80 * magnitude;
  1033. } else {
  1034. expand_a = (0x0c * 0xaaaa) + 0x06 * (magnitude - 0xaaaa);
  1035. expand_b = (0x80 * 0xaaaa) + 0xff * (magnitude - 0xaaaa);
  1036. }
  1037. expand_a = expand_a >> 1;
  1038. SDL_memset(cmd, 0x00, 7);
  1039. cmd[0] = 0xfe;
  1040. cmd[1] = 0x0d;
  1041. cmd[2] = (Uint8)(expand_a / 0xaaaa);
  1042. cmd[3] = (Uint8)(expand_a / 0xaaaa);
  1043. cmd[4] = (Uint8)(expand_b / 0xaaaa);
  1044. ret = SDL_SendJoystickEffect(ctx->hid_handle, cmd, sizeof(cmd));
  1045. if (!ret) {
  1046. SDL_UnlockMutex(ctx->mutex);
  1047. SDL_SetError("Failed sending autocenter magnitude command");
  1048. return false;
  1049. }
  1050. // enable
  1051. SDL_memset(cmd, 0x00, 7);
  1052. cmd[0] = 0x14;
  1053. ret = SDL_SendJoystickEffect(ctx->hid_handle, cmd, sizeof(cmd));
  1054. if (!ret) {
  1055. SDL_UnlockMutex(ctx->mutex);
  1056. SDL_SetError("Failed sending autocenter enable command");
  1057. return false;
  1058. }
  1059. }
  1060. SDL_UnlockMutex(ctx->mutex);
  1061. return true;
  1062. }
  1063. static bool SDL_HIDAPI_HapticDriverLg4ff_Pause(SDL_HIDAPI_HapticDevice *device)
  1064. {
  1065. return SDL_Unsupported();
  1066. }
  1067. static bool SDL_HIDAPI_HapticDriverLg4ff_Resume(SDL_HIDAPI_HapticDevice *device)
  1068. {
  1069. return SDL_Unsupported();
  1070. }
  1071. SDL_HIDAPI_HapticDriver SDL_HIDAPI_HapticDriverLg4ff = {
  1072. SDL_HIDAPI_HapticDriverLg4ff_JoystickSupported,
  1073. SDL_HIDAPI_HapticDriverLg4ff_Open,
  1074. SDL_HIDAPI_HapticDriverLg4ff_Close,
  1075. SDL_HIDAPI_HapticDriverLg4ff_NumEffects,
  1076. SDL_HIDAPI_HapticDriverLg4ff_NumEffects,
  1077. SDL_HIDAPI_HapticDriverLg4ff_GetFeatures,
  1078. SDL_HIDAPI_HapticDriverLg4ff_NumAxes,
  1079. SDL_HIDAPI_HapticDriverLg4ff_CreateEffect,
  1080. SDL_HIDAPI_HapticDriverLg4ff_UpdateEffect,
  1081. SDL_HIDAPI_HapticDriverLg4ff_RunEffect,
  1082. SDL_HIDAPI_HapticDriverLg4ff_StopEffect,
  1083. SDL_HIDAPI_HapticDriverLg4ff_DestroyEffect,
  1084. SDL_HIDAPI_HapticDriverLg4ff_GetEffectStatus,
  1085. SDL_HIDAPI_HapticDriverLg4ff_SetGain,
  1086. SDL_HIDAPI_HapticDriverLg4ff_SetAutocenter,
  1087. SDL_HIDAPI_HapticDriverLg4ff_Pause,
  1088. SDL_HIDAPI_HapticDriverLg4ff_Resume,
  1089. SDL_HIDAPI_HapticDriverLg4ff_StopEffects,
  1090. };
  1091. #endif //SDL_HAPTIC_HIDAPI_LG4FF
  1092. #endif //SDL_JOYSTICK_HIDAPI