src/joystick/linux/SDL_sysjoystick.c
author Ryan C. Gordon <icculus@icculus.org>
Tue, 26 May 2015 12:03:51 -0400
changeset 9630 6c86d3216f6a
parent 9619 b94b6d0bff0f
child 9998 f67cf37e9cd4
permissions -rw-r--r--
Linux joystick: Look at entire axis namespace for controls (thanks, "spaz16"!).

This apparently has fallout: the PS4 (and maybe PS3?) controllers apparently
report some bogus axes, but it won't change the axes we currently expect, and
thus the game controller config string is still stable.

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