src/joystick/linux/SDL_sysjoystick.c
author Sam Lantinga <slouken@libsdl.org>
Thu, 16 Jan 2020 20:49:25 -0800
changeset 13422 fd6a12de91c7
parent 13406 b2a308f6c7eb
child 13442 d885f6cfea3b
permissions -rw-r--r--
Updated copyright date for 2020
slouken@0
     1
/*
slouken@5535
     2
  Simple DirectMedia Layer
slouken@13422
     3
  Copyright (C) 1997-2020 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@12088
    32
#include <errno.h>              /* errno, strerror */
slouken@0
    33
#include <fcntl.h>
slouken@12088
    34
#include <limits.h>             /* For the definition of PATH_MAX */
slouken@0
    35
#include <sys/ioctl.h>
slouken@12088
    36
#include <unistd.h>
cameron@12944
    37
#include <dirent.h>
slouken@0
    38
#include <linux/joystick.h>
slouken@0
    39
icculus@6734
    40
#include "SDL_assert.h"
slouken@0
    41
#include "SDL_joystick.h"
icculus@6734
    42
#include "SDL_endian.h"
cameron@12944
    43
#include "SDL_timer.h"
slouken@11532
    44
#include "../../events/SDL_events_c.h"
slouken@1361
    45
#include "../SDL_sysjoystick.h"
slouken@1361
    46
#include "../SDL_joystick_c.h"
slouken@11532
    47
#include "../steam/SDL_steamcontroller.h"
slouken@2713
    48
#include "SDL_sysjoystick_c.h"
slouken@12088
    49
#include "../hidapi/SDL_hidapijoystick_c.h"
slouken@0
    50
slouken@6910
    51
/* This isn't defined in older Linux kernel headers */
slouken@6910
    52
#ifndef SYN_DROPPED
slouken@6910
    53
#define SYN_DROPPED 3
slouken@6910
    54
#endif
slouken@6910
    55
gabomdq@7772
    56
#include "../../core/linux/SDL_udev.h"
icculus@6734
    57
gabomdq@7772
    58
static int MaybeAddDevice(const char *path);
gabomdq@7772
    59
#if SDL_USE_LIBUDEV
gabomdq@7772
    60
static int MaybeRemoveDevice(const char *path);
gabomdq@7772
    61
#endif /* SDL_USE_LIBUDEV */
slouken@892
    62
icculus@6734
    63
/* A linked list of available joysticks */
icculus@6734
    64
typedef struct SDL_joylist_item
icculus@6734
    65
{
icculus@6734
    66
    int device_instance;
icculus@6734
    67
    char *path;   /* "/dev/input/event2" or whatever */
icculus@6734
    68
    char *name;   /* "SideWinder 3D Pro" or whatever */
slouken@6743
    69
    SDL_JoystickGUID guid;
icculus@6734
    70
    dev_t devnum;
icculus@6734
    71
    struct joystick_hwdata *hwdata;
icculus@6734
    72
    struct SDL_joylist_item *next;
slouken@11532
    73
slouken@11532
    74
    /* Steam Controller support */
slouken@11532
    75
    SDL_bool m_bSteamController;
icculus@6734
    76
} SDL_joylist_item;
icculus@6734
    77
icculus@6734
    78
static SDL_joylist_item *SDL_joylist = NULL;
icculus@6734
    79
static SDL_joylist_item *SDL_joylist_tail = NULL;
icculus@6734
    80
static int numjoysticks = 0;
icculus@6734
    81
cameron@12944
    82
#if !SDL_USE_LIBUDEV
cameron@13310
    83
static Uint32 last_joy_detect_time;
cameron@13310
    84
static time_t last_input_dir_mtime;
cameron@12944
    85
#endif
slouken@11532
    86
slouken@0
    87
#define test_bit(nr, addr) \
icculus@6734
    88
    (((1UL << ((nr) % (sizeof(long) * 8))) & ((addr)[(nr) / (sizeof(long) * 8)])) != 0)
slouken@3404
    89
#define NBITS(x) ((((x)-1)/(sizeof(long) * 8))+1)
slouken@0
    90
slouken@1895
    91
static int
slouken@13327
    92
PrefixMatch(const char *a, const char *b)
slouken@13327
    93
{
slouken@13327
    94
    int matchlen = 0;
slouken@13327
    95
    while (*a && *b) {
slouken@13327
    96
        if (*a++ == *b++) {
slouken@13327
    97
            ++matchlen;
slouken@13327
    98
        } else {
slouken@13327
    99
            break;
slouken@13327
   100
        }
slouken@13327
   101
    }
slouken@13327
   102
    return matchlen;
slouken@13327
   103
}
slouken@13327
   104
slouken@13327
   105
static int
slouken@6743
   106
IsJoystick(int fd, char *namebuf, const size_t namebuflen, SDL_JoystickGUID *guid)
slouken@0
   107
{
gabomdq@7909
   108
    struct input_id inpid;
slouken@10595
   109
    Uint16 *guid16 = (Uint16 *)guid->data;
slouken@13333
   110
    const char *name;
slouken@13327
   111
    const char *spot;
gabomdq@7909
   112
gabomdq@7909
   113
#if !SDL_USE_LIBUDEV
gabomdq@7909
   114
    /* When udev is enabled we only get joystick devices here, so there's no need to test them */
slouken@3404
   115
    unsigned long evbit[NBITS(EV_MAX)] = { 0 };
slouken@3404
   116
    unsigned long keybit[NBITS(KEY_MAX)] = { 0 };
slouken@3404
   117
    unsigned long absbit[NBITS(ABS_MAX)] = { 0 };
slouken@0
   118
slouken@1895
   119
    if ((ioctl(fd, EVIOCGBIT(0, sizeof(evbit)), evbit) < 0) ||
slouken@1895
   120
        (ioctl(fd, EVIOCGBIT(EV_KEY, sizeof(keybit)), keybit) < 0) ||
slouken@1895
   121
        (ioctl(fd, EVIOCGBIT(EV_ABS, sizeof(absbit)), absbit) < 0)) {
slouken@1895
   122
        return (0);
slouken@1895
   123
    }
bobbens@3079
   124
slouken@1895
   125
    if (!(test_bit(EV_KEY, evbit) && test_bit(EV_ABS, evbit) &&
bobbens@3079
   126
          test_bit(ABS_X, absbit) && test_bit(ABS_Y, absbit))) {
slouken@1895
   127
        return 0;
bobbens@3079
   128
    }
gabomdq@7909
   129
#endif
icculus@6734
   130
slouken@13333
   131
    if (ioctl(fd, EVIOCGID, &inpid) < 0) {
icculus@6734
   132
        return 0;
icculus@6734
   133
    }
icculus@6734
   134
slouken@13333
   135
    name = SDL_GetCustomJoystickName(inpid.vendor, inpid.product);
slouken@13333
   136
    if (name) {
slouken@13333
   137
        SDL_strlcpy(namebuf, name, namebuflen);
slouken@13333
   138
    } else {
slouken@13333
   139
        if (ioctl(fd, EVIOCGNAME(namebuflen), namebuf) < 0) {
slouken@13333
   140
            return 0;
slouken@13327
   141
        }
slouken@13327
   142
slouken@13333
   143
        /* Remove duplicate manufacturer in the name */
slouken@13333
   144
        for (spot = namebuf + 1; *spot; ++spot) {
slouken@13333
   145
            int matchlen = PrefixMatch(namebuf, spot);
slouken@13333
   146
            if (matchlen > 0 && spot[matchlen - 1] == ' ') {
slouken@13333
   147
                SDL_memmove(namebuf, spot, SDL_strlen(spot)+1);
slouken@13333
   148
                break;
slouken@13333
   149
            }
slouken@13333
   150
        }
icculus@6734
   151
    }
icculus@6734
   152
slouken@12088
   153
#ifdef SDL_JOYSTICK_HIDAPI
slouken@13160
   154
    if (HIDAPI_IsDevicePresent(inpid.vendor, inpid.product, inpid.version, namebuf)) {
slouken@12088
   155
        /* The HIDAPI driver is taking care of this device */
slouken@12088
   156
        return 0;
slouken@12088
   157
    }
slouken@12088
   158
#endif
slouken@12088
   159
slouken@6831
   160
#ifdef DEBUG_JOYSTICK
slouken@10595
   161
    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
   162
#endif
slouken@6831
   163
slouken@6831
   164
    SDL_memset(guid->data, 0, sizeof(guid->data));
slouken@6831
   165
icculus@6734
   166
    /* We only need 16 bits for each of these; space them out to fill 128. */
icculus@6734
   167
    /* Byteswap so devices get same GUID on little/big endian platforms. */
slouken@10595
   168
    *guid16++ = SDL_SwapLE16(inpid.bustype);
slouken@10595
   169
    *guid16++ = 0;
slouken@6831
   170
slouken@10595
   171
    if (inpid.vendor && inpid.product) {
slouken@10595
   172
        *guid16++ = SDL_SwapLE16(inpid.vendor);
slouken@10595
   173
        *guid16++ = 0;
slouken@10595
   174
        *guid16++ = SDL_SwapLE16(inpid.product);
slouken@10595
   175
        *guid16++ = 0;
slouken@10595
   176
        *guid16++ = SDL_SwapLE16(inpid.version);
slouken@10595
   177
        *guid16++ = 0;
slouken@6831
   178
    } else {
slouken@6831
   179
        SDL_strlcpy((char*)guid16, namebuf, sizeof(guid->data) - 4);
slouken@6831
   180
    }
icculus@6734
   181
slouken@12088
   182
    if (SDL_ShouldIgnoreJoystick(namebuf, *guid)) {
slouken@11201
   183
        return 0;
slouken@11201
   184
    }
icculus@6734
   185
    return 1;
slouken@0
   186
}
slouken@0
   187
gabomdq@7772
   188
#if SDL_USE_LIBUDEV
philipp@11094
   189
static void joystick_udev_callback(SDL_UDEV_deviceevent udev_type, int udev_class, const char *devpath)
gabomdq@7772
   190
{
slouken@9321
   191
    if (devpath == NULL) {
gabomdq@7772
   192
        return;
gabomdq@7772
   193
    }
slouken@9321
   194
slouken@9321
   195
    switch (udev_type) {
gabomdq@7772
   196
        case SDL_UDEV_DEVICEADDED:
slouken@9321
   197
            if (!(udev_class & SDL_UDEV_DEVICE_JOYSTICK)) {
slouken@9321
   198
                return;
slouken@9321
   199
            }
slouken@7802
   200
            MaybeAddDevice(devpath);
gabomdq@7772
   201
            break;
gabomdq@7772
   202
            
gabomdq@7772
   203
        case SDL_UDEV_DEVICEREMOVED:
slouken@7802
   204
            MaybeRemoveDevice(devpath);
gabomdq@7772
   205
            break;
gabomdq@7772
   206
            
gabomdq@7772
   207
        default:
gabomdq@7772
   208
            break;
gabomdq@7772
   209
    }
gabomdq@7772
   210
    
gabomdq@7772
   211
}
gabomdq@7772
   212
#endif /* SDL_USE_LIBUDEV */
gabomdq@7772
   213
icculus@6734
   214
static int
icculus@6734
   215
MaybeAddDevice(const char *path)
icculus@6734
   216
{
icculus@6734
   217
    struct stat sb;
icculus@6734
   218
    int fd = -1;
icculus@6734
   219
    int isstick = 0;
icculus@6734
   220
    char namebuf[128];
slouken@6743
   221
    SDL_JoystickGUID guid;
icculus@6734
   222
    SDL_joylist_item *item;
icculus@6734
   223
icculus@6734
   224
    if (path == NULL) {
icculus@6734
   225
        return -1;
icculus@6734
   226
    }
icculus@6734
   227
icculus@6734
   228
    if (stat(path, &sb) == -1) {
icculus@6734
   229
        return -1;
icculus@6734
   230
    }
icculus@6734
   231
icculus@6734
   232
    /* Check to make sure it's not already in list. */
icculus@6734
   233
    for (item = SDL_joylist; item != NULL; item = item->next) {
icculus@6734
   234
        if (sb.st_rdev == item->devnum) {
icculus@6734
   235
            return -1;  /* already have this one */
icculus@6734
   236
        }
icculus@6734
   237
    }
icculus@6734
   238
icculus@6734
   239
    fd = open(path, O_RDONLY, 0);
icculus@6734
   240
    if (fd < 0) {
icculus@6734
   241
        return -1;
icculus@6734
   242
    }
icculus@6734
   243
icculus@6734
   244
#ifdef DEBUG_INPUT_EVENTS
icculus@6734
   245
    printf("Checking %s\n", path);
icculus@6734
   246
#endif
icculus@6734
   247
icculus@6734
   248
    isstick = IsJoystick(fd, namebuf, sizeof (namebuf), &guid);
icculus@6734
   249
    close(fd);
icculus@6734
   250
    if (!isstick) {
icculus@6734
   251
        return -1;
icculus@6734
   252
    }
icculus@6734
   253
icculus@6734
   254
    item = (SDL_joylist_item *) SDL_malloc(sizeof (SDL_joylist_item));
icculus@6734
   255
    if (item == NULL) {
icculus@6734
   256
        return -1;
icculus@6734
   257
    }
icculus@6734
   258
icculus@6734
   259
    SDL_zerop(item);
icculus@6734
   260
    item->devnum = sb.st_rdev;
icculus@6734
   261
    item->path = SDL_strdup(path);
icculus@6734
   262
    item->name = SDL_strdup(namebuf);
icculus@6734
   263
    item->guid = guid;
icculus@6734
   264
slouken@12088
   265
    if ((item->path == NULL) || (item->name == NULL)) {
icculus@6734
   266
         SDL_free(item->path);
icculus@6734
   267
         SDL_free(item->name);
icculus@6734
   268
         SDL_free(item);
icculus@6734
   269
         return -1;
icculus@6734
   270
    }
icculus@6734
   271
slouken@12088
   272
    item->device_instance = SDL_GetNextJoystickInstanceID();
icculus@6734
   273
    if (SDL_joylist_tail == NULL) {
icculus@6734
   274
        SDL_joylist = SDL_joylist_tail = item;
icculus@6734
   275
    } else {
icculus@6734
   276
        SDL_joylist_tail->next = item;
icculus@6734
   277
        SDL_joylist_tail = item;
icculus@6734
   278
    }
icculus@6734
   279
slouken@7916
   280
    /* Need to increment the joystick count before we post the event */
slouken@7916
   281
    ++numjoysticks;
slouken@7916
   282
slouken@12088
   283
    SDL_PrivateJoystickAdded(item->device_instance);
slouken@7802
   284
slouken@7916
   285
    return numjoysticks;
icculus@6734
   286
}
icculus@6734
   287
icculus@6749
   288
#if SDL_USE_LIBUDEV
icculus@6734
   289
static int
icculus@6734
   290
MaybeRemoveDevice(const char *path)
icculus@6734
   291
{
icculus@6734
   292
    SDL_joylist_item *item;
icculus@6734
   293
    SDL_joylist_item *prev = NULL;
icculus@6734
   294
icculus@6734
   295
    if (path == NULL) {
icculus@6734
   296
        return -1;
icculus@6734
   297
    }
slouken@6690
   298
icculus@6734
   299
    for (item = SDL_joylist; item != NULL; item = item->next) {
icculus@6734
   300
        /* found it, remove it. */
icculus@6734
   301
        if (SDL_strcmp(path, item->path) == 0) {
icculus@6734
   302
            const int retval = item->device_instance;
icculus@6734
   303
            if (item->hwdata) {
icculus@6752
   304
                item->hwdata->item = NULL;
icculus@6734
   305
            }
icculus@6734
   306
            if (prev != NULL) {
icculus@6734
   307
                prev->next = item->next;
icculus@6734
   308
            } else {
jorgen@6865
   309
                SDL_assert(SDL_joylist == item);
jorgen@6865
   310
                SDL_joylist = item->next;
jorgen@6865
   311
            }
jorgen@6865
   312
            if (item == SDL_joylist_tail) {
jorgen@6865
   313
                SDL_joylist_tail = prev;
icculus@6734
   314
            }
slouken@7802
   315
slouken@7916
   316
            /* Need to decrement the joystick count before we post the event */
slouken@7916
   317
            --numjoysticks;
slouken@7916
   318
slouken@10226
   319
            SDL_PrivateJoystickRemoved(item->device_instance);
slouken@7802
   320
icculus@6734
   321
            SDL_free(item->path);
icculus@6734
   322
            SDL_free(item->name);
icculus@6734
   323
            SDL_free(item);
icculus@6734
   324
            return retval;
icculus@6734
   325
        }
icculus@6734
   326
        prev = item;
icculus@6734
   327
    }
icculus@6734
   328
icculus@6734
   329
    return -1;
icculus@6734
   330
}
icculus@6749
   331
#endif
icculus@6734
   332
cameron@12944
   333
static void
cameron@12944
   334
HandlePendingRemovals(void)
icculus@6734
   335
{
cameron@12944
   336
    SDL_joylist_item *prev = NULL;
cameron@12944
   337
    SDL_joylist_item *item = SDL_joylist;
cameron@12944
   338
cameron@12944
   339
    while (item != NULL) {
cameron@12944
   340
        if (item->hwdata && item->hwdata->gone) {
cameron@12944
   341
            item->hwdata->item = NULL;
icculus@6734
   342
cameron@12944
   343
            if (prev != NULL) {
cameron@12944
   344
                prev->next = item->next;
cameron@12944
   345
            } else {
cameron@12944
   346
                SDL_assert(SDL_joylist == item);
cameron@12944
   347
                SDL_joylist = item->next;
cameron@12944
   348
            }
cameron@12944
   349
            if (item == SDL_joylist_tail) {
cameron@12944
   350
                SDL_joylist_tail = prev;
cameron@12944
   351
            }
icculus@6734
   352
cameron@12944
   353
            /* Need to decrement the joystick count before we post the event */
cameron@12944
   354
            --numjoysticks;
cameron@12944
   355
cameron@12944
   356
            SDL_PrivateJoystickRemoved(item->device_instance);
cameron@12944
   357
cameron@12944
   358
            SDL_free(item->path);
cameron@12944
   359
            SDL_free(item->name);
cameron@12944
   360
            SDL_free(item);
icculus@6734
   361
cameron@12944
   362
            if (prev != NULL) {
cameron@12944
   363
                item = prev->next;
cameron@12944
   364
            } else {
cameron@12944
   365
                item = SDL_joylist;
cameron@12944
   366
            }
cameron@12944
   367
        } else {
cameron@12944
   368
            prev = item;
cameron@12944
   369
            item = item->next;
cameron@12944
   370
        }
icculus@6734
   371
    }
icculus@6734
   372
}
icculus@6734
   373
slouken@11532
   374
static SDL_bool SteamControllerConnectedCallback(const char *name, SDL_JoystickGUID guid, int *device_instance)
slouken@11532
   375
{
slouken@11532
   376
    SDL_joylist_item *item;
slouken@11532
   377
slouken@11532
   378
    item = (SDL_joylist_item *) SDL_calloc(1, sizeof (SDL_joylist_item));
slouken@11532
   379
    if (item == NULL) {
slouken@11532
   380
        return SDL_FALSE;
slouken@11532
   381
    }
slouken@11532
   382
slouken@11532
   383
    item->path = SDL_strdup("");
slouken@11532
   384
    item->name = SDL_strdup(name);
slouken@11532
   385
    item->guid = guid;
slouken@11532
   386
    item->m_bSteamController = SDL_TRUE;
slouken@11532
   387
slouken@11532
   388
    if ((item->path == NULL) || (item->name == NULL)) {
slouken@11532
   389
         SDL_free(item->path);
slouken@11532
   390
         SDL_free(item->name);
slouken@11532
   391
         SDL_free(item);
slouken@11532
   392
         return SDL_FALSE;
slouken@11532
   393
    }
slouken@11532
   394
slouken@12088
   395
    *device_instance = item->device_instance = SDL_GetNextJoystickInstanceID();
slouken@11532
   396
    if (SDL_joylist_tail == NULL) {
slouken@11532
   397
        SDL_joylist = SDL_joylist_tail = item;
slouken@11532
   398
    } else {
slouken@11532
   399
        SDL_joylist_tail->next = item;
slouken@11532
   400
        SDL_joylist_tail = item;
slouken@11532
   401
    }
slouken@11532
   402
slouken@11532
   403
    /* Need to increment the joystick count before we post the event */
slouken@11532
   404
    ++numjoysticks;
slouken@11532
   405
slouken@12088
   406
    SDL_PrivateJoystickAdded(item->device_instance);
slouken@11532
   407
slouken@11532
   408
    return SDL_TRUE;
slouken@11532
   409
}
slouken@11532
   410
slouken@11532
   411
static void SteamControllerDisconnectedCallback(int device_instance)
slouken@11532
   412
{
slouken@11532
   413
    SDL_joylist_item *item;
slouken@11532
   414
    SDL_joylist_item *prev = NULL;
slouken@11532
   415
slouken@11532
   416
    for (item = SDL_joylist; item != NULL; item = item->next) {
slouken@11532
   417
        /* found it, remove it. */
slouken@11532
   418
        if (item->device_instance == device_instance) {
slouken@11532
   419
            if (item->hwdata) {
slouken@11532
   420
                item->hwdata->item = NULL;
slouken@11532
   421
            }
slouken@11532
   422
            if (prev != NULL) {
slouken@11532
   423
                prev->next = item->next;
slouken@11532
   424
            } else {
slouken@11532
   425
                SDL_assert(SDL_joylist == item);
slouken@11532
   426
                SDL_joylist = item->next;
slouken@11532
   427
            }
slouken@11532
   428
            if (item == SDL_joylist_tail) {
slouken@11532
   429
                SDL_joylist_tail = prev;
slouken@11532
   430
            }
slouken@11532
   431
slouken@11532
   432
            /* Need to decrement the joystick count before we post the event */
slouken@11532
   433
            --numjoysticks;
slouken@11532
   434
slouken@11532
   435
            SDL_PrivateJoystickRemoved(item->device_instance);
slouken@11532
   436
slouken@11532
   437
            SDL_free(item->name);
slouken@11532
   438
            SDL_free(item);
slouken@11532
   439
            return;
slouken@11532
   440
        }
slouken@11532
   441
        prev = item;
slouken@11532
   442
    }
slouken@11532
   443
}
slouken@11532
   444
cameron@12944
   445
static void
cameron@12944
   446
LINUX_JoystickDetect(void)
cameron@12944
   447
{
cameron@12944
   448
#if SDL_USE_LIBUDEV
cameron@12944
   449
    SDL_UDEV_Poll();
cameron@12944
   450
#else
cameron@12944
   451
    const Uint32 SDL_JOY_DETECT_INTERVAL_MS = 3000;  /* Update every 3 seconds */
cameron@12944
   452
    Uint32 now = SDL_GetTicks();
cameron@12944
   453
cameron@12944
   454
    if (!last_joy_detect_time || SDL_TICKS_PASSED(now, last_joy_detect_time + SDL_JOY_DETECT_INTERVAL_MS)) {
cameron@13310
   455
        struct stat sb;
cameron@13310
   456
cameron@13310
   457
        /* Opening input devices can generate synchronous device I/O, so avoid it if we can */
cameron@13310
   458
        if (stat("/dev/input", &sb) == 0 && sb.st_mtime != last_input_dir_mtime) {
cameron@13310
   459
            DIR *folder;
cameron@13310
   460
            struct dirent *dent;
cameron@12944
   461
cameron@13310
   462
            folder = opendir("/dev/input");
cameron@13310
   463
            if (folder) {
cameron@13310
   464
                while ((dent = readdir(folder))) {
cameron@13310
   465
                    int len = SDL_strlen(dent->d_name);
cameron@13310
   466
                    if (len > 5 && SDL_strncmp(dent->d_name, "event", 5) == 0) {
cameron@13310
   467
                        char path[PATH_MAX];
cameron@13310
   468
                        SDL_snprintf(path, SDL_arraysize(path), "/dev/input/%s", dent->d_name);
cameron@13310
   469
                        MaybeAddDevice(path);
cameron@13310
   470
                    }
cameron@12944
   471
                }
cameron@13310
   472
cameron@13310
   473
                closedir(folder);
cameron@12944
   474
            }
cameron@12944
   475
cameron@13310
   476
            last_input_dir_mtime = sb.st_mtime;
cameron@12944
   477
        }
cameron@12944
   478
cameron@12944
   479
        last_joy_detect_time = now;
cameron@12944
   480
    }
cameron@12944
   481
#endif
cameron@12944
   482
cameron@12944
   483
    HandlePendingRemovals();
cameron@12944
   484
cameron@12944
   485
    SDL_UpdateSteamControllers();
cameron@12944
   486
}
cameron@12944
   487
slouken@12088
   488
static int
slouken@12088
   489
LINUX_JoystickInit(void)
slouken@0
   490
{
slouken@5317
   491
    /* First see if the user specified one or more joysticks to use */
slouken@1895
   492
    if (SDL_getenv("SDL_JOYSTICK_DEVICE") != NULL) {
slouken@5317
   493
        char *envcopy, *envpath, *delim;
slouken@5317
   494
        envcopy = SDL_strdup(SDL_getenv("SDL_JOYSTICK_DEVICE"));
slouken@5317
   495
        envpath = envcopy;
slouken@5317
   496
        while (envpath != NULL) {
slouken@5317
   497
            delim = SDL_strchr(envpath, ':');
slouken@5317
   498
            if (delim != NULL) {
slouken@5317
   499
                *delim++ = '\0';
slouken@5317
   500
            }
icculus@6734
   501
            MaybeAddDevice(envpath);
slouken@5317
   502
            envpath = delim;
slouken@1895
   503
        }
slouken@5317
   504
        SDL_free(envcopy);
slouken@1895
   505
    }
slouken@554
   506
slouken@11532
   507
    SDL_InitSteamControllers(SteamControllerConnectedCallback,
slouken@11532
   508
                             SteamControllerDisconnectedCallback);
slouken@11532
   509
icculus@6734
   510
#if SDL_USE_LIBUDEV
cameron@12944
   511
    if (SDL_UDEV_Init() < 0) {
cameron@12944
   512
        return SDL_SetError("Could not initialize UDEV");
cameron@12944
   513
    }
cameron@12944
   514
cameron@12944
   515
    /* Set up the udev callback */
cameron@12944
   516
    if (SDL_UDEV_AddCallback(joystick_udev_callback) < 0) {
cameron@12944
   517
        SDL_UDEV_Quit();
cameron@12944
   518
        return SDL_SetError("Could not set up joystick <-> udev callback");
cameron@12944
   519
    }
cameron@12944
   520
cameron@12944
   521
    /* Force a scan to build the initial device list */
cameron@12944
   522
    SDL_UDEV_Scan();
cameron@12944
   523
#else
cameron@13310
   524
    /* Force immediate joystick detection */
cameron@13310
   525
    last_joy_detect_time = 0;
cameron@13310
   526
    last_input_dir_mtime = 0;
cameron@13310
   527
cameron@12944
   528
    /* Report all devices currently present */
cameron@12944
   529
    LINUX_JoystickDetect();
slouken@0
   530
#endif
cameron@12944
   531
cameron@12944
   532
    return 0;
slouken@0
   533
}
slouken@0
   534
slouken@12088
   535
static int
slouken@12088
   536
LINUX_JoystickGetCount(void)
slouken@6707
   537
{
icculus@6734
   538
    return numjoysticks;
icculus@6734
   539
}
icculus@6734
   540
icculus@6734
   541
static SDL_joylist_item *
icculus@6734
   542
JoystickByDevIndex(int device_index)
icculus@6734
   543
{
icculus@6734
   544
    SDL_joylist_item *item = SDL_joylist;
icculus@6734
   545
icculus@6734
   546
    if ((device_index < 0) || (device_index >= numjoysticks)) {
icculus@6734
   547
        return NULL;
icculus@6734
   548
    }
icculus@6734
   549
icculus@6734
   550
    while (device_index > 0) {
icculus@6734
   551
        SDL_assert(item != NULL);
icculus@6734
   552
        device_index--;
icculus@6734
   553
        item = item->next;
icculus@6734
   554
    }
icculus@6734
   555
icculus@6734
   556
    return item;
slouken@6707
   557
}
slouken@6707
   558
slouken@0
   559
/* Function to get the device-dependent name of a joystick */
slouken@12088
   560
static const char *
slouken@12088
   561
LINUX_JoystickGetDeviceName(int device_index)
slouken@0
   562
{
icculus@6734
   563
    return JoystickByDevIndex(device_index)->name;
slouken@0
   564
}
slouken@0
   565
slouken@12359
   566
static int
slouken@12359
   567
LINUX_JoystickGetDevicePlayerIndex(int device_index)
slouken@12359
   568
{
slouken@12359
   569
    return -1;
slouken@12359
   570
}
slouken@12359
   571
slouken@13369
   572
static void
slouken@13369
   573
LINUX_JoystickSetDevicePlayerIndex(int device_index, int player_index)
slouken@13369
   574
{
slouken@13369
   575
}
slouken@13369
   576
slouken@12088
   577
static SDL_JoystickGUID
slouken@12088
   578
LINUX_JoystickGetDeviceGUID( int device_index )
slouken@12088
   579
{
slouken@12088
   580
    return JoystickByDevIndex(device_index)->guid;
slouken@12088
   581
}
slouken@12088
   582
slouken@6707
   583
/* Function to perform the mapping from device index to the instance id for this index */
slouken@12088
   584
static SDL_JoystickID
slouken@12088
   585
LINUX_JoystickGetDeviceInstanceID(int device_index)
slouken@6707
   586
{
icculus@6734
   587
    return JoystickByDevIndex(device_index)->device_instance;
slouken@6707
   588
}
slouken@6707
   589
slouken@1895
   590
static int
slouken@1895
   591
allocate_hatdata(SDL_Joystick * joystick)
slouken@0
   592
{
slouken@1895
   593
    int i;
slouken@0
   594
slouken@1895
   595
    joystick->hwdata->hats =
slouken@1895
   596
        (struct hwdata_hat *) SDL_malloc(joystick->nhats *
slouken@1895
   597
                                         sizeof(struct hwdata_hat));
slouken@1895
   598
    if (joystick->hwdata->hats == NULL) {
slouken@1895
   599
        return (-1);
slouken@1895
   600
    }
slouken@1895
   601
    for (i = 0; i < joystick->nhats; ++i) {
slouken@1895
   602
        joystick->hwdata->hats[i].axis[0] = 1;
slouken@1895
   603
        joystick->hwdata->hats[i].axis[1] = 1;
slouken@1895
   604
    }
slouken@1895
   605
    return (0);
slouken@0
   606
}
slouken@0
   607
slouken@1895
   608
static int
slouken@1895
   609
allocate_balldata(SDL_Joystick * joystick)
slouken@0
   610
{
slouken@1895
   611
    int i;
slouken@0
   612
slouken@1895
   613
    joystick->hwdata->balls =
slouken@1895
   614
        (struct hwdata_ball *) SDL_malloc(joystick->nballs *
slouken@1895
   615
                                          sizeof(struct hwdata_ball));
slouken@1895
   616
    if (joystick->hwdata->balls == NULL) {
slouken@1895
   617
        return (-1);
slouken@1895
   618
    }
slouken@1895
   619
    for (i = 0; i < joystick->nballs; ++i) {
slouken@1895
   620
        joystick->hwdata->balls[i].axis[0] = 0;
slouken@1895
   621
        joystick->hwdata->balls[i].axis[1] = 0;
slouken@1895
   622
    }
slouken@1895
   623
    return (0);
slouken@0
   624
}
slouken@0
   625
icculus@6729
   626
static void
icculus@6729
   627
ConfigJoystick(SDL_Joystick * joystick, int fd)
slouken@0
   628
{
slouken@1895
   629
    int i, t;
slouken@3404
   630
    unsigned long keybit[NBITS(KEY_MAX)] = { 0 };
slouken@3404
   631
    unsigned long absbit[NBITS(ABS_MAX)] = { 0 };
slouken@3404
   632
    unsigned long relbit[NBITS(REL_MAX)] = { 0 };
slouken@12088
   633
    unsigned long ffbit[NBITS(FF_MAX)] = { 0 };
slouken@0
   634
slouken@1895
   635
    /* See if this device uses the new unified event API */
slouken@1895
   636
    if ((ioctl(fd, EVIOCGBIT(EV_KEY, sizeof(keybit)), keybit) >= 0) &&
slouken@1895
   637
        (ioctl(fd, EVIOCGBIT(EV_ABS, sizeof(absbit)), absbit) >= 0) &&
slouken@1895
   638
        (ioctl(fd, EVIOCGBIT(EV_REL, sizeof(relbit)), relbit) >= 0)) {
slouken@0
   639
slouken@1895
   640
        /* Get the number of buttons, axes, and other thingamajigs */
slouken@1895
   641
        for (i = BTN_JOYSTICK; i < KEY_MAX; ++i) {
slouken@1895
   642
            if (test_bit(i, keybit)) {
slouken@0
   643
#ifdef DEBUG_INPUT_EVENTS
slouken@1895
   644
                printf("Joystick has button: 0x%x\n", i);
slouken@0
   645
#endif
slouken@10641
   646
                joystick->hwdata->key_map[i] = joystick->nbuttons;
slouken@1895
   647
                ++joystick->nbuttons;
slouken@1895
   648
            }
slouken@1895
   649
        }
slouken@10641
   650
        for (i = 0; i < BTN_JOYSTICK; ++i) {
slouken@1895
   651
            if (test_bit(i, keybit)) {
slouken@0
   652
#ifdef DEBUG_INPUT_EVENTS
slouken@1895
   653
                printf("Joystick has button: 0x%x\n", i);
slouken@0
   654
#endif
slouken@10641
   655
                joystick->hwdata->key_map[i] = joystick->nbuttons;
slouken@1895
   656
                ++joystick->nbuttons;
slouken@1895
   657
            }
slouken@1895
   658
        }
icculus@9630
   659
        for (i = 0; i < ABS_MAX; ++i) {
slouken@1895
   660
            /* Skip hats */
slouken@1895
   661
            if (i == ABS_HAT0X) {
slouken@1895
   662
                i = ABS_HAT3Y;
slouken@1895
   663
                continue;
slouken@1895
   664
            }
slouken@1895
   665
            if (test_bit(i, absbit)) {
slouken@5084
   666
                struct input_absinfo absinfo;
slouken@0
   667
slouken@8053
   668
                if (ioctl(fd, EVIOCGABS(i), &absinfo) < 0) {
slouken@1895
   669
                    continue;
slouken@8053
   670
                }
slouken@0
   671
#ifdef DEBUG_INPUT_EVENTS
slouken@8053
   672
                printf("Joystick has absolute axis: 0x%.2x\n", i);
slouken@1895
   673
                printf("Values = { %d, %d, %d, %d, %d }\n",
slouken@5084
   674
                       absinfo.value, absinfo.minimum, absinfo.maximum,
slouken@5084
   675
                       absinfo.fuzz, absinfo.flat);
slouken@0
   676
#endif /* DEBUG_INPUT_EVENTS */
slouken@1895
   677
                joystick->hwdata->abs_map[i] = joystick->naxes;
slouken@5084
   678
                if (absinfo.minimum == absinfo.maximum) {
slouken@1895
   679
                    joystick->hwdata->abs_correct[i].used = 0;
slouken@1895
   680
                } else {
slouken@1895
   681
                    joystick->hwdata->abs_correct[i].used = 1;
slouken@1895
   682
                    joystick->hwdata->abs_correct[i].coef[0] =
slouken@6845
   683
                        (absinfo.maximum + absinfo.minimum) - 2 * absinfo.flat;
slouken@1895
   684
                    joystick->hwdata->abs_correct[i].coef[1] =
slouken@6845
   685
                        (absinfo.maximum + absinfo.minimum) + 2 * absinfo.flat;
slouken@6845
   686
                    t = ((absinfo.maximum - absinfo.minimum) - 4 * absinfo.flat);
slouken@1895
   687
                    if (t != 0) {
slouken@1895
   688
                        joystick->hwdata->abs_correct[i].coef[2] =
slouken@6845
   689
                            (1 << 28) / t;
slouken@1895
   690
                    } else {
slouken@1895
   691
                        joystick->hwdata->abs_correct[i].coef[2] = 0;
slouken@1895
   692
                    }
slouken@1895
   693
                }
slouken@1895
   694
                ++joystick->naxes;
slouken@1895
   695
            }
slouken@1895
   696
        }
slouken@1895
   697
        for (i = ABS_HAT0X; i <= ABS_HAT3Y; i += 2) {
slouken@1895
   698
            if (test_bit(i, absbit) || test_bit(i + 1, absbit)) {
slouken@8053
   699
                struct input_absinfo absinfo;
slouken@12846
   700
                int hat_index = (i - ABS_HAT0X) / 2;
slouken@8053
   701
slouken@8053
   702
                if (ioctl(fd, EVIOCGABS(i), &absinfo) < 0) {
slouken@8053
   703
                    continue;
slouken@8053
   704
                }
slouken@0
   705
#ifdef DEBUG_INPUT_EVENTS
slouken@12846
   706
                printf("Joystick has hat %d\n", hat_index);
slouken@8053
   707
                printf("Values = { %d, %d, %d, %d, %d }\n",
slouken@8053
   708
                       absinfo.value, absinfo.minimum, absinfo.maximum,
slouken@8053
   709
                       absinfo.fuzz, absinfo.flat);
slouken@8053
   710
#endif /* DEBUG_INPUT_EVENTS */
slouken@12846
   711
                joystick->hwdata->hats_indices[joystick->nhats++] = hat_index;
slouken@1895
   712
            }
slouken@1895
   713
        }
slouken@1895
   714
        if (test_bit(REL_X, relbit) || test_bit(REL_Y, relbit)) {
slouken@1895
   715
            ++joystick->nballs;
slouken@1895
   716
        }
slouken@0
   717
slouken@1895
   718
        /* Allocate data to keep track of these thingamajigs */
slouken@1895
   719
        if (joystick->nhats > 0) {
slouken@1895
   720
            if (allocate_hatdata(joystick) < 0) {
slouken@1895
   721
                joystick->nhats = 0;
slouken@1895
   722
            }
slouken@1895
   723
        }
slouken@1895
   724
        if (joystick->nballs > 0) {
slouken@1895
   725
            if (allocate_balldata(joystick) < 0) {
slouken@1895
   726
                joystick->nballs = 0;
slouken@1895
   727
            }
slouken@1895
   728
        }
slouken@1895
   729
    }
slouken@12088
   730
slouken@12088
   731
    if (ioctl(fd, EVIOCGBIT(EV_FF, sizeof(ffbit)), ffbit) >= 0) {
slouken@12088
   732
        if (test_bit(FF_RUMBLE, ffbit)) {
slouken@12088
   733
            joystick->hwdata->ff_rumble = SDL_TRUE;
slouken@12088
   734
        }
slouken@12088
   735
        if (test_bit(FF_SINE, ffbit)) {
slouken@12088
   736
            joystick->hwdata->ff_sine = SDL_TRUE;
slouken@12088
   737
        }
slouken@12088
   738
    }
slouken@0
   739
}
slouken@0
   740
slouken@892
   741
slouken@0
   742
/* Function to open a joystick for use.
philipp@9380
   743
   The joystick to open is specified by the device index.
slouken@0
   744
   This should fill the nbuttons and naxes fields of the joystick structure.
slouken@0
   745
   It returns 0, or -1 if there is an error.
slouken@0
   746
 */
slouken@12088
   747
static int
slouken@12088
   748
LINUX_JoystickOpen(SDL_Joystick * joystick, int device_index)
slouken@0
   749
{
icculus@6734
   750
    SDL_joylist_item *item = JoystickByDevIndex(device_index);
slouken@892
   751
icculus@6734
   752
    if (item == NULL) {
icculus@7037
   753
        return SDL_SetError("No such device");
icculus@6734
   754
    }
slouken@892
   755
icculus@6751
   756
    joystick->instance_id = item->device_instance;
slouken@1895
   757
    joystick->hwdata = (struct joystick_hwdata *)
slouken@11532
   758
        SDL_calloc(1, sizeof(*joystick->hwdata));
slouken@1895
   759
    if (joystick->hwdata == NULL) {
icculus@7037
   760
        return SDL_OutOfMemory();
slouken@1895
   761
    }
icculus@6752
   762
    joystick->hwdata->item = item;
icculus@6734
   763
    joystick->hwdata->guid = item->guid;
slouken@12088
   764
    joystick->hwdata->effect.id = -1;
slouken@11532
   765
    joystick->hwdata->m_bSteamController = item->m_bSteamController;
slouken@12410
   766
    SDL_memset(joystick->hwdata->abs_map, 0xFF, sizeof(joystick->hwdata->abs_map));
slouken@11532
   767
slouken@11532
   768
    if (item->m_bSteamController) {
slouken@11532
   769
        joystick->hwdata->fd = -1;
slouken@11532
   770
        SDL_GetSteamControllerInputs(&joystick->nbuttons,
slouken@11532
   771
                                     &joystick->naxes,
slouken@11532
   772
                                     &joystick->nhats);
slouken@11532
   773
    } else {
slouken@12088
   774
        int fd = open(item->path, O_RDWR, 0);
slouken@11532
   775
        if (fd < 0) {
slouken@11532
   776
            SDL_free(joystick->hwdata);
slouken@11532
   777
            joystick->hwdata = NULL;
slouken@11532
   778
            return SDL_SetError("Unable to open %s", item->path);
slouken@11532
   779
        }
slouken@11532
   780
slouken@11532
   781
        joystick->hwdata->fd = fd;
slouken@11532
   782
        joystick->hwdata->fname = SDL_strdup(item->path);
slouken@11532
   783
        if (joystick->hwdata->fname == NULL) {
slouken@11532
   784
            SDL_free(joystick->hwdata);
slouken@11532
   785
            joystick->hwdata = NULL;
slouken@11532
   786
            close(fd);
slouken@11532
   787
            return SDL_OutOfMemory();
slouken@11532
   788
        }
slouken@11532
   789
slouken@11532
   790
        /* Set the joystick to non-blocking read mode */
slouken@11532
   791
        fcntl(fd, F_SETFL, O_NONBLOCK);
slouken@11532
   792
slouken@11532
   793
        /* Get the number of buttons and axes on the joystick */
slouken@11532
   794
        ConfigJoystick(joystick, fd);
icculus@6734
   795
    }
icculus@6734
   796
icculus@6734
   797
    SDL_assert(item->hwdata == NULL);
icculus@6734
   798
    item->hwdata = joystick->hwdata;
slouken@0
   799
slouken@7191
   800
    /* mark joystick as fresh and ready */
slouken@6844
   801
    joystick->hwdata->fresh = 1;
slouken@6844
   802
slouken@1895
   803
    return (0);
slouken@0
   804
}
slouken@0
   805
slouken@12088
   806
static int
slouken@12088
   807
LINUX_JoystickRumble(SDL_Joystick * joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble, Uint32 duration_ms)
slouken@12088
   808
{
slouken@12088
   809
    struct input_event event;
slouken@12088
   810
slouken@12333
   811
    if (joystick->hwdata->ff_rumble) {
slouken@12333
   812
        struct ff_effect *effect = &joystick->hwdata->effect;
slouken@12088
   813
slouken@12333
   814
        effect->type = FF_RUMBLE;
slouken@12333
   815
        effect->replay.length = SDL_min(duration_ms, 32767);
slouken@12333
   816
        effect->u.rumble.strong_magnitude = low_frequency_rumble;
slouken@12333
   817
        effect->u.rumble.weak_magnitude = high_frequency_rumble;
slouken@12333
   818
    } else if (joystick->hwdata->ff_sine) {
slouken@12333
   819
        /* Scale and average the two rumble strengths */
slouken@12333
   820
        Sint16 magnitude = (Sint16)(((low_frequency_rumble / 2) + (high_frequency_rumble / 2)) / 2);
slouken@12333
   821
        struct ff_effect *effect = &joystick->hwdata->effect;
slouken@12088
   822
slouken@12333
   823
        effect->type = FF_PERIODIC;
slouken@12333
   824
        effect->replay.length = SDL_min(duration_ms, 32767);
slouken@12333
   825
        effect->u.periodic.waveform = FF_SINE;
slouken@12333
   826
        effect->u.periodic.magnitude = magnitude;
slouken@12333
   827
    } else {
slouken@12333
   828
        return SDL_Unsupported();
slouken@12088
   829
    }
slouken@12088
   830
slouken@12088
   831
    if (ioctl(joystick->hwdata->fd, EVIOCSFF, &joystick->hwdata->effect) < 0) {
slouken@13406
   832
        /* The kernel may have lost this effect, try to allocate a new one */
slouken@13406
   833
        joystick->hwdata->effect.id = -1;
slouken@13406
   834
        if (ioctl(joystick->hwdata->fd, EVIOCSFF, &joystick->hwdata->effect) < 0) {
slouken@13406
   835
            return SDL_SetError("Couldn't update rumble effect: %s", strerror(errno));
slouken@13406
   836
        }
slouken@12088
   837
    }
slouken@12088
   838
slouken@12088
   839
    event.type = EV_FF;
slouken@12088
   840
    event.code = joystick->hwdata->effect.id;
slouken@12088
   841
    event.value = 1;
slouken@12088
   842
    if (write(joystick->hwdata->fd, &event, sizeof(event)) < 0) {
slouken@12088
   843
        return SDL_SetError("Couldn't start rumble effect: %s", strerror(errno));
slouken@12088
   844
    }
slouken@12088
   845
    return 0;
slouken@12088
   846
}
slouken@12088
   847
slouken@7860
   848
static SDL_INLINE void
slouken@1895
   849
HandleHat(SDL_Joystick * stick, Uint8 hat, int axis, int value)
slouken@0
   850
{
slouken@1895
   851
    struct hwdata_hat *the_hat;
slouken@1895
   852
    const Uint8 position_map[3][3] = {
slouken@1895
   853
        {SDL_HAT_LEFTUP, SDL_HAT_UP, SDL_HAT_RIGHTUP},
slouken@1895
   854
        {SDL_HAT_LEFT, SDL_HAT_CENTERED, SDL_HAT_RIGHT},
slouken@1895
   855
        {SDL_HAT_LEFTDOWN, SDL_HAT_DOWN, SDL_HAT_RIGHTDOWN}
slouken@1895
   856
    };
slouken@0
   857
slouken@12848
   858
    the_hat = &stick->hwdata->hats[hat];
slouken@1895
   859
    if (value < 0) {
slouken@1895
   860
        value = 0;
slouken@1895
   861
    } else if (value == 0) {
slouken@1895
   862
        value = 1;
slouken@1895
   863
    } else if (value > 0) {
slouken@1895
   864
        value = 2;
slouken@1895
   865
    }
slouken@1895
   866
    if (value != the_hat->axis[axis]) {
slouken@1895
   867
        the_hat->axis[axis] = value;
slouken@12848
   868
        SDL_PrivateJoystickHat(stick, hat,
slouken@12847
   869
                               position_map[the_hat->axis[1]][the_hat->axis[0]]);
slouken@1895
   870
    }
slouken@0
   871
}
slouken@0
   872
slouken@7860
   873
static SDL_INLINE void
slouken@1895
   874
HandleBall(SDL_Joystick * stick, Uint8 ball, int axis, int value)
slouken@0
   875
{
slouken@1895
   876
    stick->hwdata->balls[ball].axis[axis] += value;
slouken@0
   877
}
slouken@0
   878
slouken@0
   879
slouken@7860
   880
static SDL_INLINE int
icculus@6729
   881
AxisCorrect(SDL_Joystick * joystick, int which, int value)
slouken@0
   882
{
slouken@1895
   883
    struct axis_correct *correct;
slouken@0
   884
slouken@1895
   885
    correct = &joystick->hwdata->abs_correct[which];
slouken@1895
   886
    if (correct->used) {
slouken@6845
   887
        value *= 2;
slouken@1895
   888
        if (value > correct->coef[0]) {
slouken@1895
   889
            if (value < correct->coef[1]) {
slouken@1895
   890
                return 0;
slouken@1895
   891
            }
slouken@1895
   892
            value -= correct->coef[1];
slouken@1895
   893
        } else {
slouken@1895
   894
            value -= correct->coef[0];
slouken@1895
   895
        }
slouken@1895
   896
        value *= correct->coef[2];
slouken@6845
   897
        value >>= 13;
slouken@1895
   898
    }
slouken@554
   899
slouken@1895
   900
    /* Clamp and return */
slouken@1895
   901
    if (value < -32768)
slouken@1895
   902
        return -32768;
slouken@1895
   903
    if (value > 32767)
slouken@1895
   904
        return 32767;
slouken@554
   905
slouken@1895
   906
    return value;
slouken@0
   907
}
slouken@0
   908
slouken@7860
   909
static SDL_INLINE void
slouken@6844
   910
PollAllValues(SDL_Joystick * joystick)
slouken@6844
   911
{
slouken@6844
   912
    struct input_absinfo absinfo;
slouken@12793
   913
    int i;
slouken@6844
   914
slouken@7191
   915
    /* Poll all axis */
slouken@12793
   916
    for (i = ABS_X; i < ABS_MAX; i++) {
slouken@12793
   917
        if (i == ABS_HAT0X) {
slouken@12793
   918
            i = ABS_HAT3Y;
slouken@12793
   919
            continue;
slouken@12793
   920
        }
slouken@12793
   921
        if (joystick->hwdata->abs_correct[i].used) {
slouken@12793
   922
            if (ioctl(joystick->hwdata->fd, EVIOCGABS(i), &absinfo) >= 0) {
slouken@12793
   923
                absinfo.value = AxisCorrect(joystick, i, absinfo.value);
slouken@6844
   924
slouken@6844
   925
#ifdef DEBUG_INPUT_EVENTS
slouken@12793
   926
                printf("Joystick : Re-read Axis %d (%d) val= %d\n",
slouken@12793
   927
                    joystick->hwdata->abs_map[i], i, absinfo.value);
slouken@6844
   928
#endif
slouken@12793
   929
                SDL_PrivateJoystickAxis(joystick,
slouken@12793
   930
                        joystick->hwdata->abs_map[i],
slouken@12793
   931
                        absinfo.value);
slouken@6844
   932
            }
slouken@6844
   933
        }
slouken@6844
   934
    }
slouken@6844
   935
}
slouken@6844
   936
slouken@7860
   937
static SDL_INLINE void
icculus@6729
   938
HandleInputEvents(SDL_Joystick * joystick)
slouken@0
   939
{
slouken@1895
   940
    struct input_event events[32];
slouken@1895
   941
    int i, len;
slouken@1895
   942
    int code;
slouken@0
   943
slouken@6844
   944
    if (joystick->hwdata->fresh) {
slouken@6844
   945
        PollAllValues(joystick);
slouken@6844
   946
        joystick->hwdata->fresh = 0;
slouken@6844
   947
    }
slouken@6844
   948
slouken@1895
   949
    while ((len = read(joystick->hwdata->fd, events, (sizeof events))) > 0) {
slouken@1895
   950
        len /= sizeof(events[0]);
slouken@1895
   951
        for (i = 0; i < len; ++i) {
slouken@1895
   952
            code = events[i].code;
slouken@1895
   953
            switch (events[i].type) {
slouken@1895
   954
            case EV_KEY:
slouken@10641
   955
                SDL_PrivateJoystickButton(joystick,
slouken@10641
   956
                                          joystick->hwdata->key_map[code],
slouken@10641
   957
                                          events[i].value);
slouken@1895
   958
                break;
slouken@1895
   959
            case EV_ABS:
slouken@1895
   960
                switch (code) {
slouken@1895
   961
                case ABS_HAT0X:
slouken@1895
   962
                case ABS_HAT0Y:
slouken@1895
   963
                case ABS_HAT1X:
slouken@1895
   964
                case ABS_HAT1Y:
slouken@1895
   965
                case ABS_HAT2X:
slouken@1895
   966
                case ABS_HAT2Y:
slouken@1895
   967
                case ABS_HAT3X:
slouken@1895
   968
                case ABS_HAT3Y:
slouken@1895
   969
                    code -= ABS_HAT0X;
slouken@12848
   970
                    HandleHat(joystick, joystick->hwdata->hats_indices[code / 2], code % 2, events[i].value);
slouken@1895
   971
                    break;
slouken@1895
   972
                default:
slouken@12410
   973
                    if (joystick->hwdata->abs_map[code] != 0xFF) {
slouken@12410
   974
                        events[i].value =
slouken@12410
   975
                            AxisCorrect(joystick, code, events[i].value);
slouken@12410
   976
                        SDL_PrivateJoystickAxis(joystick,
slouken@12410
   977
                                                joystick->hwdata->abs_map[code],
slouken@12410
   978
                                                events[i].value);
slouken@12410
   979
                    }
slouken@1895
   980
                    break;
slouken@1895
   981
                }
slouken@1895
   982
                break;
slouken@1895
   983
            case EV_REL:
slouken@1895
   984
                switch (code) {
slouken@1895
   985
                case REL_X:
slouken@1895
   986
                case REL_Y:
slouken@1895
   987
                    code -= REL_X;
slouken@1895
   988
                    HandleBall(joystick, code / 2, code % 2, events[i].value);
slouken@1895
   989
                    break;
slouken@1895
   990
                default:
slouken@1895
   991
                    break;
slouken@1895
   992
                }
slouken@1895
   993
                break;
slouken@6844
   994
            case EV_SYN:
slouken@6844
   995
                switch (code) {
slouken@6844
   996
                case SYN_DROPPED :
slouken@6844
   997
#ifdef DEBUG_INPUT_EVENTS
philipp@7133
   998
                    printf("Event SYN_DROPPED detected\n");
slouken@6844
   999
#endif
slouken@6844
  1000
                    PollAllValues(joystick);
slouken@6844
  1001
                    break;
slouken@6844
  1002
                default:
slouken@6844
  1003
                    break;
slouken@6844
  1004
                }
slouken@1895
  1005
            default:
slouken@1895
  1006
                break;
slouken@1895
  1007
            }
slouken@1895
  1008
        }
slouken@1895
  1009
    }
cameron@12944
  1010
cameron@12944
  1011
    if (errno == ENODEV) {
cameron@12944
  1012
        /* We have to wait until the JoystickDetect callback to remove this */
cameron@12944
  1013
        joystick->hwdata->gone = SDL_TRUE;
cameron@12944
  1014
    }
slouken@0
  1015
}
slouken@0
  1016
slouken@12088
  1017
static void
slouken@12088
  1018
LINUX_JoystickUpdate(SDL_Joystick * joystick)
slouken@0
  1019
{
slouken@1895
  1020
    int i;
slouken@1895
  1021
slouken@11532
  1022
    if (joystick->hwdata->m_bSteamController) {
slouken@11532
  1023
        SDL_UpdateSteamController(joystick);
slouken@11532
  1024
        return;
slouken@11532
  1025
    }
slouken@11532
  1026
icculus@6729
  1027
    HandleInputEvents(joystick);
slouken@0
  1028
slouken@1895
  1029
    /* Deliver ball motion updates */
slouken@1895
  1030
    for (i = 0; i < joystick->nballs; ++i) {
slouken@1895
  1031
        int xrel, yrel;
slouken@0
  1032
slouken@1895
  1033
        xrel = joystick->hwdata->balls[i].axis[0];
slouken@1895
  1034
        yrel = joystick->hwdata->balls[i].axis[1];
slouken@1895
  1035
        if (xrel || yrel) {
slouken@1895
  1036
            joystick->hwdata->balls[i].axis[0] = 0;
slouken@1895
  1037
            joystick->hwdata->balls[i].axis[1] = 0;
slouken@1895
  1038
            SDL_PrivateJoystickBall(joystick, (Uint8) i, xrel, yrel);
slouken@1895
  1039
        }
slouken@1895
  1040
    }
slouken@0
  1041
}
slouken@0
  1042
slouken@0
  1043
/* Function to close a joystick after use */
slouken@12088
  1044
static void
slouken@12088
  1045
LINUX_JoystickClose(SDL_Joystick * joystick)
slouken@0
  1046
{
slouken@1895
  1047
    if (joystick->hwdata) {
slouken@12088
  1048
        if (joystick->hwdata->effect.id >= 0) {
slouken@12088
  1049
            ioctl(joystick->hwdata->fd, EVIOCRMFF, joystick->hwdata->effect.id);
slouken@12088
  1050
            joystick->hwdata->effect.id = -1;
slouken@12088
  1051
        }
slouken@11532
  1052
        if (joystick->hwdata->fd >= 0) {
slouken@11532
  1053
            close(joystick->hwdata->fd);
slouken@11532
  1054
        }
icculus@6752
  1055
        if (joystick->hwdata->item) {
icculus@6752
  1056
            joystick->hwdata->item->hwdata = NULL;
icculus@6752
  1057
        }
icculus@6734
  1058
        SDL_free(joystick->hwdata->hats);
icculus@6734
  1059
        SDL_free(joystick->hwdata->balls);
icculus@6734
  1060
        SDL_free(joystick->hwdata->fname);
slouken@1895
  1061
        SDL_free(joystick->hwdata);
slouken@1895
  1062
    }
slouken@0
  1063
}
slouken@0
  1064
slouken@0
  1065
/* Function to perform any system-specific joystick related cleanup */
slouken@12088
  1066
static void
slouken@12088
  1067
LINUX_JoystickQuit(void)
slouken@0
  1068
{
icculus@6734
  1069
    SDL_joylist_item *item = NULL;
icculus@6734
  1070
    SDL_joylist_item *next = NULL;
icculus@6734
  1071
icculus@6734
  1072
    for (item = SDL_joylist; item; item = next) {
icculus@6734
  1073
        next = item->next;
icculus@6734
  1074
        SDL_free(item->path);
icculus@6734
  1075
        SDL_free(item->name);
icculus@6734
  1076
        SDL_free(item);
icculus@6734
  1077
    }
icculus@6734
  1078
icculus@6734
  1079
    SDL_joylist = SDL_joylist_tail = NULL;
slouken@0
  1080
icculus@6734
  1081
    numjoysticks = 0;
icculus@6734
  1082
icculus@6734
  1083
#if SDL_USE_LIBUDEV
gabomdq@7772
  1084
    SDL_UDEV_DelCallback(joystick_udev_callback);
gabomdq@7772
  1085
    SDL_UDEV_Quit();
icculus@6734
  1086
#endif
slouken@11532
  1087
slouken@11532
  1088
    SDL_QuitSteamControllers();
slouken@0
  1089
}
slouken@0
  1090
slouken@12088
  1091
SDL_JoystickDriver SDL_LINUX_JoystickDriver =
slouken@6690
  1092
{
slouken@12088
  1093
    LINUX_JoystickInit,
slouken@12088
  1094
    LINUX_JoystickGetCount,
slouken@12088
  1095
    LINUX_JoystickDetect,
slouken@12088
  1096
    LINUX_JoystickGetDeviceName,
slouken@12359
  1097
    LINUX_JoystickGetDevicePlayerIndex,
slouken@13369
  1098
    LINUX_JoystickSetDevicePlayerIndex,
slouken@12088
  1099
    LINUX_JoystickGetDeviceGUID,
slouken@12088
  1100
    LINUX_JoystickGetDeviceInstanceID,
slouken@12088
  1101
    LINUX_JoystickOpen,
slouken@12088
  1102
    LINUX_JoystickRumble,
slouken@12088
  1103
    LINUX_JoystickUpdate,
slouken@12088
  1104
    LINUX_JoystickClose,
slouken@12088
  1105
    LINUX_JoystickQuit,
slouken@12088
  1106
};
slouken@6690
  1107
slouken@1635
  1108
#endif /* SDL_JOYSTICK_LINUX */
slouken@6693
  1109
slouken@1895
  1110
/* vi: set ts=4 sw=4 expandtab: */