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