src/joystick/linux/SDL_sysjoystick.c
author Sam Lantinga <slouken@libsdl.org>
Sun, 13 Nov 2016 22:57:41 -0800
changeset 10609 d702ecbd8ba7
parent 10595 9da2701eeb4a
child 10617 346c02ff71b6
permissions -rw-r--r--
Patch from Sylvain to fix clang warnings
slouken@0
     1
/*
slouken@5535
     2
  Simple DirectMedia Layer
slouken@9998
     3
  Copyright (C) 1997-2016 Sam Lantinga <slouken@libsdl.org>
slouken@0
     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@0
     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@0
    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@0
    20
*/
icculus@8093
    21
#include "../../SDL_internal.h"
slouken@0
    22
slouken@1635
    23
#ifdef SDL_JOYSTICK_LINUX
slouken@1635
    24
icculus@6729
    25
#ifndef SDL_INPUT_LINUXEV
icculus@6729
    26
#error SDL now requires a Linux 2.4+ kernel with /dev/input/event support.
icculus@6729
    27
#endif
icculus@6729
    28
philipp@8138
    29
/* This is the Linux implementation of the SDL joystick API */
slouken@0
    30
slouken@0
    31
#include <sys/stat.h>
slouken@0
    32
#include <unistd.h>
slouken@0
    33
#include <fcntl.h>
slouken@0
    34
#include <sys/ioctl.h>
slouken@1895
    35
#include <limits.h>             /* For the definition of PATH_MAX */
slouken@0
    36
#include <linux/joystick.h>
slouken@0
    37
icculus@6734
    38
#include "SDL_assert.h"
slouken@0
    39
#include "SDL_joystick.h"
icculus@6734
    40
#include "SDL_endian.h"
slouken@1361
    41
#include "../SDL_sysjoystick.h"
slouken@1361
    42
#include "../SDL_joystick_c.h"
slouken@2713
    43
#include "SDL_sysjoystick_c.h"
slouken@0
    44
slouken@6910
    45
/* This isn't defined in older Linux kernel headers */
slouken@6910
    46
#ifndef SYN_DROPPED
slouken@6910
    47
#define SYN_DROPPED 3
slouken@6910
    48
#endif
slouken@6910
    49
gabomdq@7772
    50
#include "../../core/linux/SDL_udev.h"
icculus@6734
    51
gabomdq@7772
    52
static int MaybeAddDevice(const char *path);
gabomdq@7772
    53
#if SDL_USE_LIBUDEV
gabomdq@7772
    54
static int MaybeRemoveDevice(const char *path);
slouken@7788
    55
void joystick_udev_callback(SDL_UDEV_deviceevent udev_type, int udev_class, const char *devpath);
gabomdq@7772
    56
#endif /* SDL_USE_LIBUDEV */
slouken@892
    57
slouken@0
    58
icculus@6734
    59
/* A linked list of available joysticks */
icculus@6734
    60
typedef struct SDL_joylist_item
icculus@6734
    61
{
icculus@6734
    62
    int device_instance;
icculus@6734
    63
    char *path;   /* "/dev/input/event2" or whatever */
icculus@6734
    64
    char *name;   /* "SideWinder 3D Pro" or whatever */
slouken@6743
    65
    SDL_JoystickGUID guid;
icculus@6734
    66
    dev_t devnum;
icculus@6734
    67
    struct joystick_hwdata *hwdata;
icculus@6734
    68
    struct SDL_joylist_item *next;
icculus@6734
    69
} SDL_joylist_item;
icculus@6734
    70
icculus@6734
    71
static SDL_joylist_item *SDL_joylist = NULL;
icculus@6734
    72
static SDL_joylist_item *SDL_joylist_tail = NULL;
icculus@6734
    73
static int numjoysticks = 0;
icculus@6734
    74
static int instance_counter = 0;
icculus@6734
    75
slouken@0
    76
#define test_bit(nr, addr) \
icculus@6734
    77
    (((1UL << ((nr) % (sizeof(long) * 8))) & ((addr)[(nr) / (sizeof(long) * 8)])) != 0)
slouken@3404
    78
#define NBITS(x) ((((x)-1)/(sizeof(long) * 8))+1)
slouken@0
    79
slouken@1895
    80
static int
slouken@6743
    81
IsJoystick(int fd, char *namebuf, const size_t namebuflen, SDL_JoystickGUID *guid)
slouken@0
    82
{
gabomdq@7909
    83
    struct input_id inpid;
slouken@10595
    84
    Uint16 *guid16 = (Uint16 *)guid->data;
gabomdq@7909
    85
gabomdq@7909
    86
#if !SDL_USE_LIBUDEV
gabomdq@7909
    87
    /* When udev is enabled we only get joystick devices here, so there's no need to test them */
slouken@3404
    88
    unsigned long evbit[NBITS(EV_MAX)] = { 0 };
slouken@3404
    89
    unsigned long keybit[NBITS(KEY_MAX)] = { 0 };
slouken@3404
    90
    unsigned long absbit[NBITS(ABS_MAX)] = { 0 };
slouken@0
    91
slouken@1895
    92
    if ((ioctl(fd, EVIOCGBIT(0, sizeof(evbit)), evbit) < 0) ||
slouken@1895
    93
        (ioctl(fd, EVIOCGBIT(EV_KEY, sizeof(keybit)), keybit) < 0) ||
slouken@1895
    94
        (ioctl(fd, EVIOCGBIT(EV_ABS, sizeof(absbit)), absbit) < 0)) {
slouken@1895
    95
        return (0);
slouken@1895
    96
    }
bobbens@3079
    97
slouken@1895
    98
    if (!(test_bit(EV_KEY, evbit) && test_bit(EV_ABS, evbit) &&
bobbens@3079
    99
          test_bit(ABS_X, absbit) && test_bit(ABS_Y, absbit))) {
slouken@1895
   100
        return 0;
bobbens@3079
   101
    }
gabomdq@7909
   102
#endif
icculus@6734
   103
icculus@6734
   104
    if (ioctl(fd, EVIOCGNAME(namebuflen), namebuf) < 0) {
icculus@6734
   105
        return 0;
icculus@6734
   106
    }
icculus@6734
   107
icculus@6734
   108
    if (ioctl(fd, EVIOCGID, &inpid) < 0) {
icculus@6734
   109
        return 0;
icculus@6734
   110
    }
icculus@6734
   111
slouken@6831
   112
#ifdef DEBUG_JOYSTICK
slouken@10595
   113
    printf("Joystick: %s, bustype = %d, vendor = 0x%.4x, product = 0x%.4x, version = %d\n", namebuf, inpid.bustype, inpid.vendor, inpid.product, inpid.version);
slouken@6831
   114
#endif
slouken@6831
   115
slouken@6831
   116
    SDL_memset(guid->data, 0, sizeof(guid->data));
slouken@6831
   117
icculus@6734
   118
    /* We only need 16 bits for each of these; space them out to fill 128. */
icculus@6734
   119
    /* Byteswap so devices get same GUID on little/big endian platforms. */
slouken@10595
   120
    *guid16++ = SDL_SwapLE16(inpid.bustype);
slouken@10595
   121
    *guid16++ = 0;
slouken@6831
   122
slouken@10595
   123
    if (inpid.vendor && inpid.product) {
slouken@10595
   124
        *guid16++ = SDL_SwapLE16(inpid.vendor);
slouken@10595
   125
        *guid16++ = 0;
slouken@10595
   126
        *guid16++ = SDL_SwapLE16(inpid.product);
slouken@10595
   127
        *guid16++ = 0;
slouken@10595
   128
        *guid16++ = SDL_SwapLE16(inpid.version);
slouken@10595
   129
        *guid16++ = 0;
slouken@6831
   130
    } else {
slouken@6831
   131
        SDL_strlcpy((char*)guid16, namebuf, sizeof(guid->data) - 4);
slouken@6831
   132
    }
icculus@6734
   133
icculus@6734
   134
    return 1;
slouken@0
   135
}
slouken@0
   136
gabomdq@7772
   137
#if SDL_USE_LIBUDEV
slouken@7788
   138
void joystick_udev_callback(SDL_UDEV_deviceevent udev_type, int udev_class, const char *devpath)
gabomdq@7772
   139
{
slouken@9321
   140
    if (devpath == NULL) {
gabomdq@7772
   141
        return;
gabomdq@7772
   142
    }
slouken@9321
   143
slouken@9321
   144
    switch (udev_type) {
gabomdq@7772
   145
        case SDL_UDEV_DEVICEADDED:
slouken@9321
   146
            if (!(udev_class & SDL_UDEV_DEVICE_JOYSTICK)) {
slouken@9321
   147
                return;
slouken@9321
   148
            }
slouken@7802
   149
            MaybeAddDevice(devpath);
gabomdq@7772
   150
            break;
gabomdq@7772
   151
            
gabomdq@7772
   152
        case SDL_UDEV_DEVICEREMOVED:
slouken@7802
   153
            MaybeRemoveDevice(devpath);
gabomdq@7772
   154
            break;
gabomdq@7772
   155
            
gabomdq@7772
   156
        default:
gabomdq@7772
   157
            break;
gabomdq@7772
   158
    }
gabomdq@7772
   159
    
gabomdq@7772
   160
}
gabomdq@7772
   161
#endif /* SDL_USE_LIBUDEV */
gabomdq@7772
   162
slouken@0
   163
icculus@6734
   164
/* !!! FIXME: I would love to dump this code and use libudev instead. */
icculus@6734
   165
static int
icculus@6734
   166
MaybeAddDevice(const char *path)
icculus@6734
   167
{
icculus@6734
   168
    struct stat sb;
icculus@6734
   169
    int fd = -1;
icculus@6734
   170
    int isstick = 0;
icculus@6734
   171
    char namebuf[128];
slouken@6743
   172
    SDL_JoystickGUID guid;
icculus@6734
   173
    SDL_joylist_item *item;
icculus@6734
   174
icculus@6734
   175
    if (path == NULL) {
icculus@6734
   176
        return -1;
icculus@6734
   177
    }
icculus@6734
   178
icculus@6734
   179
    if (stat(path, &sb) == -1) {
icculus@6734
   180
        return -1;
icculus@6734
   181
    }
icculus@6734
   182
icculus@6734
   183
    /* Check to make sure it's not already in list. */
icculus@6734
   184
    for (item = SDL_joylist; item != NULL; item = item->next) {
icculus@6734
   185
        if (sb.st_rdev == item->devnum) {
icculus@6734
   186
            return -1;  /* already have this one */
icculus@6734
   187
        }
icculus@6734
   188
    }
icculus@6734
   189
icculus@6734
   190
    fd = open(path, O_RDONLY, 0);
icculus@6734
   191
    if (fd < 0) {
icculus@6734
   192
        return -1;
icculus@6734
   193
    }
icculus@6734
   194
icculus@6734
   195
#ifdef DEBUG_INPUT_EVENTS
icculus@6734
   196
    printf("Checking %s\n", path);
icculus@6734
   197
#endif
icculus@6734
   198
icculus@6734
   199
    isstick = IsJoystick(fd, namebuf, sizeof (namebuf), &guid);
icculus@6734
   200
    close(fd);
icculus@6734
   201
    if (!isstick) {
icculus@6734
   202
        return -1;
icculus@6734
   203
    }
icculus@6734
   204
icculus@6734
   205
    item = (SDL_joylist_item *) SDL_malloc(sizeof (SDL_joylist_item));
icculus@6734
   206
    if (item == NULL) {
icculus@6734
   207
        return -1;
icculus@6734
   208
    }
icculus@6734
   209
icculus@6734
   210
    SDL_zerop(item);
icculus@6734
   211
    item->devnum = sb.st_rdev;
icculus@6734
   212
    item->path = SDL_strdup(path);
icculus@6734
   213
    item->name = SDL_strdup(namebuf);
icculus@6734
   214
    item->guid = guid;
icculus@6734
   215
icculus@6734
   216
    if ( (item->path == NULL) || (item->name == NULL) ) {
icculus@6734
   217
         SDL_free(item->path);
icculus@6734
   218
         SDL_free(item->name);
icculus@6734
   219
         SDL_free(item);
icculus@6734
   220
         return -1;
icculus@6734
   221
    }
icculus@6734
   222
icculus@6734
   223
    item->device_instance = instance_counter++;
icculus@6734
   224
    if (SDL_joylist_tail == NULL) {
icculus@6734
   225
        SDL_joylist = SDL_joylist_tail = item;
icculus@6734
   226
    } else {
icculus@6734
   227
        SDL_joylist_tail->next = item;
icculus@6734
   228
        SDL_joylist_tail = item;
icculus@6734
   229
    }
icculus@6734
   230
slouken@7916
   231
    /* Need to increment the joystick count before we post the event */
slouken@7916
   232
    ++numjoysticks;
slouken@7916
   233
slouken@10226
   234
    SDL_PrivateJoystickAdded(numjoysticks - 1);
slouken@7802
   235
slouken@7916
   236
    return numjoysticks;
icculus@6734
   237
}
icculus@6734
   238
icculus@6749
   239
#if SDL_USE_LIBUDEV
icculus@6734
   240
/* !!! FIXME: I would love to dump this code and use libudev instead. */
icculus@6734
   241
static int
icculus@6734
   242
MaybeRemoveDevice(const char *path)
icculus@6734
   243
{
icculus@6734
   244
    SDL_joylist_item *item;
icculus@6734
   245
    SDL_joylist_item *prev = NULL;
icculus@6734
   246
icculus@6734
   247
    if (path == NULL) {
icculus@6734
   248
        return -1;
icculus@6734
   249
    }
slouken@6690
   250
icculus@6734
   251
    for (item = SDL_joylist; item != NULL; item = item->next) {
icculus@6734
   252
        /* found it, remove it. */
icculus@6734
   253
        if (SDL_strcmp(path, item->path) == 0) {
icculus@6734
   254
            const int retval = item->device_instance;
icculus@6734
   255
            if (item->hwdata) {
icculus@6752
   256
                item->hwdata->item = NULL;
icculus@6734
   257
            }
icculus@6734
   258
            if (prev != NULL) {
icculus@6734
   259
                prev->next = item->next;
icculus@6734
   260
            } else {
jorgen@6865
   261
                SDL_assert(SDL_joylist == item);
jorgen@6865
   262
                SDL_joylist = item->next;
jorgen@6865
   263
            }
jorgen@6865
   264
            if (item == SDL_joylist_tail) {
jorgen@6865
   265
                SDL_joylist_tail = prev;
icculus@6734
   266
            }
slouken@7802
   267
slouken@7916
   268
            /* Need to decrement the joystick count before we post the event */
slouken@7916
   269
            --numjoysticks;
slouken@7916
   270
slouken@10226
   271
            SDL_PrivateJoystickRemoved(item->device_instance);
slouken@7802
   272
icculus@6734
   273
            SDL_free(item->path);
icculus@6734
   274
            SDL_free(item->name);
icculus@6734
   275
            SDL_free(item);
icculus@6734
   276
            return retval;
icculus@6734
   277
        }
icculus@6734
   278
        prev = item;
icculus@6734
   279
    }
icculus@6734
   280
icculus@6734
   281
    return -1;
icculus@6734
   282
}
icculus@6749
   283
#endif
icculus@6734
   284
slouken@10609
   285
#if ! SDL_USE_LIBUDEV
icculus@6734
   286
static int
icculus@6734
   287
JoystickInitWithoutUdev(void)
icculus@6734
   288
{
icculus@6734
   289
    int i;
icculus@6734
   290
    char path[PATH_MAX];
icculus@6734
   291
icculus@6734
   292
    /* !!! FIXME: only finds sticks if they're called /dev/input/event[0..31] */
icculus@6734
   293
    /* !!! FIXME:  we could at least readdir() through /dev/input...? */
icculus@6734
   294
    /* !!! FIXME:  (or delete this and rely on libudev?) */
icculus@6734
   295
    for (i = 0; i < 32; i++) {
icculus@6734
   296
        SDL_snprintf(path, SDL_arraysize(path), "/dev/input/event%d", i);
icculus@6734
   297
        MaybeAddDevice(path);
icculus@6734
   298
    }
icculus@6734
   299
icculus@6734
   300
    return numjoysticks;
icculus@6734
   301
}
slouken@10609
   302
#endif
icculus@6734
   303
icculus@6734
   304
#if SDL_USE_LIBUDEV
icculus@6734
   305
static int
icculus@6734
   306
JoystickInitWithUdev(void)
icculus@6734
   307
{
gabomdq@7772
   308
    if (SDL_UDEV_Init() < 0) {
gabomdq@7772
   309
        return SDL_SetError("Could not initialize UDEV");
icculus@6734
   310
    }
icculus@6734
   311
gabomdq@7772
   312
    /* Set up the udev callback */
slouken@9321
   313
    if (SDL_UDEV_AddCallback(joystick_udev_callback) < 0) {
gabomdq@7772
   314
        SDL_UDEV_Quit();
gabomdq@7772
   315
        return SDL_SetError("Could not set up joystick <-> udev callback");
icculus@6734
   316
    }
gabomdq@7772
   317
    
gabomdq@7772
   318
    /* Force a scan to build the initial device list */
gabomdq@7772
   319
    SDL_UDEV_Scan();
icculus@6734
   320
icculus@6734
   321
    return numjoysticks;
icculus@6734
   322
}
icculus@6734
   323
#endif
icculus@6734
   324
slouken@1895
   325
int
slouken@1895
   326
SDL_SYS_JoystickInit(void)
slouken@0
   327
{
slouken@5317
   328
    /* First see if the user specified one or more joysticks to use */
slouken@1895
   329
    if (SDL_getenv("SDL_JOYSTICK_DEVICE") != NULL) {
slouken@5317
   330
        char *envcopy, *envpath, *delim;
slouken@5317
   331
        envcopy = SDL_strdup(SDL_getenv("SDL_JOYSTICK_DEVICE"));
slouken@5317
   332
        envpath = envcopy;
slouken@5317
   333
        while (envpath != NULL) {
slouken@5317
   334
            delim = SDL_strchr(envpath, ':');
slouken@5317
   335
            if (delim != NULL) {
slouken@5317
   336
                *delim++ = '\0';
slouken@5317
   337
            }
icculus@6734
   338
            MaybeAddDevice(envpath);
slouken@5317
   339
            envpath = delim;
slouken@1895
   340
        }
slouken@5317
   341
        SDL_free(envcopy);
slouken@1895
   342
    }
slouken@554
   343
icculus@6734
   344
#if SDL_USE_LIBUDEV
gabomdq@7772
   345
    return JoystickInitWithUdev();
slouken@10609
   346
#else 
slouken@10609
   347
    return JoystickInitWithoutUdev();
slouken@0
   348
#endif
slouken@0
   349
}
slouken@0
   350
slouken@6707
   351
int SDL_SYS_NumJoysticks()
slouken@6707
   352
{
icculus@6734
   353
    return numjoysticks;
icculus@6734
   354
}
icculus@6734
   355
slouken@6707
   356
void SDL_SYS_JoystickDetect()
slouken@6707
   357
{
icculus@6734
   358
#if SDL_USE_LIBUDEV
gabomdq@7772
   359
    SDL_UDEV_Poll();
icculus@6734
   360
#endif
gabomdq@7772
   361
    
slouken@6707
   362
}
slouken@6707
   363
icculus@6734
   364
static SDL_joylist_item *
icculus@6734
   365
JoystickByDevIndex(int device_index)
icculus@6734
   366
{
icculus@6734
   367
    SDL_joylist_item *item = SDL_joylist;
icculus@6734
   368
icculus@6734
   369
    if ((device_index < 0) || (device_index >= numjoysticks)) {
icculus@6734
   370
        return NULL;
icculus@6734
   371
    }
icculus@6734
   372
icculus@6734
   373
    while (device_index > 0) {
icculus@6734
   374
        SDL_assert(item != NULL);
icculus@6734
   375
        device_index--;
icculus@6734
   376
        item = item->next;
icculus@6734
   377
    }
icculus@6734
   378
icculus@6734
   379
    return item;
slouken@6707
   380
}
slouken@6707
   381
slouken@0
   382
/* Function to get the device-dependent name of a joystick */
slouken@1895
   383
const char *
slouken@6707
   384
SDL_SYS_JoystickNameForDeviceIndex(int device_index)
slouken@0
   385
{
icculus@6734
   386
    return JoystickByDevIndex(device_index)->name;
slouken@0
   387
}
slouken@0
   388
slouken@6707
   389
/* Function to perform the mapping from device index to the instance id for this index */
slouken@6707
   390
SDL_JoystickID SDL_SYS_GetInstanceIdOfDeviceIndex(int device_index)
slouken@6707
   391
{
icculus@6734
   392
    return JoystickByDevIndex(device_index)->device_instance;
slouken@6707
   393
}
slouken@6707
   394
slouken@1895
   395
static int
slouken@1895
   396
allocate_hatdata(SDL_Joystick * joystick)
slouken@0
   397
{
slouken@1895
   398
    int i;
slouken@0
   399
slouken@1895
   400
    joystick->hwdata->hats =
slouken@1895
   401
        (struct hwdata_hat *) SDL_malloc(joystick->nhats *
slouken@1895
   402
                                         sizeof(struct hwdata_hat));
slouken@1895
   403
    if (joystick->hwdata->hats == NULL) {
slouken@1895
   404
        return (-1);
slouken@1895
   405
    }
slouken@1895
   406
    for (i = 0; i < joystick->nhats; ++i) {
slouken@1895
   407
        joystick->hwdata->hats[i].axis[0] = 1;
slouken@1895
   408
        joystick->hwdata->hats[i].axis[1] = 1;
slouken@1895
   409
    }
slouken@1895
   410
    return (0);
slouken@0
   411
}
slouken@0
   412
slouken@1895
   413
static int
slouken@1895
   414
allocate_balldata(SDL_Joystick * joystick)
slouken@0
   415
{
slouken@1895
   416
    int i;
slouken@0
   417
slouken@1895
   418
    joystick->hwdata->balls =
slouken@1895
   419
        (struct hwdata_ball *) SDL_malloc(joystick->nballs *
slouken@1895
   420
                                          sizeof(struct hwdata_ball));
slouken@1895
   421
    if (joystick->hwdata->balls == NULL) {
slouken@1895
   422
        return (-1);
slouken@1895
   423
    }
slouken@1895
   424
    for (i = 0; i < joystick->nballs; ++i) {
slouken@1895
   425
        joystick->hwdata->balls[i].axis[0] = 0;
slouken@1895
   426
        joystick->hwdata->balls[i].axis[1] = 0;
slouken@1895
   427
    }
slouken@1895
   428
    return (0);
slouken@0
   429
}
slouken@0
   430
icculus@6729
   431
static void
icculus@6729
   432
ConfigJoystick(SDL_Joystick * joystick, int fd)
slouken@0
   433
{
slouken@1895
   434
    int i, t;
slouken@3404
   435
    unsigned long keybit[NBITS(KEY_MAX)] = { 0 };
slouken@3404
   436
    unsigned long absbit[NBITS(ABS_MAX)] = { 0 };
slouken@3404
   437
    unsigned long relbit[NBITS(REL_MAX)] = { 0 };
slouken@0
   438
slouken@1895
   439
    /* See if this device uses the new unified event API */
slouken@1895
   440
    if ((ioctl(fd, EVIOCGBIT(EV_KEY, sizeof(keybit)), keybit) >= 0) &&
slouken@1895
   441
        (ioctl(fd, EVIOCGBIT(EV_ABS, sizeof(absbit)), absbit) >= 0) &&
slouken@1895
   442
        (ioctl(fd, EVIOCGBIT(EV_REL, sizeof(relbit)), relbit) >= 0)) {
slouken@0
   443
slouken@1895
   444
        /* Get the number of buttons, axes, and other thingamajigs */
slouken@1895
   445
        for (i = BTN_JOYSTICK; i < KEY_MAX; ++i) {
slouken@1895
   446
            if (test_bit(i, keybit)) {
slouken@0
   447
#ifdef DEBUG_INPUT_EVENTS
slouken@1895
   448
                printf("Joystick has button: 0x%x\n", i);
slouken@0
   449
#endif
slouken@1895
   450
                joystick->hwdata->key_map[i - BTN_MISC] = joystick->nbuttons;
slouken@1895
   451
                ++joystick->nbuttons;
slouken@1895
   452
            }
slouken@1895
   453
        }
slouken@1895
   454
        for (i = BTN_MISC; i < BTN_JOYSTICK; ++i) {
slouken@1895
   455
            if (test_bit(i, keybit)) {
slouken@0
   456
#ifdef DEBUG_INPUT_EVENTS
slouken@1895
   457
                printf("Joystick has button: 0x%x\n", i);
slouken@0
   458
#endif
slouken@1895
   459
                joystick->hwdata->key_map[i - BTN_MISC] = joystick->nbuttons;
slouken@1895
   460
                ++joystick->nbuttons;
slouken@1895
   461
            }
slouken@1895
   462
        }
icculus@9630
   463
        for (i = 0; i < ABS_MAX; ++i) {
slouken@1895
   464
            /* Skip hats */
slouken@1895
   465
            if (i == ABS_HAT0X) {
slouken@1895
   466
                i = ABS_HAT3Y;
slouken@1895
   467
                continue;
slouken@1895
   468
            }
slouken@1895
   469
            if (test_bit(i, absbit)) {
slouken@5084
   470
                struct input_absinfo absinfo;
slouken@0
   471
slouken@8053
   472
                if (ioctl(fd, EVIOCGABS(i), &absinfo) < 0) {
slouken@1895
   473
                    continue;
slouken@8053
   474
                }
slouken@0
   475
#ifdef DEBUG_INPUT_EVENTS
slouken@8053
   476
                printf("Joystick has absolute axis: 0x%.2x\n", i);
slouken@1895
   477
                printf("Values = { %d, %d, %d, %d, %d }\n",
slouken@5084
   478
                       absinfo.value, absinfo.minimum, absinfo.maximum,
slouken@5084
   479
                       absinfo.fuzz, absinfo.flat);
slouken@0
   480
#endif /* DEBUG_INPUT_EVENTS */
slouken@1895
   481
                joystick->hwdata->abs_map[i] = joystick->naxes;
slouken@5084
   482
                if (absinfo.minimum == absinfo.maximum) {
slouken@1895
   483
                    joystick->hwdata->abs_correct[i].used = 0;
slouken@1895
   484
                } else {
slouken@1895
   485
                    joystick->hwdata->abs_correct[i].used = 1;
slouken@1895
   486
                    joystick->hwdata->abs_correct[i].coef[0] =
slouken@6845
   487
                        (absinfo.maximum + absinfo.minimum) - 2 * absinfo.flat;
slouken@1895
   488
                    joystick->hwdata->abs_correct[i].coef[1] =
slouken@6845
   489
                        (absinfo.maximum + absinfo.minimum) + 2 * absinfo.flat;
slouken@6845
   490
                    t = ((absinfo.maximum - absinfo.minimum) - 4 * absinfo.flat);
slouken@1895
   491
                    if (t != 0) {
slouken@1895
   492
                        joystick->hwdata->abs_correct[i].coef[2] =
slouken@6845
   493
                            (1 << 28) / t;
slouken@1895
   494
                    } else {
slouken@1895
   495
                        joystick->hwdata->abs_correct[i].coef[2] = 0;
slouken@1895
   496
                    }
slouken@1895
   497
                }
slouken@1895
   498
                ++joystick->naxes;
slouken@1895
   499
            }
slouken@1895
   500
        }
slouken@1895
   501
        for (i = ABS_HAT0X; i <= ABS_HAT3Y; i += 2) {
slouken@1895
   502
            if (test_bit(i, absbit) || test_bit(i + 1, absbit)) {
slouken@8053
   503
                struct input_absinfo absinfo;
slouken@8053
   504
slouken@8053
   505
                if (ioctl(fd, EVIOCGABS(i), &absinfo) < 0) {
slouken@8053
   506
                    continue;
slouken@8053
   507
                }
slouken@0
   508
#ifdef DEBUG_INPUT_EVENTS
slouken@1895
   509
                printf("Joystick has hat %d\n", (i - ABS_HAT0X) / 2);
slouken@8053
   510
                printf("Values = { %d, %d, %d, %d, %d }\n",
slouken@8053
   511
                       absinfo.value, absinfo.minimum, absinfo.maximum,
slouken@8053
   512
                       absinfo.fuzz, absinfo.flat);
slouken@8053
   513
#endif /* DEBUG_INPUT_EVENTS */
slouken@1895
   514
                ++joystick->nhats;
slouken@1895
   515
            }
slouken@1895
   516
        }
slouken@1895
   517
        if (test_bit(REL_X, relbit) || test_bit(REL_Y, relbit)) {
slouken@1895
   518
            ++joystick->nballs;
slouken@1895
   519
        }
slouken@0
   520
slouken@1895
   521
        /* Allocate data to keep track of these thingamajigs */
slouken@1895
   522
        if (joystick->nhats > 0) {
slouken@1895
   523
            if (allocate_hatdata(joystick) < 0) {
slouken@1895
   524
                joystick->nhats = 0;
slouken@1895
   525
            }
slouken@1895
   526
        }
slouken@1895
   527
        if (joystick->nballs > 0) {
slouken@1895
   528
            if (allocate_balldata(joystick) < 0) {
slouken@1895
   529
                joystick->nballs = 0;
slouken@1895
   530
            }
slouken@1895
   531
        }
slouken@1895
   532
    }
slouken@0
   533
}
slouken@0
   534
slouken@892
   535
slouken@0
   536
/* Function to open a joystick for use.
philipp@9380
   537
   The joystick to open is specified by the device index.
slouken@0
   538
   This should fill the nbuttons and naxes fields of the joystick structure.
slouken@0
   539
   It returns 0, or -1 if there is an error.
slouken@0
   540
 */
slouken@1895
   541
int
slouken@6690
   542
SDL_SYS_JoystickOpen(SDL_Joystick * joystick, int device_index)
slouken@0
   543
{
icculus@6734
   544
    SDL_joylist_item *item = JoystickByDevIndex(device_index);
icculus@6734
   545
    char *fname = NULL;
icculus@6734
   546
    int fd = -1;
slouken@892
   547
icculus@6734
   548
    if (item == NULL) {
icculus@7037
   549
        return SDL_SetError("No such device");
icculus@6734
   550
    }
slouken@892
   551
icculus@6734
   552
    fname = item->path;
icculus@6734
   553
    fd = open(fname, O_RDONLY, 0);
slouken@1895
   554
    if (fd < 0) {
icculus@7037
   555
        return SDL_SetError("Unable to open %s", fname);
slouken@1895
   556
    }
icculus@6734
   557
icculus@6751
   558
    joystick->instance_id = item->device_instance;
slouken@1895
   559
    joystick->hwdata = (struct joystick_hwdata *)
slouken@1895
   560
        SDL_malloc(sizeof(*joystick->hwdata));
slouken@1895
   561
    if (joystick->hwdata == NULL) {
icculus@6734
   562
        close(fd);
icculus@7037
   563
        return SDL_OutOfMemory();
slouken@1895
   564
    }
slouken@1895
   565
    SDL_memset(joystick->hwdata, 0, sizeof(*joystick->hwdata));
icculus@6752
   566
    joystick->hwdata->item = item;
icculus@6734
   567
    joystick->hwdata->guid = item->guid;
slouken@1895
   568
    joystick->hwdata->fd = fd;
icculus@6734
   569
    joystick->hwdata->fname = SDL_strdup(item->path);
icculus@6734
   570
    if (joystick->hwdata->fname == NULL) {
icculus@6734
   571
        SDL_free(joystick->hwdata);
icculus@6734
   572
        joystick->hwdata = NULL;
icculus@6734
   573
        close(fd);
icculus@7037
   574
        return SDL_OutOfMemory();
icculus@6734
   575
    }
icculus@6734
   576
icculus@6734
   577
    SDL_assert(item->hwdata == NULL);
icculus@6734
   578
    item->hwdata = joystick->hwdata;
slouken@0
   579
slouken@1895
   580
    /* Set the joystick to non-blocking read mode */
slouken@1895
   581
    fcntl(fd, F_SETFL, O_NONBLOCK);
slouken@0
   582
slouken@1895
   583
    /* Get the number of buttons and axes on the joystick */
icculus@6729
   584
    ConfigJoystick(joystick, fd);
slouken@554
   585
slouken@7191
   586
    /* mark joystick as fresh and ready */
slouken@6844
   587
    joystick->hwdata->fresh = 1;
slouken@6844
   588
slouken@1895
   589
    return (0);
slouken@0
   590
}
slouken@0
   591
philipp@9561
   592
/* Function to determine if this joystick is attached to the system right now */
slouken@6707
   593
SDL_bool SDL_SYS_JoystickAttached(SDL_Joystick *joystick)
slouken@6707
   594
{
icculus@9433
   595
    return joystick->hwdata->item != NULL;
slouken@6707
   596
}
slouken@6707
   597
slouken@7860
   598
static SDL_INLINE void
slouken@1895
   599
HandleHat(SDL_Joystick * stick, Uint8 hat, int axis, int value)
slouken@0
   600
{
slouken@1895
   601
    struct hwdata_hat *the_hat;
slouken@1895
   602
    const Uint8 position_map[3][3] = {
slouken@1895
   603
        {SDL_HAT_LEFTUP, SDL_HAT_UP, SDL_HAT_RIGHTUP},
slouken@1895
   604
        {SDL_HAT_LEFT, SDL_HAT_CENTERED, SDL_HAT_RIGHT},
slouken@1895
   605
        {SDL_HAT_LEFTDOWN, SDL_HAT_DOWN, SDL_HAT_RIGHTDOWN}
slouken@1895
   606
    };
slouken@0
   607
slouken@1895
   608
    the_hat = &stick->hwdata->hats[hat];
slouken@1895
   609
    if (value < 0) {
slouken@1895
   610
        value = 0;
slouken@1895
   611
    } else if (value == 0) {
slouken@1895
   612
        value = 1;
slouken@1895
   613
    } else if (value > 0) {
slouken@1895
   614
        value = 2;
slouken@1895
   615
    }
slouken@1895
   616
    if (value != the_hat->axis[axis]) {
slouken@1895
   617
        the_hat->axis[axis] = value;
slouken@1895
   618
        SDL_PrivateJoystickHat(stick, hat,
slouken@3013
   619
                               position_map[the_hat->
slouken@3013
   620
                                            axis[1]][the_hat->axis[0]]);
slouken@1895
   621
    }
slouken@0
   622
}
slouken@0
   623
slouken@7860
   624
static SDL_INLINE void
slouken@1895
   625
HandleBall(SDL_Joystick * stick, Uint8 ball, int axis, int value)
slouken@0
   626
{
slouken@1895
   627
    stick->hwdata->balls[ball].axis[axis] += value;
slouken@0
   628
}
slouken@0
   629
slouken@0
   630
slouken@7860
   631
static SDL_INLINE int
icculus@6729
   632
AxisCorrect(SDL_Joystick * joystick, int which, int value)
slouken@0
   633
{
slouken@1895
   634
    struct axis_correct *correct;
slouken@0
   635
slouken@1895
   636
    correct = &joystick->hwdata->abs_correct[which];
slouken@1895
   637
    if (correct->used) {
slouken@6845
   638
        value *= 2;
slouken@1895
   639
        if (value > correct->coef[0]) {
slouken@1895
   640
            if (value < correct->coef[1]) {
slouken@1895
   641
                return 0;
slouken@1895
   642
            }
slouken@1895
   643
            value -= correct->coef[1];
slouken@1895
   644
        } else {
slouken@1895
   645
            value -= correct->coef[0];
slouken@1895
   646
        }
slouken@1895
   647
        value *= correct->coef[2];
slouken@6845
   648
        value >>= 13;
slouken@1895
   649
    }
slouken@554
   650
slouken@1895
   651
    /* Clamp and return */
slouken@1895
   652
    if (value < -32768)
slouken@1895
   653
        return -32768;
slouken@1895
   654
    if (value > 32767)
slouken@1895
   655
        return 32767;
slouken@554
   656
slouken@1895
   657
    return value;
slouken@0
   658
}
slouken@0
   659
slouken@7860
   660
static SDL_INLINE void
slouken@6844
   661
PollAllValues(SDL_Joystick * joystick)
slouken@6844
   662
{
slouken@6844
   663
    struct input_absinfo absinfo;
slouken@6844
   664
    int a, b = 0;
slouken@6844
   665
slouken@7191
   666
    /* Poll all axis */
slouken@6844
   667
    for (a = ABS_X; b < ABS_MAX; a++) {
slouken@6844
   668
        switch (a) {
slouken@6844
   669
        case ABS_HAT0X:
slouken@6844
   670
        case ABS_HAT0Y:
slouken@6844
   671
        case ABS_HAT1X:
slouken@6844
   672
        case ABS_HAT1Y:
slouken@6844
   673
        case ABS_HAT2X:
slouken@6844
   674
        case ABS_HAT2Y:
slouken@6844
   675
        case ABS_HAT3X:
slouken@6844
   676
        case ABS_HAT3Y:
slouken@7191
   677
            /* ingore hats */
slouken@6844
   678
            break;
slouken@6844
   679
        default:
slouken@6844
   680
            if (joystick->hwdata->abs_correct[b].used) {
slouken@6844
   681
                if (ioctl(joystick->hwdata->fd, EVIOCGABS(a), &absinfo) >= 0) {
slouken@6844
   682
                    absinfo.value = AxisCorrect(joystick, b, absinfo.value);
slouken@6844
   683
slouken@6844
   684
#ifdef DEBUG_INPUT_EVENTS
slouken@6844
   685
                    printf("Joystick : Re-read Axis %d (%d) val= %d\n",
slouken@6844
   686
                        joystick->hwdata->abs_map[b], a, absinfo.value);
slouken@6844
   687
#endif
slouken@6844
   688
                    SDL_PrivateJoystickAxis(joystick,
slouken@6844
   689
                            joystick->hwdata->abs_map[b],
slouken@6844
   690
                            absinfo.value);
slouken@6844
   691
                }
slouken@6844
   692
            }
slouken@6844
   693
            b++;
slouken@6844
   694
        }
slouken@6844
   695
    }
slouken@6844
   696
}
slouken@6844
   697
slouken@7860
   698
static SDL_INLINE void
icculus@6729
   699
HandleInputEvents(SDL_Joystick * joystick)
slouken@0
   700
{
slouken@1895
   701
    struct input_event events[32];
slouken@1895
   702
    int i, len;
slouken@1895
   703
    int code;
slouken@0
   704
slouken@6844
   705
    if (joystick->hwdata->fresh) {
slouken@6844
   706
        PollAllValues(joystick);
slouken@6844
   707
        joystick->hwdata->fresh = 0;
slouken@6844
   708
    }
slouken@6844
   709
slouken@1895
   710
    while ((len = read(joystick->hwdata->fd, events, (sizeof events))) > 0) {
slouken@1895
   711
        len /= sizeof(events[0]);
slouken@1895
   712
        for (i = 0; i < len; ++i) {
slouken@1895
   713
            code = events[i].code;
slouken@1895
   714
            switch (events[i].type) {
slouken@1895
   715
            case EV_KEY:
slouken@1895
   716
                if (code >= BTN_MISC) {
slouken@1895
   717
                    code -= BTN_MISC;
icculus@6728
   718
                    SDL_PrivateJoystickButton(joystick,
icculus@6728
   719
                                              joystick->hwdata->key_map[code],
icculus@6728
   720
                                              events[i].value);
slouken@1895
   721
                }
slouken@1895
   722
                break;
slouken@1895
   723
            case EV_ABS:
slouken@1895
   724
                switch (code) {
slouken@1895
   725
                case ABS_HAT0X:
slouken@1895
   726
                case ABS_HAT0Y:
slouken@1895
   727
                case ABS_HAT1X:
slouken@1895
   728
                case ABS_HAT1Y:
slouken@1895
   729
                case ABS_HAT2X:
slouken@1895
   730
                case ABS_HAT2Y:
slouken@1895
   731
                case ABS_HAT3X:
slouken@1895
   732
                case ABS_HAT3Y:
slouken@1895
   733
                    code -= ABS_HAT0X;
slouken@1895
   734
                    HandleHat(joystick, code / 2, code % 2, events[i].value);
slouken@1895
   735
                    break;
slouken@1895
   736
                default:
slouken@1895
   737
                    events[i].value =
icculus@6729
   738
                        AxisCorrect(joystick, code, events[i].value);
icculus@6728
   739
                    SDL_PrivateJoystickAxis(joystick,
icculus@6728
   740
                                            joystick->hwdata->abs_map[code],
icculus@6728
   741
                                            events[i].value);
slouken@1895
   742
                    break;
slouken@1895
   743
                }
slouken@1895
   744
                break;
slouken@1895
   745
            case EV_REL:
slouken@1895
   746
                switch (code) {
slouken@1895
   747
                case REL_X:
slouken@1895
   748
                case REL_Y:
slouken@1895
   749
                    code -= REL_X;
slouken@1895
   750
                    HandleBall(joystick, code / 2, code % 2, events[i].value);
slouken@1895
   751
                    break;
slouken@1895
   752
                default:
slouken@1895
   753
                    break;
slouken@1895
   754
                }
slouken@1895
   755
                break;
slouken@6844
   756
            case EV_SYN:
slouken@6844
   757
                switch (code) {
slouken@6844
   758
                case SYN_DROPPED :
slouken@6844
   759
#ifdef DEBUG_INPUT_EVENTS
philipp@7133
   760
                    printf("Event SYN_DROPPED detected\n");
slouken@6844
   761
#endif
slouken@6844
   762
                    PollAllValues(joystick);
slouken@6844
   763
                    break;
slouken@6844
   764
                default:
slouken@6844
   765
                    break;
slouken@6844
   766
                }
slouken@1895
   767
            default:
slouken@1895
   768
                break;
slouken@1895
   769
            }
slouken@1895
   770
        }
slouken@1895
   771
    }
slouken@0
   772
}
slouken@0
   773
slouken@1895
   774
void
slouken@1895
   775
SDL_SYS_JoystickUpdate(SDL_Joystick * joystick)
slouken@0
   776
{
slouken@1895
   777
    int i;
slouken@1895
   778
icculus@6729
   779
    HandleInputEvents(joystick);
slouken@0
   780
slouken@1895
   781
    /* Deliver ball motion updates */
slouken@1895
   782
    for (i = 0; i < joystick->nballs; ++i) {
slouken@1895
   783
        int xrel, yrel;
slouken@0
   784
slouken@1895
   785
        xrel = joystick->hwdata->balls[i].axis[0];
slouken@1895
   786
        yrel = joystick->hwdata->balls[i].axis[1];
slouken@1895
   787
        if (xrel || yrel) {
slouken@1895
   788
            joystick->hwdata->balls[i].axis[0] = 0;
slouken@1895
   789
            joystick->hwdata->balls[i].axis[1] = 0;
slouken@1895
   790
            SDL_PrivateJoystickBall(joystick, (Uint8) i, xrel, yrel);
slouken@1895
   791
        }
slouken@1895
   792
    }
slouken@0
   793
}
slouken@0
   794
slouken@0
   795
/* Function to close a joystick after use */
slouken@1895
   796
void
slouken@1895
   797
SDL_SYS_JoystickClose(SDL_Joystick * joystick)
slouken@0
   798
{
slouken@1895
   799
    if (joystick->hwdata) {
icculus@6728
   800
        close(joystick->hwdata->fd);
icculus@6752
   801
        if (joystick->hwdata->item) {
icculus@6752
   802
            joystick->hwdata->item->hwdata = NULL;
icculus@6752
   803
        }
icculus@6734
   804
        SDL_free(joystick->hwdata->hats);
icculus@6734
   805
        SDL_free(joystick->hwdata->balls);
icculus@6734
   806
        SDL_free(joystick->hwdata->fname);
slouken@1895
   807
        SDL_free(joystick->hwdata);
slouken@1895
   808
    }
slouken@0
   809
}
slouken@0
   810
slouken@0
   811
/* Function to perform any system-specific joystick related cleanup */
slouken@1895
   812
void
slouken@1895
   813
SDL_SYS_JoystickQuit(void)
slouken@0
   814
{
icculus@6734
   815
    SDL_joylist_item *item = NULL;
icculus@6734
   816
    SDL_joylist_item *next = NULL;
icculus@6734
   817
icculus@6734
   818
    for (item = SDL_joylist; item; item = next) {
icculus@6734
   819
        next = item->next;
icculus@6734
   820
        SDL_free(item->path);
icculus@6734
   821
        SDL_free(item->name);
icculus@6734
   822
        SDL_free(item);
icculus@6734
   823
    }
icculus@6734
   824
icculus@6734
   825
    SDL_joylist = SDL_joylist_tail = NULL;
slouken@0
   826
icculus@6734
   827
    numjoysticks = 0;
icculus@6734
   828
    instance_counter = 0;
icculus@6734
   829
icculus@6734
   830
#if SDL_USE_LIBUDEV
gabomdq@7772
   831
    SDL_UDEV_DelCallback(joystick_udev_callback);
gabomdq@7772
   832
    SDL_UDEV_Quit();
icculus@6734
   833
#endif
slouken@0
   834
}
slouken@0
   835
slouken@6738
   836
SDL_JoystickGUID SDL_SYS_JoystickGetDeviceGUID( int device_index )
slouken@6690
   837
{
icculus@6734
   838
    return JoystickByDevIndex(device_index)->guid;
slouken@6690
   839
}
slouken@6690
   840
slouken@6738
   841
SDL_JoystickGUID SDL_SYS_JoystickGetGUID(SDL_Joystick * joystick)
slouken@6690
   842
{
icculus@6734
   843
    return joystick->hwdata->guid;
slouken@6690
   844
}
slouken@6690
   845
slouken@1635
   846
#endif /* SDL_JOYSTICK_LINUX */
slouken@6693
   847
slouken@1895
   848
/* vi: set ts=4 sw=4 expandtab: */