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