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