Simple Directmedia Layer
at main 1132 lines 31 kB view raw
1/* 2 Simple DirectMedia Layer 3 Copyright (C) 1997-2024 Sam Lantinga <slouken@libsdl.org> 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 dest->direction = 0; // Handled by the condition-specifics. 828 829 // Replay 830 dest->replay.length = (condition->length == SDL_HAPTIC_INFINITY) ? 0 : CLAMP(condition->length); 831 dest->replay.delay = CLAMP(condition->delay); 832 833 // Trigger 834 dest->trigger.button = SDL_SYS_ToButton(condition->button); 835 dest->trigger.interval = CLAMP(condition->interval); 836 837 // Condition 838 // X axis 839 dest->u.condition[0].right_saturation = condition->right_sat[0]; 840 dest->u.condition[0].left_saturation = condition->left_sat[0]; 841 dest->u.condition[0].right_coeff = condition->right_coeff[0]; 842 dest->u.condition[0].left_coeff = condition->left_coeff[0]; 843 dest->u.condition[0].deadband = condition->deadband[0]; 844 dest->u.condition[0].center = condition->center[0]; 845 // Y axis 846 dest->u.condition[1].right_saturation = condition->right_sat[1]; 847 dest->u.condition[1].left_saturation = condition->left_sat[1]; 848 dest->u.condition[1].right_coeff = condition->right_coeff[1]; 849 dest->u.condition[1].left_coeff = condition->left_coeff[1]; 850 dest->u.condition[1].deadband = condition->deadband[1]; 851 dest->u.condition[1].center = condition->center[1]; 852 853 /* 854 * There is no envelope in the linux force feedback api for conditions. 855 */ 856 857 break; 858 859 case SDL_HAPTIC_RAMP: 860 ramp = &src->ramp; 861 862 // Header 863 dest->type = FF_RAMP; 864 if (!SDL_SYS_ToDirection(&dest->direction, &ramp->direction)) { 865 return false; 866 } 867 868 // Replay 869 dest->replay.length = (ramp->length == SDL_HAPTIC_INFINITY) ? 0 : CLAMP(ramp->length); 870 dest->replay.delay = CLAMP(ramp->delay); 871 872 // Trigger 873 dest->trigger.button = SDL_SYS_ToButton(ramp->button); 874 dest->trigger.interval = CLAMP(ramp->interval); 875 876 // Ramp 877 dest->u.ramp.start_level = ramp->start; 878 dest->u.ramp.end_level = ramp->end; 879 880 // Envelope 881 dest->u.ramp.envelope.attack_length = CLAMP(ramp->attack_length); 882 dest->u.ramp.envelope.attack_level = CLAMP(ramp->attack_level); 883 dest->u.ramp.envelope.fade_length = CLAMP(ramp->fade_length); 884 dest->u.ramp.envelope.fade_level = CLAMP(ramp->fade_level); 885 886 break; 887 888 case SDL_HAPTIC_LEFTRIGHT: 889 leftright = &src->leftright; 890 891 // Header 892 dest->type = FF_RUMBLE; 893 dest->direction = 0; 894 895 // Replay 896 dest->replay.length = (leftright->length == SDL_HAPTIC_INFINITY) ? 0 : CLAMP(leftright->length); 897 898 // Trigger 899 dest->trigger.button = 0; 900 dest->trigger.interval = 0; 901 902 // Rumble (Linux expects 0-65535, so multiply by 2) 903 dest->u.rumble.strong_magnitude = CLAMP(leftright->large_magnitude) * 2; 904 dest->u.rumble.weak_magnitude = CLAMP(leftright->small_magnitude) * 2; 905 906 break; 907 908 default: 909 return SDL_SetError("Haptic: Unknown effect type."); 910 } 911 912 return true; 913} 914 915/* 916 * Creates a new haptic effect. 917 */ 918bool SDL_SYS_HapticNewEffect(SDL_Haptic *haptic, struct haptic_effect *effect, 919 const SDL_HapticEffect *base) 920{ 921 struct ff_effect *linux_effect; 922 923 // Allocate the hardware effect 924 effect->hweffect = (struct haptic_hweffect *) 925 SDL_calloc(1, sizeof(struct haptic_hweffect)); 926 if (!effect->hweffect) { 927 return false; 928 } 929 930 // Prepare the ff_effect 931 linux_effect = &effect->hweffect->effect; 932 if (!SDL_SYS_ToFFEffect(linux_effect, base)) { 933 goto new_effect_err; 934 } 935 linux_effect->id = -1; // Have the kernel give it an id 936 937 // Upload the effect 938 if (ioctl(haptic->hwdata->fd, EVIOCSFF, linux_effect) < 0) { 939 SDL_SetError("Haptic: Error uploading effect to the device: %s", 940 strerror(errno)); 941 goto new_effect_err; 942 } 943 944 return true; 945 946new_effect_err: 947 SDL_free(effect->hweffect); 948 effect->hweffect = NULL; 949 return false; 950} 951 952/* 953 * Updates an effect. 954 * 955 * Note: Dynamically updating the direction can in some cases force 956 * the effect to restart and run once. 957 */ 958bool SDL_SYS_HapticUpdateEffect(SDL_Haptic *haptic, 959 struct haptic_effect *effect, 960 const SDL_HapticEffect *data) 961{ 962 struct ff_effect linux_effect; 963 964 // Create the new effect 965 if (!SDL_SYS_ToFFEffect(&linux_effect, data)) { 966 return false; 967 } 968 linux_effect.id = effect->hweffect->effect.id; 969 970 // See if it can be uploaded. 971 if (ioctl(haptic->hwdata->fd, EVIOCSFF, &linux_effect) < 0) { 972 return SDL_SetError("Haptic: Error updating the effect: %s", 973 strerror(errno)); 974 } 975 976 // Copy the new effect into memory. 977 SDL_memcpy(&effect->hweffect->effect, &linux_effect, 978 sizeof(struct ff_effect)); 979 980 return true; 981} 982 983/* 984 * Runs an effect. 985 */ 986bool SDL_SYS_HapticRunEffect(SDL_Haptic *haptic, struct haptic_effect *effect, 987 Uint32 iterations) 988{ 989 struct input_event run; 990 991 // Prepare to run the effect 992 run.type = EV_FF; 993 run.code = effect->hweffect->effect.id; 994 // We don't actually have infinity here, so we just do INT_MAX which is pretty damn close. 995 run.value = (iterations > INT_MAX) ? INT_MAX : iterations; 996 997 if (write(haptic->hwdata->fd, (const void *)&run, sizeof(run)) < 0) { 998 return SDL_SetError("Haptic: Unable to run the effect: %s", strerror(errno)); 999 } 1000 1001 return true; 1002} 1003 1004/* 1005 * Stops an effect. 1006 */ 1007bool SDL_SYS_HapticStopEffect(SDL_Haptic *haptic, struct haptic_effect *effect) 1008{ 1009 struct input_event stop; 1010 1011 stop.type = EV_FF; 1012 stop.code = effect->hweffect->effect.id; 1013 stop.value = 0; 1014 1015 if (write(haptic->hwdata->fd, (const void *)&stop, sizeof(stop)) < 0) { 1016 return SDL_SetError("Haptic: Unable to stop the effect: %s", 1017 strerror(errno)); 1018 } 1019 1020 return true; 1021} 1022 1023/* 1024 * Frees the effect. 1025 */ 1026void SDL_SYS_HapticDestroyEffect(SDL_Haptic *haptic, struct haptic_effect *effect) 1027{ 1028 if (ioctl(haptic->hwdata->fd, EVIOCRMFF, effect->hweffect->effect.id) < 0) { 1029 SDL_SetError("Haptic: Error removing the effect from the device: %s", 1030 strerror(errno)); 1031 } 1032 SDL_free(effect->hweffect); 1033 effect->hweffect = NULL; 1034} 1035 1036/* 1037 * Gets the status of a haptic effect. 1038 */ 1039int SDL_SYS_HapticGetEffectStatus(SDL_Haptic *haptic, 1040 struct haptic_effect *effect) 1041{ 1042#if 0 // Not supported atm. 1043 struct input_event ie; 1044 1045 ie.type = EV_FF; 1046 ie.type = EV_FF_STATUS; 1047 ie.code = effect->hweffect->effect.id; 1048 1049 if (write(haptic->hwdata->fd, &ie, sizeof(ie)) < 0) { 1050 SDL_SetError("Haptic: Error getting device status."); 1051 return -1; 1052 } 1053 1054 return 1; 1055#endif 1056 1057 SDL_Unsupported(); 1058 return -1; 1059} 1060 1061/* 1062 * Sets the gain. 1063 */ 1064bool SDL_SYS_HapticSetGain(SDL_Haptic *haptic, int gain) 1065{ 1066 struct input_event ie; 1067 1068 ie.type = EV_FF; 1069 ie.code = FF_GAIN; 1070 ie.value = (0xFFFFUL * gain) / 100; 1071 1072 if (write(haptic->hwdata->fd, &ie, sizeof(ie)) < 0) { 1073 return SDL_SetError("Haptic: Error setting gain: %s", strerror(errno)); 1074 } 1075 1076 return true; 1077} 1078 1079/* 1080 * Sets the autocentering. 1081 */ 1082bool SDL_SYS_HapticSetAutocenter(SDL_Haptic *haptic, int autocenter) 1083{ 1084 struct input_event ie; 1085 1086 ie.type = EV_FF; 1087 ie.code = FF_AUTOCENTER; 1088 ie.value = (0xFFFFUL * autocenter) / 100; 1089 1090 if (write(haptic->hwdata->fd, &ie, sizeof(ie)) < 0) { 1091 return SDL_SetError("Haptic: Error setting autocenter: %s", strerror(errno)); 1092 } 1093 1094 return true; 1095} 1096 1097/* 1098 * Pausing is not supported atm by linux. 1099 */ 1100bool SDL_SYS_HapticPause(SDL_Haptic *haptic) 1101{ 1102 return SDL_Unsupported(); 1103} 1104 1105/* 1106 * Unpausing is not supported atm by linux. 1107 */ 1108bool SDL_SYS_HapticResume(SDL_Haptic *haptic) 1109{ 1110 return SDL_Unsupported(); 1111} 1112 1113/* 1114 * Stops all the currently playing effects. 1115 */ 1116bool SDL_SYS_HapticStopAll(SDL_Haptic *haptic) 1117{ 1118 int i, ret; 1119 1120 // Linux does not support this natively so we have to loop. 1121 for (i = 0; i < haptic->neffects; i++) { 1122 if (haptic->effects[i].hweffect != NULL) { 1123 ret = SDL_SYS_HapticStopEffect(haptic, &haptic->effects[i]); 1124 if (ret < 0) { 1125 return SDL_SetError("Haptic: Error while trying to stop all playing effects."); 1126 } 1127 } 1128 } 1129 return true; 1130} 1131 1132#endif // SDL_HAPTIC_LINUX