Atlas - SDL_syshaptic.c
Home / ext / SDL / src / haptic / linux Lines: 2 | Size: 31119 bytes [Download] [Show on GitHub] [Search similar files] [Raw] [Raw (proxy)][FILE BEGIN]1/* 2 Simple DirectMedia Layer 3 Copyright (C) 1997-2025 Sam Lantinga <[email protected]> 4 5 This software is provided 'as-is', without any express or implied 6 warranty. In no event will the authors be held liable for any damages 7 arising from the use of this software. 8 9 Permission is granted to anyone to use this software for any purpose, 10 including commercial applications, and to alter it and redistribute it 11 freely, subject to the following restrictions: 12 13 1. The origin of this software must not be misrepresented; you must not 14 claim that you wrote the original software. If you use this software 15 in a product, an acknowledgment in the product documentation would be 16 appreciated but is not required. 17 2. Altered source versions must be plainly marked as such, and must not be 18 misrepresented as being the original software. 19 3. This notice may not be removed or altered from any source distribution. 20*/ 21#include "SDL_internal.h" 22 23#ifdef SDL_HAPTIC_LINUX 24 25#include "../SDL_syshaptic.h" 26#include "../../joystick/SDL_sysjoystick.h" // For the real SDL_Joystick 27#include "../../joystick/linux/SDL_sysjoystick_c.h" // For joystick hwdata 28#include "../../core/linux/SDL_evdev_capabilities.h" 29#include "../../core/linux/SDL_udev.h" 30 31#include <unistd.h> // close 32#include <linux/input.h> // Force feedback linux stuff. 33#include <fcntl.h> // O_RDWR 34#include <limits.h> // INT_MAX 35#include <errno.h> // errno 36#include <string.h> // strerror 37#include <sys/stat.h> // stat 38 39#define MAX_HAPTICS 32 // It's doubtful someone has more then 32 evdev 40 41static bool MaybeAddDevice(const char *path); 42#ifdef SDL_USE_LIBUDEV 43static bool MaybeRemoveDevice(const char *path); 44static void haptic_udev_callback(SDL_UDEV_deviceevent udev_type, int udev_class, const char *devpath); 45#endif // SDL_USE_LIBUDEV 46 47/* 48 * List of available haptic devices. 49 */ 50typedef struct SDL_hapticlist_item 51{ 52 SDL_HapticID instance_id; 53 char *fname; // Dev path name (like /dev/input/event1) 54 SDL_Haptic *haptic; // Associated haptic. 55 dev_t dev_num; 56 struct SDL_hapticlist_item *next; 57} SDL_hapticlist_item; 58 59/* 60 * Haptic system hardware data. 61 */ 62struct haptic_hwdata 63{ 64 int fd; // File descriptor of the device. 65 char *fname; // Points to the name in SDL_hapticlist. 66}; 67 68/* 69 * Haptic system effect data. 70 */ 71struct haptic_hweffect 72{ 73 struct ff_effect effect; // The linux kernel effect structure. 74}; 75 76static SDL_hapticlist_item *SDL_hapticlist = NULL; 77static SDL_hapticlist_item *SDL_hapticlist_tail = NULL; 78static int numhaptics = 0; 79 80#define EV_TEST(ev, f) \ 81 if (test_bit((ev), features)) { \ 82 ret |= (f); \ 83 } 84/* 85 * Test whether a device has haptic properties. 86 * Returns available properties or 0 if there are none. 87 */ 88static Uint32 EV_IsHaptic(int fd) 89{ 90 unsigned long features[1 + FF_MAX / sizeof(unsigned long)]; 91 Uint32 ret = 0; 92 93 // Ask device for what it has. 94 if (ioctl(fd, EVIOCGBIT(EV_FF, sizeof(features)), features) < 0) { 95 SDL_SetError("Haptic: Unable to get device's features: %s", strerror(errno)); 96 return 0; 97 } 98 99 // Convert supported features to SDL_HAPTIC platform-neutral features. 100 EV_TEST(FF_CONSTANT, SDL_HAPTIC_CONSTANT); 101 EV_TEST(FF_SINE, SDL_HAPTIC_SINE); 102 EV_TEST(FF_SQUARE, SDL_HAPTIC_SQUARE); 103 EV_TEST(FF_TRIANGLE, SDL_HAPTIC_TRIANGLE); 104 EV_TEST(FF_SAW_UP, SDL_HAPTIC_SAWTOOTHUP); 105 EV_TEST(FF_SAW_DOWN, SDL_HAPTIC_SAWTOOTHDOWN); 106 EV_TEST(FF_RAMP, SDL_HAPTIC_RAMP); 107 EV_TEST(FF_SPRING, SDL_HAPTIC_SPRING); 108 EV_TEST(FF_FRICTION, SDL_HAPTIC_FRICTION); 109 EV_TEST(FF_DAMPER, SDL_HAPTIC_DAMPER); 110 EV_TEST(FF_INERTIA, SDL_HAPTIC_INERTIA); 111 EV_TEST(FF_CUSTOM, SDL_HAPTIC_CUSTOM); 112 EV_TEST(FF_GAIN, SDL_HAPTIC_GAIN); 113 EV_TEST(FF_AUTOCENTER, SDL_HAPTIC_AUTOCENTER); 114 EV_TEST(FF_RUMBLE, SDL_HAPTIC_LEFTRIGHT); 115 116 // Return what it supports. 117 return ret; 118} 119 120/* 121 * Tests whether a device is a mouse or not. 122 */ 123static bool EV_IsMouse(int fd) 124{ 125 unsigned long argp[40]; 126 127 // Ask for supported features. 128 if (ioctl(fd, EVIOCGBIT(EV_KEY, sizeof(argp)), argp) < 0) { 129 return false; 130 } 131 132 // Currently we only test for BTN_MOUSE which can give fake positives. 133 if (test_bit(BTN_MOUSE, argp) != 0) { 134 return true; 135 } 136 137 return true; 138} 139 140/* 141 * Initializes the haptic subsystem by finding available devices. 142 */ 143bool SDL_SYS_HapticInit(void) 144{ 145 const char joydev_pattern[] = "/dev/input/event%d"; 146 char path[PATH_MAX]; 147 int i, j; 148 149 /* 150 * Limit amount of checks to MAX_HAPTICS since we may or may not have 151 * permission to some or all devices. 152 */ 153 i = 0; 154 for (j = 0; j < MAX_HAPTICS; ++j) { 155 (void)SDL_snprintf(path, PATH_MAX, joydev_pattern, i++); 156 MaybeAddDevice(path); 157 } 158 159#ifdef SDL_USE_LIBUDEV 160 if (!SDL_UDEV_Init()) { 161 return SDL_SetError("Could not initialize UDEV"); 162 } 163 164 if (!SDL_UDEV_AddCallback(haptic_udev_callback)) { 165 SDL_UDEV_Quit(); 166 return SDL_SetError("Could not setup haptic <-> udev callback"); 167 } 168 169 // Force a scan to build the initial device list 170 SDL_UDEV_Scan(); 171#endif // SDL_USE_LIBUDEV 172 173 return true; 174} 175 176int SDL_SYS_NumHaptics(void) 177{ 178 return numhaptics; 179} 180 181static SDL_hapticlist_item *HapticByDevIndex(int device_index) 182{ 183 SDL_hapticlist_item *item = SDL_hapticlist; 184 185 if ((device_index < 0) || (device_index >= numhaptics)) { 186 return NULL; 187 } 188 189 while (device_index > 0) { 190 SDL_assert(item != NULL); 191 --device_index; 192 item = item->next; 193 } 194 195 return item; 196} 197 198static SDL_hapticlist_item *HapticByInstanceID(SDL_HapticID instance_id) 199{ 200 SDL_hapticlist_item *item; 201 for (item = SDL_hapticlist; item; item = item->next) { 202 if (instance_id == item->instance_id) { 203 return item; 204 } 205 } 206 return NULL; 207} 208 209#ifdef SDL_USE_LIBUDEV 210static void haptic_udev_callback(SDL_UDEV_deviceevent udev_type, int udev_class, const char *devpath) 211{ 212 if (!devpath || !(udev_class & SDL_UDEV_DEVICE_JOYSTICK)) { 213 return; 214 } 215 216 switch (udev_type) { 217 case SDL_UDEV_DEVICEADDED: 218 MaybeAddDevice(devpath); 219 break; 220 221 case SDL_UDEV_DEVICEREMOVED: 222 MaybeRemoveDevice(devpath); 223 break; 224 225 default: 226 break; 227 } 228} 229#endif // SDL_USE_LIBUDEV 230 231static bool MaybeAddDevice(const char *path) 232{ 233 struct stat sb; 234 int fd; 235 Uint32 supported; 236 SDL_hapticlist_item *item; 237 238 if (!path) { 239 return false; 240 } 241 242 // try to open 243 fd = open(path, O_RDWR | O_CLOEXEC, 0); 244 if (fd < 0) { 245 return false; 246 } 247 248 // get file status 249 if (fstat(fd, &sb) != 0) { 250 close(fd); 251 return false; 252 } 253 254 // check for duplicates 255 for (item = SDL_hapticlist; item; item = item->next) { 256 if (item->dev_num == sb.st_rdev) { 257 close(fd); 258 return false; // duplicate. 259 } 260 } 261 262#ifdef DEBUG_INPUT_EVENTS 263 printf("Checking %s\n", path); 264#endif 265 266 // see if it works 267 supported = EV_IsHaptic(fd); 268 close(fd); 269 if (!supported) { 270 return false; 271 } 272 273 item = (SDL_hapticlist_item *)SDL_calloc(1, sizeof(SDL_hapticlist_item)); 274 if (!item) { 275 return false; 276 } 277 278 item->instance_id = SDL_GetNextObjectID(); 279 item->fname = SDL_strdup(path); 280 if (!item->fname) { 281 SDL_free(item); 282 return false; 283 } 284 285 item->dev_num = sb.st_rdev; 286 287 // TODO: should we add instance IDs? 288 if (!SDL_hapticlist_tail) { 289 SDL_hapticlist = SDL_hapticlist_tail = item; 290 } else { 291 SDL_hapticlist_tail->next = item; 292 SDL_hapticlist_tail = item; 293 } 294 295 ++numhaptics; 296 297 // !!! TODO: Send a haptic add event? 298 299 return true; 300} 301 302#ifdef SDL_USE_LIBUDEV 303static bool MaybeRemoveDevice(const char *path) 304{ 305 SDL_hapticlist_item *item; 306 SDL_hapticlist_item *prev = NULL; 307 308 if (!path) { 309 return false; 310 } 311 312 for (item = SDL_hapticlist; item; item = item->next) { 313 // found it, remove it. 314 if (SDL_strcmp(path, item->fname) == 0) { 315 const bool result = item->haptic ? true : false; 316 317 if (prev) { 318 prev->next = item->next; 319 } else { 320 SDL_assert(SDL_hapticlist == item); 321 SDL_hapticlist = item->next; 322 } 323 if (item == SDL_hapticlist_tail) { 324 SDL_hapticlist_tail = prev; 325 } 326 327 // Need to decrement the haptic count 328 --numhaptics; 329 // !!! TODO: Send a haptic remove event? 330 331 SDL_free(item->fname); 332 SDL_free(item); 333 return result; 334 } 335 prev = item; 336 } 337 338 return false; 339} 340#endif // SDL_USE_LIBUDEV 341 342/* 343 * Return the instance ID of a haptic device, does not need to be opened. 344 */ 345SDL_HapticID SDL_SYS_HapticInstanceID(int index) 346{ 347 SDL_hapticlist_item *item; 348 349 item = HapticByDevIndex(index); 350 if (item) { 351 return item->instance_id; 352 } 353 return 0; 354} 355 356/* 357 * Gets the name from a file descriptor. 358 */ 359static const char *SDL_SYS_HapticNameFromFD(int fd) 360{ 361 static char namebuf[128]; 362 363 // We use the evdev name ioctl. 364 if (ioctl(fd, EVIOCGNAME(sizeof(namebuf)), namebuf) <= 0) { 365 return NULL; 366 } 367 368 return namebuf; 369} 370 371/* 372 * Return the name of a haptic device, does not need to be opened. 373 */ 374const char *SDL_SYS_HapticName(int index) 375{ 376 SDL_hapticlist_item *item; 377 int fd; 378 const char *name = NULL; 379 380 item = HapticByDevIndex(index); 381 if (item) { 382 // Open the haptic device. 383 fd = open(item->fname, O_RDONLY | O_CLOEXEC, 0); 384 385 if (fd >= 0) { 386 387 name = SDL_SYS_HapticNameFromFD(fd); 388 if (!name) { 389 // No name found, return device character device 390 name = item->fname; 391 } 392 close(fd); 393 } 394 } 395 return name; 396} 397 398/* 399 * Opens the haptic device from the file descriptor. 400 */ 401static bool SDL_SYS_HapticOpenFromFD(SDL_Haptic *haptic, int fd) 402{ 403 // Allocate the hwdata 404 haptic->hwdata = (struct haptic_hwdata *) 405 SDL_calloc(1, sizeof(*haptic->hwdata)); 406 if (!haptic->hwdata) { 407 goto open_err; 408 } 409 410 // Set the data. 411 haptic->hwdata->fd = fd; 412 haptic->supported = EV_IsHaptic(fd); 413 haptic->naxes = 2; // Hardcoded for now, not sure if it's possible to find out. 414 415 // Set the effects 416 if (ioctl(fd, EVIOCGEFFECTS, &haptic->neffects) < 0) { 417 SDL_SetError("Haptic: Unable to query device memory: %s", 418 strerror(errno)); 419 goto open_err; 420 } 421 haptic->nplaying = haptic->neffects; // Linux makes no distinction. 422 haptic->effects = (struct haptic_effect *) 423 SDL_malloc(sizeof(struct haptic_effect) * haptic->neffects); 424 if (!haptic->effects) { 425 goto open_err; 426 } 427 // Clear the memory 428 SDL_memset(haptic->effects, 0, 429 sizeof(struct haptic_effect) * haptic->neffects); 430 431 return true; 432 433 // Error handling 434open_err: 435 close(fd); 436 if (haptic->hwdata) { 437 SDL_free(haptic->hwdata); 438 haptic->hwdata = NULL; 439 } 440 return false; 441} 442 443/* 444 * Opens a haptic device for usage. 445 */ 446bool SDL_SYS_HapticOpen(SDL_Haptic *haptic) 447{ 448 int fd; 449 SDL_hapticlist_item *item; 450 451 item = HapticByInstanceID(haptic->instance_id); 452 // Open the character device 453 fd = open(item->fname, O_RDWR | O_CLOEXEC, 0); 454 if (fd < 0) { 455 return SDL_SetError("Haptic: Unable to open %s: %s", 456 item->fname, strerror(errno)); 457 } 458 459 // Try to create the haptic. 460 if (!SDL_SYS_HapticOpenFromFD(haptic, fd)) { 461 // Already closes on error. 462 return false; 463 } 464 465 // Set the fname. 466 haptic->hwdata->fname = SDL_strdup(item->fname); 467 return true; 468} 469 470/* 471 * Opens a haptic device from first mouse it finds for usage. 472 */ 473int SDL_SYS_HapticMouse(void) 474{ 475 int fd; 476 int device_index = 0; 477 SDL_hapticlist_item *item; 478 479 for (item = SDL_hapticlist; item; item = item->next) { 480 // Open the device. 481 fd = open(item->fname, O_RDWR | O_CLOEXEC, 0); 482 if (fd < 0) { 483 return SDL_SetError("Haptic: Unable to open %s: %s", 484 item->fname, strerror(errno)); 485 } 486 487 // Is it a mouse? 488 if (EV_IsMouse(fd)) { 489 close(fd); 490 return device_index; 491 } 492 493 close(fd); 494 495 ++device_index; 496 } 497 498 return -1; 499} 500 501/* 502 * Checks to see if a joystick has haptic features. 503 */ 504bool SDL_SYS_JoystickIsHaptic(SDL_Joystick *joystick) 505{ 506#ifdef SDL_JOYSTICK_LINUX 507 SDL_AssertJoysticksLocked(); 508 509 if (joystick->driver != &SDL_LINUX_JoystickDriver) { 510 return false; 511 } 512 if (EV_IsHaptic(joystick->hwdata->fd)) { 513 return true; 514 } 515#endif 516 return false; 517} 518 519/* 520 * Checks to see if the haptic device and joystick are in reality the same. 521 */ 522bool SDL_SYS_JoystickSameHaptic(SDL_Haptic *haptic, SDL_Joystick *joystick) 523{ 524#ifdef SDL_JOYSTICK_LINUX 525 SDL_AssertJoysticksLocked(); 526 527 if (joystick->driver != &SDL_LINUX_JoystickDriver) { 528 return false; 529 } 530 /* We are assuming Linux is using evdev which should trump the old 531 * joystick methods. */ 532 if (SDL_strcmp(joystick->hwdata->fname, haptic->hwdata->fname) == 0) { 533 return true; 534 } 535#endif 536 return false; 537} 538 539/* 540 * Opens a SDL_Haptic from a SDL_Joystick. 541 */ 542bool SDL_SYS_HapticOpenFromJoystick(SDL_Haptic *haptic, SDL_Joystick *joystick) 543{ 544#ifdef SDL_JOYSTICK_LINUX 545 int fd; 546 SDL_hapticlist_item *item; 547 const char *name; 548 549 SDL_AssertJoysticksLocked(); 550 551 if (joystick->driver != &SDL_LINUX_JoystickDriver) { 552 return false; 553 } 554 // Find the joystick in the haptic list. 555 for (item = SDL_hapticlist; item; item = item->next) { 556 if (SDL_strcmp(item->fname, joystick->hwdata->fname) == 0) { 557 haptic->instance_id = item->instance_id; 558 break; 559 } 560 } 561 562 fd = open(joystick->hwdata->fname, O_RDWR | O_CLOEXEC, 0); 563 if (fd < 0) { 564 return SDL_SetError("Haptic: Unable to open %s: %s", 565 joystick->hwdata->fname, strerror(errno)); 566 } 567 if (!SDL_SYS_HapticOpenFromFD(haptic, fd)) { 568 // Already closes on error. 569 return false; 570 } 571 572 haptic->hwdata->fname = SDL_strdup(joystick->hwdata->fname); 573 574 name = SDL_SYS_HapticNameFromFD(fd); 575 if (name) { 576 haptic->name = SDL_strdup(name); 577 } 578 return true; 579#else 580 return false; 581#endif 582} 583 584/* 585 * Closes the haptic device. 586 */ 587void SDL_SYS_HapticClose(SDL_Haptic *haptic) 588{ 589 if (haptic->hwdata) { 590 591 // Free effects. 592 SDL_free(haptic->effects); 593 haptic->effects = NULL; 594 haptic->neffects = 0; 595 596 // Clean up 597 close(haptic->hwdata->fd); 598 599 // Free 600 SDL_free(haptic->hwdata->fname); 601 SDL_free(haptic->hwdata); 602 haptic->hwdata = NULL; 603 } 604 605 // Clear the rest. 606 SDL_memset(haptic, 0, sizeof(SDL_Haptic)); 607} 608 609/* 610 * Clean up after system specific haptic stuff 611 */ 612void SDL_SYS_HapticQuit(void) 613{ 614 SDL_hapticlist_item *item = NULL; 615 SDL_hapticlist_item *next = NULL; 616 617 for (item = SDL_hapticlist; item; item = next) { 618 next = item->next; 619 /* Opened and not closed haptics are leaked, this is on purpose. 620 * Close your haptic devices after usage. */ 621 SDL_free(item->fname); 622 SDL_free(item); 623 } 624 625#ifdef SDL_USE_LIBUDEV 626 SDL_UDEV_DelCallback(haptic_udev_callback); 627 SDL_UDEV_Quit(); 628#endif // SDL_USE_LIBUDEV 629 630 numhaptics = 0; 631 SDL_hapticlist = NULL; 632 SDL_hapticlist_tail = NULL; 633} 634 635/* 636 * Converts an SDL button to a ff_trigger button. 637 */ 638static Uint16 SDL_SYS_ToButton(Uint16 button) 639{ 640 Uint16 ff_button; 641 642 ff_button = 0; 643 644 /* 645 * Not sure what the proper syntax is because this actually isn't implemented 646 * in the current kernel from what I've seen (2.6.26). 647 */ 648 if (button != 0) { 649 ff_button = BTN_GAMEPAD + button - 1; 650 } 651 652 return ff_button; 653} 654 655/* 656 * Initializes the ff_effect usable direction from a SDL_HapticDirection. 657 */ 658static bool SDL_SYS_ToDirection(Uint16 *dest, const SDL_HapticDirection *src) 659{ 660 Uint32 tmp; 661 662 switch (src->type) { 663 case SDL_HAPTIC_POLAR: 664 tmp = ((src->dir[0] % 36000) * 0x8000) / 18000; // convert to range [0,0xFFFF] 665 *dest = (Uint16)tmp; 666 break; 667 668 case SDL_HAPTIC_SPHERICAL: 669 /* 670 We convert to polar, because that's the only supported direction on Linux. 671 The first value of a spherical direction is practically the same as a 672 Polar direction, except that we have to add 90 degrees. It is the angle 673 from EAST {1,0} towards SOUTH {0,1}. 674 --> add 9000 675 --> finally convert to [0,0xFFFF] as in case SDL_HAPTIC_POLAR. 676 */ 677 tmp = ((src->dir[0]) + 9000) % 36000; // Convert to polars 678 tmp = (tmp * 0x8000) / 18000; // convert to range [0,0xFFFF] 679 *dest = (Uint16)tmp; 680 break; 681 682 case SDL_HAPTIC_CARTESIAN: 683 if (!src->dir[1]) { 684 *dest = (src->dir[0] >= 0 ? 0x4000 : 0xC000); 685 } else if (!src->dir[0]) { 686 *dest = (src->dir[1] >= 0 ? 0x8000 : 0); 687 } else { 688 float f = SDL_atan2f(src->dir[1], src->dir[0]); // Ideally we'd use fixed point math instead of floats... 689 /* 690 SDL_atan2 takes the parameters: Y-axis-value and X-axis-value (in that order) 691 - Y-axis-value is the second coordinate (from center to SOUTH) 692 - X-axis-value is the first coordinate (from center to EAST) 693 We add 36000, because SDL_atan2 also returns negative values. Then we practically 694 have the first spherical value. Therefore we proceed as in case 695 SDL_HAPTIC_SPHERICAL and add another 9000 to get the polar value. 696 --> add 45000 in total 697 --> finally convert to [0,0xFFFF] as in case SDL_HAPTIC_POLAR. 698 */ 699 tmp = (((Sint32)(f * 18000.0 / SDL_PI_D)) + 45000) % 36000; 700 tmp = (tmp * 0x8000) / 18000; // convert to range [0,0xFFFF] 701 *dest = (Uint16)tmp; 702 } 703 break; 704 case SDL_HAPTIC_STEERING_AXIS: 705 *dest = 0x4000; 706 break; 707 default: 708 return SDL_SetError("Haptic: Unsupported direction type."); 709 } 710 711 return true; 712} 713 714#define CLAMP(x) (((x) > 32767) ? 32767 : x) 715/* 716 * Initializes the Linux effect struct from a haptic_effect. 717 * Values above 32767 (for unsigned) are unspecified so we must clamp. 718 */ 719static bool SDL_SYS_ToFFEffect(struct ff_effect *dest, const SDL_HapticEffect *src) 720{ 721 const SDL_HapticConstant *constant; 722 const SDL_HapticPeriodic *periodic; 723 const SDL_HapticCondition *condition; 724 const SDL_HapticRamp *ramp; 725 const SDL_HapticLeftRight *leftright; 726 727 // Clear up 728 SDL_memset(dest, 0, sizeof(struct ff_effect)); 729 730 switch (src->type) { 731 case SDL_HAPTIC_CONSTANT: 732 constant = &src->constant; 733 734 // Header 735 dest->type = FF_CONSTANT; 736 if (!SDL_SYS_ToDirection(&dest->direction, &constant->direction)) { 737 return false; 738 } 739 740 // Replay 741 dest->replay.length = (constant->length == SDL_HAPTIC_INFINITY) ? 0 : CLAMP(constant->length); 742 dest->replay.delay = CLAMP(constant->delay); 743 744 // Trigger 745 dest->trigger.button = SDL_SYS_ToButton(constant->button); 746 dest->trigger.interval = CLAMP(constant->interval); 747 748 // Constant 749 dest->u.constant.level = constant->level; 750 751 // Envelope 752 dest->u.constant.envelope.attack_length = 753 CLAMP(constant->attack_length); 754 dest->u.constant.envelope.attack_level = 755 CLAMP(constant->attack_level); 756 dest->u.constant.envelope.fade_length = CLAMP(constant->fade_length); 757 dest->u.constant.envelope.fade_level = CLAMP(constant->fade_level); 758 759 break; 760 761 case SDL_HAPTIC_SINE: 762 case SDL_HAPTIC_SQUARE: 763 case SDL_HAPTIC_TRIANGLE: 764 case SDL_HAPTIC_SAWTOOTHUP: 765 case SDL_HAPTIC_SAWTOOTHDOWN: 766 periodic = &src->periodic; 767 768 // Header 769 dest->type = FF_PERIODIC; 770 if (!SDL_SYS_ToDirection(&dest->direction, &periodic->direction)) { 771 return false; 772 } 773 774 // Replay 775 dest->replay.length = (periodic->length == SDL_HAPTIC_INFINITY) ? 0 : CLAMP(periodic->length); 776 dest->replay.delay = CLAMP(periodic->delay); 777 778 // Trigger 779 dest->trigger.button = SDL_SYS_ToButton(periodic->button); 780 dest->trigger.interval = CLAMP(periodic->interval); 781 782 // Periodic 783 if (periodic->type == SDL_HAPTIC_SINE) { 784 dest->u.periodic.waveform = FF_SINE; 785 } else if (periodic->type == SDL_HAPTIC_SQUARE) { 786 dest->u.periodic.waveform = FF_SQUARE; 787 } else if (periodic->type == SDL_HAPTIC_TRIANGLE) { 788 dest->u.periodic.waveform = FF_TRIANGLE; 789 } else if (periodic->type == SDL_HAPTIC_SAWTOOTHUP) { 790 dest->u.periodic.waveform = FF_SAW_UP; 791 } else if (periodic->type == SDL_HAPTIC_SAWTOOTHDOWN) { 792 dest->u.periodic.waveform = FF_SAW_DOWN; 793 } 794 dest->u.periodic.period = CLAMP(periodic->period); 795 dest->u.periodic.magnitude = periodic->magnitude; 796 dest->u.periodic.offset = periodic->offset; 797 // Linux phase is defined in interval "[0x0000, 0x10000[", corresponds with "[0deg, 360deg[" phase shift. 798 dest->u.periodic.phase = ((Uint32)periodic->phase * 0x10000U) / 36000; 799 800 // Envelope 801 dest->u.periodic.envelope.attack_length = 802 CLAMP(periodic->attack_length); 803 dest->u.periodic.envelope.attack_level = 804 CLAMP(periodic->attack_level); 805 dest->u.periodic.envelope.fade_length = CLAMP(periodic->fade_length); 806 dest->u.periodic.envelope.fade_level = CLAMP(periodic->fade_level); 807 808 break; 809 810 case SDL_HAPTIC_SPRING: 811 case SDL_HAPTIC_DAMPER: 812 case SDL_HAPTIC_INERTIA: 813 case SDL_HAPTIC_FRICTION: 814 condition = &src->condition; 815 816 // Header 817 if (condition->type == SDL_HAPTIC_SPRING) { 818 dest->type = FF_SPRING; 819 } else if (condition->type == SDL_HAPTIC_DAMPER) { 820 dest->type = FF_DAMPER; 821 } else if (condition->type == SDL_HAPTIC_INERTIA) { 822 dest->type = FF_INERTIA; 823 } else if (condition->type == SDL_HAPTIC_FRICTION) { 824 dest->type = FF_FRICTION; 825 } 826 827 if (!SDL_SYS_ToDirection(&dest->direction, &condition->direction)) { 828 return false; 829 } 830 831 // Replay 832 dest->replay.length = (condition->length == SDL_HAPTIC_INFINITY) ? 0 : CLAMP(condition->length); 833 dest->replay.delay = CLAMP(condition->delay); 834 835 // Trigger 836 dest->trigger.button = SDL_SYS_ToButton(condition->button); 837 dest->trigger.interval = CLAMP(condition->interval); 838 839 // Condition 840 // X axis 841 dest->u.condition[0].right_saturation = condition->right_sat[0]; 842 dest->u.condition[0].left_saturation = condition->left_sat[0]; 843 dest->u.condition[0].right_coeff = condition->right_coeff[0]; 844 dest->u.condition[0].left_coeff = condition->left_coeff[0]; 845 dest->u.condition[0].deadband = condition->deadband[0]; 846 dest->u.condition[0].center = condition->center[0]; 847 // Y axis 848 dest->u.condition[1].right_saturation = condition->right_sat[1]; 849 dest->u.condition[1].left_saturation = condition->left_sat[1]; 850 dest->u.condition[1].right_coeff = condition->right_coeff[1]; 851 dest->u.condition[1].left_coeff = condition->left_coeff[1]; 852 dest->u.condition[1].deadband = condition->deadband[1]; 853 dest->u.condition[1].center = condition->center[1]; 854 855 /* 856 * There is no envelope in the linux force feedback api for conditions. 857 */ 858 859 break; 860 861 case SDL_HAPTIC_RAMP: 862 ramp = &src->ramp; 863 864 // Header 865 dest->type = FF_RAMP; 866 if (!SDL_SYS_ToDirection(&dest->direction, &ramp->direction)) { 867 return false; 868 } 869 870 // Replay 871 dest->replay.length = (ramp->length == SDL_HAPTIC_INFINITY) ? 0 : CLAMP(ramp->length); 872 dest->replay.delay = CLAMP(ramp->delay); 873 874 // Trigger 875 dest->trigger.button = SDL_SYS_ToButton(ramp->button); 876 dest->trigger.interval = CLAMP(ramp->interval); 877 878 // Ramp 879 dest->u.ramp.start_level = ramp->start; 880 dest->u.ramp.end_level = ramp->end; 881 882 // Envelope 883 dest->u.ramp.envelope.attack_length = CLAMP(ramp->attack_length); 884 dest->u.ramp.envelope.attack_level = CLAMP(ramp->attack_level); 885 dest->u.ramp.envelope.fade_length = CLAMP(ramp->fade_length); 886 dest->u.ramp.envelope.fade_level = CLAMP(ramp->fade_level); 887 888 break; 889 890 case SDL_HAPTIC_LEFTRIGHT: 891 leftright = &src->leftright; 892 893 // Header 894 dest->type = FF_RUMBLE; 895 dest->direction = 0x4000; 896 897 // Replay 898 dest->replay.length = (leftright->length == SDL_HAPTIC_INFINITY) ? 0 : CLAMP(leftright->length); 899 900 // Trigger 901 dest->trigger.button = 0; 902 dest->trigger.interval = 0; 903 904 // Rumble (Linux expects 0-65535, so multiply by 2) 905 dest->u.rumble.strong_magnitude = CLAMP(leftright->large_magnitude) * 2; 906 dest->u.rumble.weak_magnitude = CLAMP(leftright->small_magnitude) * 2; 907 908 break; 909 910 default: 911 return SDL_SetError("Haptic: Unknown effect type."); 912 } 913 914 return true; 915} 916 917/* 918 * Creates a new haptic effect. 919 */ 920bool SDL_SYS_HapticNewEffect(SDL_Haptic *haptic, struct haptic_effect *effect, 921 const SDL_HapticEffect *base) 922{ 923 struct ff_effect *linux_effect; 924 925 // Allocate the hardware effect 926 effect->hweffect = (struct haptic_hweffect *) 927 SDL_calloc(1, sizeof(struct haptic_hweffect)); 928 if (!effect->hweffect) { 929 return false; 930 } 931 932 // Prepare the ff_effect 933 linux_effect = &effect->hweffect->effect; 934 if (!SDL_SYS_ToFFEffect(linux_effect, base)) { 935 goto new_effect_err; 936 } 937 linux_effect->id = -1; // Have the kernel give it an id 938 939 // Upload the effect 940 if (ioctl(haptic->hwdata->fd, EVIOCSFF, linux_effect) < 0) { 941 SDL_SetError("Haptic: Error uploading effect to the device: %s", 942 strerror(errno)); 943 goto new_effect_err; 944 } 945 946 return true; 947 948new_effect_err: 949 SDL_free(effect->hweffect); 950 effect->hweffect = NULL; 951 return false; 952} 953 954/* 955 * Updates an effect. 956 * 957 * Note: Dynamically updating the direction can in some cases force 958 * the effect to restart and run once. 959 */ 960bool SDL_SYS_HapticUpdateEffect(SDL_Haptic *haptic, 961 struct haptic_effect *effect, 962 const SDL_HapticEffect *data) 963{ 964 struct ff_effect linux_effect; 965 966 // Create the new effect 967 if (!SDL_SYS_ToFFEffect(&linux_effect, data)) { 968 return false; 969 } 970 linux_effect.id = effect->hweffect->effect.id; 971 972 // See if it can be uploaded. 973 if (ioctl(haptic->hwdata->fd, EVIOCSFF, &linux_effect) < 0) { 974 return SDL_SetError("Haptic: Error updating the effect: %s", 975 strerror(errno)); 976 } 977 978 // Copy the new effect into memory. 979 SDL_memcpy(&effect->hweffect->effect, &linux_effect, 980 sizeof(struct ff_effect)); 981 982 return true; 983} 984 985/* 986 * Runs an effect. 987 */ 988bool SDL_SYS_HapticRunEffect(SDL_Haptic *haptic, struct haptic_effect *effect, 989 Uint32 iterations) 990{ 991 struct input_event run; 992 993 // Prepare to run the effect 994 run.type = EV_FF; 995 run.code = effect->hweffect->effect.id; 996 // We don't actually have infinity here, so we just do INT_MAX which is pretty damn close. 997 run.value = (iterations > INT_MAX) ? INT_MAX : iterations; 998 999 if (write(haptic->hwdata->fd, (const void *)&run, sizeof(run)) < 0) { 1000 return SDL_SetError("Haptic: Unable to run the effect: %s", strerror(errno)); 1001 } 1002 1003 return true; 1004} 1005 1006/* 1007 * Stops an effect. 1008 */ 1009bool SDL_SYS_HapticStopEffect(SDL_Haptic *haptic, struct haptic_effect *effect) 1010{ 1011 struct input_event stop; 1012 1013 stop.type = EV_FF; 1014 stop.code = effect->hweffect->effect.id; 1015 stop.value = 0; 1016 1017 if (write(haptic->hwdata->fd, (const void *)&stop, sizeof(stop)) < 0) { 1018 return SDL_SetError("Haptic: Unable to stop the effect: %s", 1019 strerror(errno)); 1020 } 1021 1022 return true; 1023} 1024 1025/* 1026 * Frees the effect. 1027 */ 1028void SDL_SYS_HapticDestroyEffect(SDL_Haptic *haptic, struct haptic_effect *effect) 1029{ 1030 if (ioctl(haptic->hwdata->fd, EVIOCRMFF, effect->hweffect->effect.id) < 0) { 1031 SDL_SetError("Haptic: Error removing the effect from the device: %s", 1032 strerror(errno)); 1033 } 1034 SDL_free(effect->hweffect); 1035 effect->hweffect = NULL; 1036} 1037 1038/* 1039 * Gets the status of a haptic effect. 1040 */ 1041int SDL_SYS_HapticGetEffectStatus(SDL_Haptic *haptic, 1042 struct haptic_effect *effect) 1043{ 1044#if 0 // Not supported atm. 1045 struct input_event ie; 1046 1047 ie.type = EV_FF; 1048 ie.type = EV_FF_STATUS; 1049 ie.code = effect->hweffect->effect.id; 1050 1051 if (write(haptic->hwdata->fd, &ie, sizeof(ie)) < 0) { 1052 SDL_SetError("Haptic: Error getting device status."); 1053 return -1; 1054 } 1055 1056 return 1; 1057#endif 1058 1059 SDL_Unsupported(); 1060 return -1; 1061} 1062 1063/* 1064 * Sets the gain. 1065 */ 1066bool SDL_SYS_HapticSetGain(SDL_Haptic *haptic, int gain) 1067{ 1068 struct input_event ie; 1069 1070 ie.type = EV_FF; 1071 ie.code = FF_GAIN; 1072 ie.value = (0xFFFFUL * gain) / 100; 1073 1074 if (write(haptic->hwdata->fd, &ie, sizeof(ie)) < 0) { 1075 return SDL_SetError("Haptic: Error setting gain: %s", strerror(errno)); 1076 } 1077 1078 return true; 1079} 1080 1081/* 1082 * Sets the autocentering. 1083 */ 1084bool SDL_SYS_HapticSetAutocenter(SDL_Haptic *haptic, int autocenter) 1085{ 1086 struct input_event ie; 1087 1088 ie.type = EV_FF; 1089 ie.code = FF_AUTOCENTER; 1090 ie.value = (0xFFFFUL * autocenter) / 100; 1091 1092 if (write(haptic->hwdata->fd, &ie, sizeof(ie)) < 0) { 1093 return SDL_SetError("Haptic: Error setting autocenter: %s", strerror(errno)); 1094 } 1095 1096 return true; 1097} 1098 1099/* 1100 * Pausing is not supported atm by linux. 1101 */ 1102bool SDL_SYS_HapticPause(SDL_Haptic *haptic) 1103{ 1104 return SDL_Unsupported(); 1105} 1106 1107/* 1108 * Unpausing is not supported atm by linux. 1109 */ 1110bool SDL_SYS_HapticResume(SDL_Haptic *haptic) 1111{ 1112 return SDL_Unsupported(); 1113} 1114 1115/* 1116 * Stops all the currently playing effects. 1117 */ 1118bool SDL_SYS_HapticStopAll(SDL_Haptic *haptic) 1119{ 1120 int i; 1121 1122 // Linux does not support this natively so we have to loop. 1123 for (i = 0; i < haptic->neffects; i++) { 1124 if (haptic->effects[i].hweffect != NULL) { 1125 if (!SDL_SYS_HapticStopEffect(haptic, &haptic->effects[i])) { 1126 return SDL_SetError("Haptic: Error while trying to stop all playing effects."); 1127 } 1128 } 1129 } 1130 return true; 1131} 1132 1133#endif // SDL_HAPTIC_LINUX 1134[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.