src/haptic/linux/SDL_syshaptic.c
author Sam Lantinga
Mon, 04 May 2020 13:17:43 -0700
changeset 13789 a359f4f93439
parent 13645 f7fc52b64177
child 13864 3aa34ecfeb63
permissions -rw-r--r--
Improvement for bug 3446 - The haptic API does not allow to select the direction axes

meyraud705

I see how the documentation is confusing. I think that the choice of the axis is an implementation detail. The documentation should state the goal of this value, so I propose this wording:

"Use this value to play an effect on the steering wheel axis. This provides
better compatibility across platforms and devices as SDL will guess the
correct axis."

Value could even be renamed 'SDL_HAPTIC_STEERING_AXIS'.

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