src/haptic/linux/SDL_syshaptic.c
author Sam Lantinga <slouken@libsdl.org>
Mon, 22 Jun 2015 23:36:06 -0700
changeset 9776 952ff8a5076f
parent 9743 5f2ac7cec7e9
child 9883 48cf7d652118
permissions -rw-r--r--
Fixed bug 3030 - SDL_RecreateWindow fails to restore title, icon, etc.

Adam M.

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