cscg22-gearboy

CSCG 2022 Challenge 'Gearboy'
git clone https://git.sinitax.com/sinitax/cscg22-gearboy
Log | Files | Refs | sfeed.txt

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 */