sdl

FORK: Simple Directmedia Layer
git clone https://git.neptards.moe/neptards/sdl.git
Log | Files | Refs

SDL_syshaptic.c (31948B)


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