src/joystick/hidapi/SDL_hidapijoystick.c
author Sam Lantinga
Wed, 11 Dec 2019 17:46:59 -0800
changeset 13332 71116618f317
parent 13330 c88765bcf1da
child 13333 88aec26a0ad0
permissions -rw-r--r--
Shorten "Performance Designed Products" to "PDP"
slouken@12088
     1
/*
slouken@12088
     2
  Simple DirectMedia Layer
slouken@12503
     3
  Copyright (C) 1997-2019 Sam Lantinga <slouken@libsdl.org>
slouken@12088
     4
slouken@12088
     5
  This software is provided 'as-is', without any express or implied
slouken@12088
     6
  warranty.  In no event will the authors be held liable for any damages
slouken@12088
     7
  arising from the use of this software.
slouken@12088
     8
slouken@12088
     9
  Permission is granted to anyone to use this software for any purpose,
slouken@12088
    10
  including commercial applications, and to alter it and redistribute it
slouken@12088
    11
  freely, subject to the following restrictions:
slouken@12088
    12
slouken@12088
    13
  1. The origin of this software must not be misrepresented; you must not
slouken@12088
    14
     claim that you wrote the original software. If you use this software
slouken@12088
    15
     in a product, an acknowledgment in the product documentation would be
slouken@12088
    16
     appreciated but is not required.
slouken@12088
    17
  2. Altered source versions must be plainly marked as such, and must not be
slouken@12088
    18
     misrepresented as being the original software.
slouken@12088
    19
  3. This notice may not be removed or altered from any source distribution.
slouken@12088
    20
*/
slouken@12088
    21
#include "../../SDL_internal.h"
slouken@12088
    22
slouken@12088
    23
#ifdef SDL_JOYSTICK_HIDAPI
slouken@12088
    24
slouken@12088
    25
#include "SDL_endian.h"
slouken@12088
    26
#include "SDL_hints.h"
slouken@12088
    27
#include "SDL_log.h"
slouken@12277
    28
#include "SDL_mutex.h"
slouken@12109
    29
#include "SDL_thread.h"
slouken@12088
    30
#include "SDL_timer.h"
slouken@12088
    31
#include "SDL_joystick.h"
slouken@12088
    32
#include "../SDL_sysjoystick.h"
slouken@12088
    33
#include "SDL_hidapijoystick_c.h"
slouken@13244
    34
#include "../../SDL_hints_c.h"
slouken@12088
    35
slouken@12109
    36
#if defined(__WIN32__)
slouken@12109
    37
#include "../../core/windows/SDL_windows.h"
slouken@12109
    38
#endif
slouken@12109
    39
slouken@12109
    40
#if defined(__MACOSX__)
slouken@12109
    41
#include <CoreFoundation/CoreFoundation.h>
slouken@12109
    42
#include <mach/mach.h>
slouken@12109
    43
#include <IOKit/IOKitLib.h>
slouken@12109
    44
#include <IOKit/usb/USBSpec.h>
slouken@12109
    45
#endif
slouken@12109
    46
slouken@12109
    47
#if defined(__LINUX__)
slouken@12109
    48
#include "../../core/linux/SDL_udev.h"
slouken@12109
    49
#ifdef SDL_USE_LIBUDEV
slouken@12109
    50
#include <poll.h>
slouken@12109
    51
#endif
slouken@12109
    52
#endif
slouken@12088
    53
slouken@12896
    54
struct joystick_hwdata
slouken@12896
    55
{
slouken@12896
    56
    SDL_HIDAPI_DeviceDriver *driver;
slouken@12896
    57
    void *context;
slouken@12896
    58
slouken@12896
    59
    SDL_mutex *mutex;
slouken@12896
    60
    hid_device *dev;
slouken@12896
    61
};
slouken@12896
    62
slouken@12088
    63
typedef struct _SDL_HIDAPI_Device
slouken@12088
    64
{
slouken@12896
    65
    SDL_JoystickID instance_id;
slouken@12088
    66
    char *name;
slouken@12088
    67
    char *path;
slouken@12088
    68
    Uint16 vendor_id;
slouken@12088
    69
    Uint16 product_id;
slouken@12162
    70
    Uint16 version;
slouken@12088
    71
    SDL_JoystickGUID guid;
slouken@12088
    72
    int interface_number;   /* Available on Windows and Linux */
slouken@12088
    73
    Uint16 usage_page;      /* Available on Windows and Mac OS X */
slouken@12088
    74
    Uint16 usage;           /* Available on Windows and Mac OS X */
slouken@12088
    75
    SDL_HIDAPI_DeviceDriver *driver;
slouken@12088
    76
slouken@12088
    77
    /* Used during scanning for device changes */
slouken@12088
    78
    SDL_bool seen;
slouken@12088
    79
slouken@12088
    80
    struct _SDL_HIDAPI_Device *next;
slouken@12088
    81
} SDL_HIDAPI_Device;
slouken@12088
    82
slouken@12088
    83
static SDL_HIDAPI_DeviceDriver *SDL_HIDAPI_drivers[] = {
slouken@12088
    84
#ifdef SDL_JOYSTICK_HIDAPI_PS4
slouken@12088
    85
    &SDL_HIDAPI_DriverPS4,
slouken@12088
    86
#endif
slouken@12088
    87
#ifdef SDL_JOYSTICK_HIDAPI_STEAM
slouken@12088
    88
    &SDL_HIDAPI_DriverSteam,
slouken@12088
    89
#endif
slouken@12088
    90
#ifdef SDL_JOYSTICK_HIDAPI_SWITCH
slouken@12088
    91
    &SDL_HIDAPI_DriverSwitch,
slouken@12088
    92
#endif
slouken@12088
    93
#ifdef SDL_JOYSTICK_HIDAPI_XBOX360
slouken@12088
    94
    &SDL_HIDAPI_DriverXbox360,
slouken@12088
    95
#endif
slouken@12088
    96
#ifdef SDL_JOYSTICK_HIDAPI_XBOXONE
slouken@12088
    97
    &SDL_HIDAPI_DriverXboxOne,
slouken@12088
    98
#endif
slouken@12088
    99
};
slouken@12953
   100
static int SDL_HIDAPI_numdrivers = 0;
slouken@12088
   101
static SDL_HIDAPI_Device *SDL_HIDAPI_devices;
slouken@12088
   102
static int SDL_HIDAPI_numjoysticks = 0;
aeikum@12972
   103
static SDL_bool initialized = SDL_FALSE;
slouken@12109
   104
slouken@12203
   105
#if defined(SDL_USE_LIBUDEV)
slouken@12203
   106
static const SDL_UDEV_Symbols * usyms = NULL;
slouken@12203
   107
#endif
slouken@12203
   108
slouken@12109
   109
static struct
slouken@12109
   110
{
slouken@12109
   111
    SDL_bool m_bHaveDevicesChanged;
slouken@12109
   112
    SDL_bool m_bCanGetNotifications;
slouken@12109
   113
    Uint32 m_unLastDetect;
slouken@12109
   114
slouken@12109
   115
#if defined(__WIN32__)
slouken@12110
   116
    SDL_threadID m_nThreadID;
slouken@12109
   117
    WNDCLASSEXA m_wndClass;
slouken@12109
   118
    HWND m_hwndMsg;
slouken@12109
   119
    HDEVNOTIFY m_hNotify;
slouken@12109
   120
    double m_flLastWin32MessageCheck;
slouken@12109
   121
#endif
slouken@12109
   122
slouken@12109
   123
#if defined(__MACOSX__)
slouken@12109
   124
    IONotificationPortRef m_notificationPort;
slouken@12109
   125
    mach_port_t m_notificationMach;
slouken@12109
   126
#endif
slouken@12109
   127
slouken@12109
   128
#if defined(SDL_USE_LIBUDEV)
slouken@12109
   129
    struct udev *m_pUdev;
slouken@12109
   130
    struct udev_monitor *m_pUdevMonitor;
slouken@12109
   131
    int m_nUdevFd;
slouken@12109
   132
#endif
slouken@12109
   133
} SDL_HIDAPI_discovery;
slouken@12109
   134
slouken@12109
   135
slouken@12109
   136
#ifdef __WIN32__
slouken@12109
   137
struct _DEV_BROADCAST_HDR
slouken@12109
   138
{
slouken@12109
   139
    DWORD       dbch_size;
slouken@12109
   140
    DWORD       dbch_devicetype;
slouken@12109
   141
    DWORD       dbch_reserved;
slouken@12109
   142
};
slouken@12109
   143
slouken@12109
   144
typedef struct _DEV_BROADCAST_DEVICEINTERFACE_A
slouken@12109
   145
{
slouken@12109
   146
    DWORD       dbcc_size;
slouken@12109
   147
    DWORD       dbcc_devicetype;
slouken@12109
   148
    DWORD       dbcc_reserved;
slouken@12109
   149
    GUID        dbcc_classguid;
slouken@12109
   150
    char        dbcc_name[ 1 ];
slouken@12109
   151
} DEV_BROADCAST_DEVICEINTERFACE_A, *PDEV_BROADCAST_DEVICEINTERFACE_A;
slouken@12109
   152
slouken@12109
   153
typedef struct  _DEV_BROADCAST_HDR      DEV_BROADCAST_HDR;
slouken@12109
   154
#define DBT_DEVICEARRIVAL               0x8000  /* system detected a new device */
slouken@12162
   155
#define DBT_DEVICEREMOVECOMPLETE        0x8004  /* device was removed from the system */
slouken@12109
   156
#define DBT_DEVTYP_DEVICEINTERFACE      0x00000005  /* device interface class */
slouken@12109
   157
#define DBT_DEVNODES_CHANGED            0x0007
slouken@12109
   158
#define DBT_CONFIGCHANGED               0x0018
slouken@12109
   159
#define DBT_DEVICETYPESPECIFIC          0x8005  /* type specific event */
slouken@12111
   160
#define DBT_DEVINSTSTARTED              0x8008  /* device installed and started */
slouken@12109
   161
slouken@12109
   162
#include <initguid.h>
slouken@12109
   163
DEFINE_GUID(GUID_DEVINTERFACE_USB_DEVICE, 0xA5DCBF10L, 0x6530, 0x11D2, 0x90, 0x1F, 0x00, 0xC0, 0x4F, 0xB9, 0x51, 0xED);
slouken@12109
   164
slouken@12109
   165
static LRESULT CALLBACK ControllerWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
slouken@12109
   166
{
slouken@12109
   167
    switch (message) {
slouken@12109
   168
    case WM_DEVICECHANGE:
slouken@12109
   169
        switch (wParam) {
slouken@12109
   170
        case DBT_DEVICEARRIVAL:
slouken@12162
   171
        case DBT_DEVICEREMOVECOMPLETE:
slouken@12109
   172
            if (((DEV_BROADCAST_HDR*)lParam)->dbch_devicetype == DBT_DEVTYP_DEVICEINTERFACE) {
slouken@12110
   173
                SDL_HIDAPI_discovery.m_bHaveDevicesChanged = SDL_TRUE;
slouken@12109
   174
            }
slouken@12109
   175
            break;
slouken@12109
   176
        }
slouken@12113
   177
        return TRUE;
slouken@12109
   178
    }
slouken@12109
   179
slouken@12109
   180
    return DefWindowProc(hwnd, message, wParam, lParam);
slouken@12109
   181
}
slouken@12109
   182
#endif /* __WIN32__ */
slouken@12109
   183
slouken@12109
   184
slouken@12109
   185
#if defined(__MACOSX__)
slouken@12110
   186
static void CallbackIOServiceFunc(void *context, io_iterator_t portIterator)
slouken@12109
   187
{
slouken@12109
   188
    /* Must drain the iterator, or we won't receive new notifications */
slouken@12110
   189
    io_object_t entry;
slouken@12110
   190
    while ((entry = IOIteratorNext(portIterator)) != 0) {
slouken@12109
   191
        IOObjectRelease(entry);
slouken@12109
   192
        *(SDL_bool*)context = SDL_TRUE;
slouken@12109
   193
    }
slouken@12109
   194
}
slouken@12109
   195
#endif /* __MACOSX__ */
slouken@12109
   196
slouken@12109
   197
static void
slouken@12109
   198
HIDAPI_InitializeDiscovery()
slouken@12109
   199
{
slouken@12109
   200
    SDL_HIDAPI_discovery.m_bHaveDevicesChanged = SDL_TRUE;
slouken@12109
   201
    SDL_HIDAPI_discovery.m_bCanGetNotifications = SDL_FALSE;
slouken@12109
   202
    SDL_HIDAPI_discovery.m_unLastDetect = 0;
slouken@12109
   203
slouken@12109
   204
#if defined(__WIN32__)
slouken@12109
   205
    SDL_HIDAPI_discovery.m_nThreadID = SDL_ThreadID();
slouken@12109
   206
slouken@12109
   207
    SDL_memset(&SDL_HIDAPI_discovery.m_wndClass, 0x0, sizeof(SDL_HIDAPI_discovery.m_wndClass));
slouken@12109
   208
    SDL_HIDAPI_discovery.m_wndClass.hInstance = GetModuleHandle(NULL);
slouken@12114
   209
    SDL_HIDAPI_discovery.m_wndClass.lpszClassName = "SDL_HIDAPI_DEVICE_DETECTION";
slouken@12109
   210
    SDL_HIDAPI_discovery.m_wndClass.lpfnWndProc = ControllerWndProc;      /* This function is called by windows */
slouken@12109
   211
    SDL_HIDAPI_discovery.m_wndClass.cbSize = sizeof(WNDCLASSEX);
slouken@12109
   212
slouken@12109
   213
    RegisterClassExA(&SDL_HIDAPI_discovery.m_wndClass);
slouken@12114
   214
    SDL_HIDAPI_discovery.m_hwndMsg = CreateWindowExA(0, "SDL_HIDAPI_DEVICE_DETECTION", NULL, 0, 0, 0, 0, 0, HWND_MESSAGE, NULL, NULL, NULL);
slouken@12109
   215
slouken@12110
   216
    {
slouken@12110
   217
        DEV_BROADCAST_DEVICEINTERFACE_A devBroadcast;
slouken@12110
   218
        SDL_memset( &devBroadcast, 0x0, sizeof( devBroadcast ) );
slouken@12109
   219
slouken@12110
   220
        devBroadcast.dbcc_size = sizeof( devBroadcast );
slouken@12110
   221
        devBroadcast.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE;
slouken@12110
   222
        devBroadcast.dbcc_classguid = GUID_DEVINTERFACE_USB_DEVICE;
slouken@12109
   223
slouken@12110
   224
        /* DEVICE_NOTIFY_ALL_INTERFACE_CLASSES is important, makes GUID_DEVINTERFACE_USB_DEVICE ignored,
slouken@12110
   225
         * but that seems to be necessary to get a notice after each individual usb input device actually
slouken@12110
   226
         * installs, rather than just as the composite device is seen.
slouken@12110
   227
         */
slouken@12110
   228
        SDL_HIDAPI_discovery.m_hNotify = RegisterDeviceNotification( SDL_HIDAPI_discovery.m_hwndMsg, &devBroadcast, DEVICE_NOTIFY_WINDOW_HANDLE | DEVICE_NOTIFY_ALL_INTERFACE_CLASSES );
slouken@12110
   229
        SDL_HIDAPI_discovery.m_bCanGetNotifications = ( SDL_HIDAPI_discovery.m_hNotify != 0 );
slouken@12110
   230
    }
slouken@12109
   231
#endif /* __WIN32__ */
slouken@12109
   232
slouken@12109
   233
#if defined(__MACOSX__)
slouken@12109
   234
    SDL_HIDAPI_discovery.m_notificationPort = IONotificationPortCreate(kIOMasterPortDefault);
slouken@12109
   235
    if (SDL_HIDAPI_discovery.m_notificationPort) {
slouken@12109
   236
        {
slouken@12109
   237
            CFMutableDictionaryRef matchingDict = IOServiceMatching("IOUSBDevice");
slouken@12109
   238
slouken@12109
   239
            /* Note: IOServiceAddMatchingNotification consumes the reference to matchingDict */
slouken@12109
   240
            io_iterator_t portIterator = 0;
slouken@12110
   241
            io_object_t entry;
slouken@12109
   242
            if (IOServiceAddMatchingNotification(SDL_HIDAPI_discovery.m_notificationPort, kIOMatchedNotification, matchingDict, CallbackIOServiceFunc, &SDL_HIDAPI_discovery.m_bHaveDevicesChanged, &portIterator) == 0) {
slouken@12109
   243
                /* Must drain the existing iterator, or we won't receive new notifications */
slouken@12110
   244
                while ((entry = IOIteratorNext(portIterator)) != 0) {
slouken@12109
   245
                    IOObjectRelease(entry);
slouken@12109
   246
                }
slouken@12109
   247
            } else {
slouken@12109
   248
                IONotificationPortDestroy(SDL_HIDAPI_discovery.m_notificationPort);
slouken@12109
   249
                SDL_HIDAPI_discovery.m_notificationPort = nil;
slouken@12109
   250
            }
slouken@12109
   251
        }
slouken@12109
   252
        {
slouken@12109
   253
            CFMutableDictionaryRef matchingDict = IOServiceMatching("IOBluetoothDevice");
slouken@12109
   254
slouken@12109
   255
            /* Note: IOServiceAddMatchingNotification consumes the reference to matchingDict */
slouken@12109
   256
            io_iterator_t portIterator = 0;
slouken@12110
   257
            io_object_t entry;
slouken@12109
   258
            if (IOServiceAddMatchingNotification(SDL_HIDAPI_discovery.m_notificationPort, kIOMatchedNotification, matchingDict, CallbackIOServiceFunc, &SDL_HIDAPI_discovery.m_bHaveDevicesChanged, &portIterator) == 0) {
slouken@12109
   259
                /* Must drain the existing iterator, or we won't receive new notifications */
slouken@12110
   260
                while ((entry = IOIteratorNext(portIterator)) != 0) {
slouken@12109
   261
                    IOObjectRelease(entry);
slouken@12109
   262
                }
slouken@12109
   263
            } else {
slouken@12109
   264
                IONotificationPortDestroy(SDL_HIDAPI_discovery.m_notificationPort);
slouken@12109
   265
                SDL_HIDAPI_discovery.m_notificationPort = nil;
slouken@12109
   266
            }
slouken@12109
   267
        }
slouken@12109
   268
    }
slouken@12109
   269
slouken@12109
   270
    SDL_HIDAPI_discovery.m_notificationMach = MACH_PORT_NULL;
slouken@12109
   271
    if (SDL_HIDAPI_discovery.m_notificationPort) {
slouken@12109
   272
        SDL_HIDAPI_discovery.m_notificationMach = IONotificationPortGetMachPort(SDL_HIDAPI_discovery.m_notificationPort);
slouken@12109
   273
    }
slouken@12109
   274
slouken@12109
   275
    SDL_HIDAPI_discovery.m_bCanGetNotifications = (SDL_HIDAPI_discovery.m_notificationMach != MACH_PORT_NULL);
slouken@12109
   276
slouken@12109
   277
#endif // __MACOSX__
slouken@12109
   278
slouken@12109
   279
#if defined(SDL_USE_LIBUDEV)
slouken@12109
   280
    SDL_HIDAPI_discovery.m_pUdev = NULL;
slouken@12109
   281
    SDL_HIDAPI_discovery.m_pUdevMonitor = NULL;
slouken@12109
   282
    SDL_HIDAPI_discovery.m_nUdevFd = -1;
slouken@12109
   283
slouken@12203
   284
    usyms = SDL_UDEV_GetUdevSyms();
slouken@12203
   285
    if (usyms) {
slouken@12203
   286
        SDL_HIDAPI_discovery.m_pUdev = usyms->udev_new();
slouken@12203
   287
    }
slouken@12109
   288
    if (SDL_HIDAPI_discovery.m_pUdev) {
slouken@12203
   289
        SDL_HIDAPI_discovery.m_pUdevMonitor = usyms->udev_monitor_new_from_netlink(SDL_HIDAPI_discovery.m_pUdev, "udev");
slouken@12109
   290
        if (SDL_HIDAPI_discovery.m_pUdevMonitor) {
slouken@12203
   291
            usyms->udev_monitor_enable_receiving(SDL_HIDAPI_discovery.m_pUdevMonitor);
slouken@12203
   292
            SDL_HIDAPI_discovery.m_nUdevFd = usyms->udev_monitor_get_fd(SDL_HIDAPI_discovery.m_pUdevMonitor);
sezeroz@12181
   293
            SDL_HIDAPI_discovery.m_bCanGetNotifications = SDL_TRUE;
slouken@12109
   294
        }
slouken@12109
   295
    }
slouken@12109
   296
slouken@12109
   297
#endif /* SDL_USE_LIBUDEV */
slouken@12109
   298
}
slouken@12109
   299
slouken@12109
   300
static void
slouken@12109
   301
HIDAPI_UpdateDiscovery()
slouken@12109
   302
{
slouken@12109
   303
    if (!SDL_HIDAPI_discovery.m_bCanGetNotifications) {
slouken@12109
   304
        const Uint32 SDL_HIDAPI_DETECT_INTERVAL_MS = 3000;  /* Update every 3 seconds */
slouken@12109
   305
        Uint32 now = SDL_GetTicks();
slouken@12109
   306
        if (!SDL_HIDAPI_discovery.m_unLastDetect || SDL_TICKS_PASSED(now, SDL_HIDAPI_discovery.m_unLastDetect + SDL_HIDAPI_DETECT_INTERVAL_MS)) {
slouken@12109
   307
            SDL_HIDAPI_discovery.m_bHaveDevicesChanged = SDL_TRUE;
slouken@12109
   308
            SDL_HIDAPI_discovery.m_unLastDetect = now;
slouken@12109
   309
        }
slouken@12109
   310
        return;
slouken@12109
   311
    }
slouken@12109
   312
slouken@12363
   313
#if defined(__WIN32__)
slouken@12363
   314
#if 0 /* just let the usual SDL_PumpEvents loop dispatch these, fixing bug 4286. --ryan. */
slouken@12109
   315
    /* We'll only get messages on the same thread that created the window */
slouken@12109
   316
    if (SDL_ThreadID() == SDL_HIDAPI_discovery.m_nThreadID) {
slouken@12109
   317
        MSG msg;
slouken@12109
   318
        while (PeekMessage(&msg, SDL_HIDAPI_discovery.m_hwndMsg, 0, 0, PM_NOREMOVE)) {
slouken@12109
   319
            if (GetMessageA(&msg, SDL_HIDAPI_discovery.m_hwndMsg, 0, 0) != 0) {
slouken@12109
   320
                TranslateMessage(&msg);
slouken@12109
   321
                DispatchMessage(&msg);
slouken@12109
   322
            }
slouken@12109
   323
        }
slouken@12109
   324
    }
slouken@12109
   325
#endif
slouken@12363
   326
#endif /* __WIN32__ */
slouken@12109
   327
slouken@12109
   328
#if defined(__MACOSX__)
slouken@12109
   329
    if (SDL_HIDAPI_discovery.m_notificationPort) {
slouken@12109
   330
        struct { mach_msg_header_t hdr; char payload[ 4096 ]; } msg;
slouken@12109
   331
        while (mach_msg(&msg.hdr, MACH_RCV_MSG | MACH_RCV_TIMEOUT, 0, sizeof(msg), SDL_HIDAPI_discovery.m_notificationMach, 0, MACH_PORT_NULL) == KERN_SUCCESS) {
slouken@12109
   332
            IODispatchCalloutFromMessage(NULL, &msg.hdr, SDL_HIDAPI_discovery.m_notificationPort);
slouken@12109
   333
        }
slouken@12109
   334
    }
slouken@12109
   335
#endif
slouken@12109
   336
slouken@12109
   337
#if defined(SDL_USE_LIBUDEV)
slouken@12109
   338
    if (SDL_HIDAPI_discovery.m_nUdevFd >= 0) {
slouken@12109
   339
        /* Drain all notification events.
slouken@12109
   340
         * We don't expect a lot of device notifications so just
slouken@12109
   341
         * do a new discovery on any kind or number of notifications.
slouken@12109
   342
         * This could be made more restrictive if necessary.
slouken@12109
   343
         */
slouken@12109
   344
        for (;;) {
slouken@12109
   345
            struct pollfd PollUdev;
sezeroz@12181
   346
            struct udev_device *pUdevDevice;
slouken@12109
   347
slouken@12109
   348
            PollUdev.fd = SDL_HIDAPI_discovery.m_nUdevFd;
slouken@12109
   349
            PollUdev.events = POLLIN;
slouken@12109
   350
            if (poll(&PollUdev, 1, 0) != 1) {
slouken@12109
   351
                break;
slouken@12109
   352
            }
slouken@12109
   353
sezeroz@12181
   354
            SDL_HIDAPI_discovery.m_bHaveDevicesChanged = SDL_TRUE;
slouken@12109
   355
slouken@12203
   356
            pUdevDevice = usyms->udev_monitor_receive_device(SDL_HIDAPI_discovery.m_pUdevMonitor);
slouken@12109
   357
            if (pUdevDevice) {
slouken@12203
   358
                usyms->udev_device_unref(pUdevDevice);
slouken@12109
   359
            }
slouken@12109
   360
        }
slouken@12109
   361
    }
slouken@12109
   362
#endif
slouken@12109
   363
}
slouken@12109
   364
slouken@12109
   365
static void
slouken@12109
   366
HIDAPI_ShutdownDiscovery()
slouken@12109
   367
{
slouken@12109
   368
#if defined(__WIN32__)
slouken@12109
   369
    if (SDL_HIDAPI_discovery.m_hNotify)
slouken@12109
   370
        UnregisterDeviceNotification(SDL_HIDAPI_discovery.m_hNotify);
slouken@12109
   371
slouken@12109
   372
    if (SDL_HIDAPI_discovery.m_hwndMsg) {
slouken@12109
   373
        DestroyWindow(SDL_HIDAPI_discovery.m_hwndMsg);
slouken@12109
   374
    }
slouken@12109
   375
slouken@12109
   376
    UnregisterClassA(SDL_HIDAPI_discovery.m_wndClass.lpszClassName, SDL_HIDAPI_discovery.m_wndClass.hInstance);
slouken@12109
   377
#endif
slouken@12109
   378
slouken@12109
   379
#if defined(__MACOSX__)
slouken@12109
   380
    if (SDL_HIDAPI_discovery.m_notificationPort) {
slouken@12109
   381
        IONotificationPortDestroy(SDL_HIDAPI_discovery.m_notificationPort);
slouken@12109
   382
    }
slouken@12109
   383
#endif
slouken@12109
   384
slouken@12109
   385
#if defined(SDL_USE_LIBUDEV)
slouken@12203
   386
    if (usyms) {
sezeroz@12206
   387
        if (SDL_HIDAPI_discovery.m_pUdevMonitor) {
sezeroz@12206
   388
            usyms->udev_monitor_unref(SDL_HIDAPI_discovery.m_pUdevMonitor);
sezeroz@12206
   389
        }
sezeroz@12206
   390
        if (SDL_HIDAPI_discovery.m_pUdev) {
sezeroz@12206
   391
            usyms->udev_unref(SDL_HIDAPI_discovery.m_pUdev);
sezeroz@12206
   392
        }
slouken@12203
   393
        SDL_UDEV_ReleaseUdevSyms();
slouken@12203
   394
        usyms = NULL;
slouken@12109
   395
    }
slouken@12109
   396
#endif
slouken@12109
   397
}
slouken@12109
   398
slouken@12095
   399
static SDL_bool
slouken@13160
   400
HIDAPI_IsDeviceSupported(Uint16 vendor_id, Uint16 product_id, Uint16 version, const char *name)
slouken@12095
   401
{
slouken@12095
   402
    int i;
slouken@12095
   403
slouken@12095
   404
    for (i = 0; i < SDL_arraysize(SDL_HIDAPI_drivers); ++i) {
slouken@12095
   405
        SDL_HIDAPI_DeviceDriver *driver = SDL_HIDAPI_drivers[i];
slouken@13160
   406
        if (driver->enabled && driver->IsSupportedDevice(vendor_id, product_id, version, -1, name)) {
slouken@12095
   407
            return SDL_TRUE;
slouken@12095
   408
        }
slouken@12095
   409
    }
slouken@12095
   410
    return SDL_FALSE;
slouken@12095
   411
}
slouken@12095
   412
slouken@12088
   413
static SDL_HIDAPI_DeviceDriver *
slouken@12088
   414
HIDAPI_GetDeviceDriver(SDL_HIDAPI_Device *device)
slouken@12088
   415
{
slouken@12168
   416
    const Uint16 USAGE_PAGE_GENERIC_DESKTOP = 0x0001;
slouken@12168
   417
    const Uint16 USAGE_JOYSTICK = 0x0004;
slouken@12168
   418
    const Uint16 USAGE_GAMEPAD = 0x0005;
slouken@12168
   419
    const Uint16 USAGE_MULTIAXISCONTROLLER = 0x0008;
slouken@12088
   420
    int i;
slouken@12088
   421
slouken@12088
   422
    if (SDL_ShouldIgnoreJoystick(device->name, device->guid)) {
slouken@12088
   423
        return NULL;
slouken@12088
   424
    }
slouken@12088
   425
slouken@12168
   426
    if (device->usage_page && device->usage_page != USAGE_PAGE_GENERIC_DESKTOP) {
slouken@12168
   427
        return NULL;
slouken@12168
   428
    }
slouken@12168
   429
    if (device->usage && device->usage != USAGE_JOYSTICK && device->usage != USAGE_GAMEPAD && device->usage != USAGE_MULTIAXISCONTROLLER) {
slouken@12168
   430
        return NULL;
slouken@12168
   431
    }
slouken@12168
   432
slouken@12088
   433
    for (i = 0; i < SDL_arraysize(SDL_HIDAPI_drivers); ++i) {
slouken@12088
   434
        SDL_HIDAPI_DeviceDriver *driver = SDL_HIDAPI_drivers[i];
slouken@13160
   435
        if (driver->enabled && driver->IsSupportedDevice(device->vendor_id, device->product_id, device->version, device->interface_number, device->name)) {
slouken@12088
   436
            return driver;
slouken@12088
   437
        }
slouken@12088
   438
    }
slouken@12088
   439
    return NULL;
slouken@12088
   440
}
slouken@12088
   441
slouken@12088
   442
static SDL_HIDAPI_Device *
slouken@12896
   443
HIDAPI_GetJoystickByIndex(int device_index)
slouken@12088
   444
{
slouken@12088
   445
    SDL_HIDAPI_Device *device = SDL_HIDAPI_devices;
slouken@12088
   446
    while (device) {
slouken@12088
   447
        if (device->driver) {
slouken@12896
   448
            if (device_index == 0) {
slouken@12088
   449
                break;
slouken@12088
   450
            }
slouken@12896
   451
            --device_index;
slouken@12088
   452
        }
slouken@12088
   453
        device = device->next;
slouken@12088
   454
    }
slouken@12088
   455
    return device;
slouken@12088
   456
}
slouken@12088
   457
slouken@12088
   458
static SDL_HIDAPI_Device *
slouken@12088
   459
HIDAPI_GetJoystickByInfo(const char *path, Uint16 vendor_id, Uint16 product_id)
slouken@12088
   460
{
slouken@12088
   461
    SDL_HIDAPI_Device *device = SDL_HIDAPI_devices;
slouken@12088
   462
    while (device) {
slouken@12088
   463
        if (device->vendor_id == vendor_id && device->product_id == product_id &&
slouken@12088
   464
            SDL_strcmp(device->path, path) == 0) {
slouken@12088
   465
            break;
slouken@12088
   466
        }
slouken@12088
   467
        device = device->next;
slouken@12088
   468
    }
slouken@12088
   469
    return device;
slouken@12088
   470
}
slouken@12088
   471
slouken@12088
   472
static void SDLCALL
slouken@12088
   473
SDL_HIDAPIDriverHintChanged(void *userdata, const char *name, const char *oldValue, const char *hint)
slouken@12088
   474
{
slouken@12088
   475
    int i;
slouken@12088
   476
    SDL_HIDAPI_Device *device = SDL_HIDAPI_devices;
slouken@13244
   477
    SDL_bool enabled = SDL_GetStringBoolean(hint, SDL_TRUE);
slouken@12088
   478
slouken@12088
   479
    if (SDL_strcmp(name, SDL_HINT_JOYSTICK_HIDAPI) == 0) {
slouken@12088
   480
        for (i = 0; i < SDL_arraysize(SDL_HIDAPI_drivers); ++i) {
slouken@12088
   481
            SDL_HIDAPI_DeviceDriver *driver = SDL_HIDAPI_drivers[i];
slouken@12088
   482
            driver->enabled = SDL_GetHintBoolean(driver->hint, enabled);
slouken@12088
   483
        }
slouken@12088
   484
    } else {
slouken@12088
   485
        for (i = 0; i < SDL_arraysize(SDL_HIDAPI_drivers); ++i) {
slouken@12088
   486
            SDL_HIDAPI_DeviceDriver *driver = SDL_HIDAPI_drivers[i];
slouken@12088
   487
            if (SDL_strcmp(name, driver->hint) == 0) {
slouken@12088
   488
                driver->enabled = enabled;
slouken@12088
   489
                break;
slouken@12088
   490
            }
slouken@12088
   491
        }
slouken@12088
   492
    }
slouken@12088
   493
slouken@12953
   494
    SDL_HIDAPI_numdrivers = 0;
slouken@12953
   495
    for (i = 0; i < SDL_arraysize(SDL_HIDAPI_drivers); ++i) {
slouken@12953
   496
        SDL_HIDAPI_DeviceDriver *driver = SDL_HIDAPI_drivers[i];
slouken@12953
   497
        if (driver->enabled) {
slouken@12953
   498
            ++SDL_HIDAPI_numdrivers;
slouken@12953
   499
        }
slouken@12953
   500
    }
slouken@12953
   501
slouken@12088
   502
    /* Update device list if driver availability changes */
slouken@12088
   503
    while (device) {
slouken@12088
   504
        if (device->driver) {
slouken@12088
   505
            if (!device->driver->enabled) {
slouken@12896
   506
                device->driver = NULL;
slouken@12896
   507
slouken@12896
   508
                --SDL_HIDAPI_numjoysticks;
slouken@12896
   509
slouken@12896
   510
                SDL_PrivateJoystickRemoved(device->instance_id);
slouken@12088
   511
            }
slouken@12088
   512
        } else {
slouken@12088
   513
            device->driver = HIDAPI_GetDeviceDriver(device);
slouken@12088
   514
            if (device->driver) {
slouken@12896
   515
                device->instance_id = SDL_GetNextJoystickInstanceID();
slouken@12896
   516
slouken@12896
   517
                ++SDL_HIDAPI_numjoysticks;
slouken@12896
   518
slouken@12896
   519
                SDL_PrivateJoystickAdded(device->instance_id);
slouken@12088
   520
            }
slouken@12088
   521
        }
slouken@12088
   522
        device = device->next;
slouken@12088
   523
    }
slouken@12088
   524
}
slouken@12088
   525
slouken@12088
   526
static void HIDAPI_JoystickDetect(void);
slouken@12088
   527
slouken@12088
   528
static int
slouken@12088
   529
HIDAPI_JoystickInit(void)
slouken@12088
   530
{
slouken@12088
   531
    int i;
slouken@12088
   532
aeikum@12972
   533
    if (initialized) {
aeikum@12972
   534
        return 0;
aeikum@12972
   535
    }
aeikum@12972
   536
slouken@12088
   537
    if (hid_init() < 0) {
slouken@12088
   538
        SDL_SetError("Couldn't initialize hidapi");
slouken@12088
   539
        return -1;
slouken@12088
   540
    }
slouken@12088
   541
slouken@12088
   542
    for (i = 0; i < SDL_arraysize(SDL_HIDAPI_drivers); ++i) {
slouken@12088
   543
        SDL_HIDAPI_DeviceDriver *driver = SDL_HIDAPI_drivers[i];
slouken@12088
   544
        SDL_AddHintCallback(driver->hint, SDL_HIDAPIDriverHintChanged, NULL);
slouken@12088
   545
    }
slouken@12088
   546
    SDL_AddHintCallback(SDL_HINT_JOYSTICK_HIDAPI,
slouken@12088
   547
                        SDL_HIDAPIDriverHintChanged, NULL);
slouken@12109
   548
    HIDAPI_InitializeDiscovery();
slouken@12088
   549
    HIDAPI_JoystickDetect();
aeikum@12972
   550
aeikum@12972
   551
    initialized = SDL_TRUE;
aeikum@12972
   552
slouken@12088
   553
    return 0;
slouken@12088
   554
}
slouken@12088
   555
slouken@12088
   556
static int
slouken@12088
   557
HIDAPI_JoystickGetCount(void)
slouken@12088
   558
{
slouken@12088
   559
    return SDL_HIDAPI_numjoysticks;
slouken@12088
   560
}
slouken@12088
   561
slouken@12088
   562
static void
slouken@12088
   563
HIDAPI_AddDevice(struct hid_device_info *info)
slouken@12088
   564
{
slouken@12088
   565
    SDL_HIDAPI_Device *device;
slouken@12088
   566
    SDL_HIDAPI_Device *curr, *last = NULL;
slouken@12088
   567
slouken@12088
   568
    for (curr = SDL_HIDAPI_devices, last = NULL; curr; last = curr, curr = curr->next) {
slouken@12088
   569
        continue;
slouken@12088
   570
    }
slouken@12088
   571
slouken@12088
   572
    device = (SDL_HIDAPI_Device *)SDL_calloc(1, sizeof(*device));
slouken@12088
   573
    if (!device) {
slouken@12088
   574
        return;
slouken@12088
   575
    }
slouken@12896
   576
    device->instance_id = -1;
slouken@12088
   577
    device->seen = SDL_TRUE;
slouken@12088
   578
    device->vendor_id = info->vendor_id;
slouken@12088
   579
    device->product_id = info->product_id;
slouken@12162
   580
    device->version = info->release_number;
slouken@12088
   581
    device->interface_number = info->interface_number;
slouken@12088
   582
    device->usage_page = info->usage_page;
slouken@12088
   583
    device->usage = info->usage;
slouken@12088
   584
    {
slouken@12088
   585
        /* FIXME: Is there any way to tell whether this is a Bluetooth device? */
slouken@12088
   586
        const Uint16 vendor = device->vendor_id;
slouken@12088
   587
        const Uint16 product = device->product_id;
slouken@12115
   588
        const Uint16 version = device->version;
slouken@12088
   589
        Uint16 *guid16 = (Uint16 *)device->guid.data;
slouken@12088
   590
slouken@12088
   591
        *guid16++ = SDL_SwapLE16(SDL_HARDWARE_BUS_USB);
slouken@12088
   592
        *guid16++ = 0;
slouken@12088
   593
        *guid16++ = SDL_SwapLE16(vendor);
slouken@12088
   594
        *guid16++ = 0;
slouken@12088
   595
        *guid16++ = SDL_SwapLE16(product);
slouken@12088
   596
        *guid16++ = 0;
slouken@12088
   597
        *guid16++ = SDL_SwapLE16(version);
slouken@12088
   598
        *guid16++ = 0;
slouken@12088
   599
slouken@12088
   600
        /* Note that this is a HIDAPI device for special handling elsewhere */
slouken@12088
   601
        device->guid.data[14] = 'h';
slouken@12088
   602
        device->guid.data[15] = 0;
slouken@12088
   603
    }
slouken@12088
   604
slouken@12185
   605
    /* Need the device name before getting the driver to know whether to ignore this device */
slouken@12088
   606
    if (!device->name && info->manufacturer_string && info->product_string) {
slouken@12088
   607
        char *manufacturer_string = SDL_iconv_string("UTF-8", "WCHAR_T", (char*)info->manufacturer_string, (SDL_wcslen(info->manufacturer_string)+1)*sizeof(wchar_t));
slouken@12088
   608
        char *product_string = SDL_iconv_string("UTF-8", "WCHAR_T", (char*)info->product_string, (SDL_wcslen(info->product_string)+1)*sizeof(wchar_t));
slouken@12088
   609
        if (!manufacturer_string && !product_string) {
slouken@12088
   610
            if (sizeof(wchar_t) == sizeof(Uint16)) {
slouken@12088
   611
                manufacturer_string = SDL_iconv_string("UTF-8", "UCS-2-INTERNAL", (char*)info->manufacturer_string, (SDL_wcslen(info->manufacturer_string)+1)*sizeof(wchar_t));
slouken@12088
   612
                product_string = SDL_iconv_string("UTF-8", "UCS-2-INTERNAL", (char*)info->product_string, (SDL_wcslen(info->product_string)+1)*sizeof(wchar_t));
slouken@12088
   613
            } else if (sizeof(wchar_t) == sizeof(Uint32)) {
slouken@12088
   614
                manufacturer_string = SDL_iconv_string("UTF-8", "UCS-4-INTERNAL", (char*)info->manufacturer_string, (SDL_wcslen(info->manufacturer_string)+1)*sizeof(wchar_t));
slouken@12088
   615
                product_string = SDL_iconv_string("UTF-8", "UCS-4-INTERNAL", (char*)info->product_string, (SDL_wcslen(info->product_string)+1)*sizeof(wchar_t));
slouken@12088
   616
            }
slouken@12088
   617
        }
slouken@12088
   618
        if (manufacturer_string && product_string) {
slouken@13332
   619
            size_t name_size;
slouken@13332
   620
slouken@13332
   621
            if (SDL_strcmp(manufacturer_string, "Performance Designed Products") == 0) {
slouken@13332
   622
                /* Shorten this so controller names are more manageable */
slouken@13332
   623
                SDL_memcpy(manufacturer_string, "PDP", 4);
slouken@13332
   624
            }
slouken@13332
   625
slouken@13332
   626
            name_size = (SDL_strlen(manufacturer_string) + 1 + SDL_strlen(product_string) + 1);
slouken@12088
   627
            device->name = (char *)SDL_malloc(name_size);
slouken@12088
   628
            if (device->name) {
slouken@13326
   629
                if (SDL_strncasecmp(manufacturer_string, product_string, SDL_strlen(manufacturer_string)) == 0) {
slouken@13326
   630
                    SDL_strlcpy(device->name, product_string, name_size);
slouken@13326
   631
                } else {
slouken@13326
   632
                    SDL_snprintf(device->name, name_size, "%s %s", manufacturer_string, product_string);
slouken@13326
   633
                }
slouken@12088
   634
            }
slouken@12088
   635
        }
slouken@12088
   636
        if (manufacturer_string) {
slouken@12088
   637
            SDL_free(manufacturer_string);
slouken@12088
   638
        }
slouken@12088
   639
        if (product_string) {
slouken@12088
   640
            SDL_free(product_string);
slouken@12088
   641
        }
slouken@12088
   642
    }
slouken@12088
   643
    if (!device->name) {
slouken@12088
   644
        size_t name_size = (6 + 1 + 6 + 1);
slouken@12088
   645
        device->name = (char *)SDL_malloc(name_size);
slouken@12088
   646
        if (!device->name) {
slouken@12088
   647
            SDL_free(device);
slouken@12088
   648
            return;
slouken@12088
   649
        }
slouken@12088
   650
        SDL_snprintf(device->name, name_size, "0x%.4x/0x%.4x", info->vendor_id, info->product_id);
slouken@12088
   651
    }
slouken@12088
   652
slouken@12185
   653
    device->driver = HIDAPI_GetDeviceDriver(device);
slouken@12185
   654
slouken@12185
   655
    if (device->driver) {
slouken@12185
   656
        const char *name = device->driver->GetDeviceName(device->vendor_id, device->product_id);
slouken@12185
   657
        if (name) {
slouken@12185
   658
            SDL_free(device->name);
slouken@12185
   659
            device->name = SDL_strdup(name);
slouken@12185
   660
        }
slouken@12185
   661
    }
slouken@12185
   662
slouken@12088
   663
    device->path = SDL_strdup(info->path);
slouken@12088
   664
    if (!device->path) {
slouken@12088
   665
        SDL_free(device->name);
slouken@12088
   666
        SDL_free(device);
slouken@12088
   667
        return;
slouken@12088
   668
    }
slouken@12088
   669
slouken@12088
   670
#ifdef DEBUG_HIDAPI
slouken@12185
   671
    SDL_Log("Adding HIDAPI device '%s' VID 0x%.4x, PID 0x%.4x, version %d, interface %d, usage page 0x%.4x, usage 0x%.4x, driver = %s\n", device->name, device->vendor_id, device->product_id, device->version, device->interface_number, device->usage_page, device->usage, device->driver ? device->driver->hint : "NONE");
slouken@12088
   672
#endif
slouken@12088
   673
slouken@12088
   674
    /* Add it to the list */
slouken@12088
   675
    if (last) {
slouken@12088
   676
        last->next = device;
slouken@12088
   677
    } else {
slouken@12088
   678
        SDL_HIDAPI_devices = device;
slouken@12088
   679
    }
slouken@12088
   680
slouken@12088
   681
    if (device->driver) {
slouken@12896
   682
        /* It's a joystick! */
slouken@12896
   683
        device->instance_id = SDL_GetNextJoystickInstanceID();
slouken@12896
   684
slouken@12896
   685
        ++SDL_HIDAPI_numjoysticks;
slouken@12896
   686
slouken@12896
   687
        SDL_PrivateJoystickAdded(device->instance_id);
slouken@12088
   688
    }
slouken@12088
   689
}
slouken@12088
   690
slouken@12088
   691
slouken@12088
   692
static void
slouken@12088
   693
HIDAPI_DelDevice(SDL_HIDAPI_Device *device, SDL_bool send_event)
slouken@12088
   694
{
slouken@12088
   695
    SDL_HIDAPI_Device *curr, *last;
slouken@12088
   696
    for (curr = SDL_HIDAPI_devices, last = NULL; curr; last = curr, curr = curr->next) {
slouken@12088
   697
        if (curr == device) {
slouken@12088
   698
            if (last) {
slouken@12088
   699
                last->next = curr->next;
slouken@12088
   700
            } else {
slouken@12088
   701
                SDL_HIDAPI_devices = curr->next;
slouken@12088
   702
            }
slouken@12088
   703
slouken@12896
   704
            if (device->driver && send_event) {
slouken@12896
   705
                /* Need to decrement the joystick count before we post the event */
slouken@12896
   706
                --SDL_HIDAPI_numjoysticks;
slouken@12896
   707
slouken@12896
   708
                SDL_PrivateJoystickRemoved(device->instance_id);
slouken@12088
   709
            }
slouken@12088
   710
slouken@12088
   711
            SDL_free(device->name);
slouken@12088
   712
            SDL_free(device->path);
slouken@12088
   713
            SDL_free(device);
slouken@12088
   714
            return;
slouken@12088
   715
        }
slouken@12088
   716
    }
slouken@12088
   717
}
slouken@12088
   718
slouken@12088
   719
static void
slouken@12088
   720
HIDAPI_UpdateDeviceList(void)
slouken@12088
   721
{
slouken@12088
   722
    SDL_HIDAPI_Device *device;
slouken@12088
   723
    struct hid_device_info *devs, *info;
slouken@12088
   724
slouken@12088
   725
    /* Prepare the existing device list */
slouken@12088
   726
    device = SDL_HIDAPI_devices;
slouken@12088
   727
    while (device) {
slouken@12088
   728
        device->seen = SDL_FALSE;
slouken@12088
   729
        device = device->next;
slouken@12088
   730
    }
slouken@12088
   731
slouken@12088
   732
    /* Enumerate the devices */
slouken@12953
   733
    if (SDL_HIDAPI_numdrivers > 0) {
slouken@12953
   734
        devs = hid_enumerate(0, 0);
slouken@12953
   735
        if (devs) {
slouken@12953
   736
            for (info = devs; info; info = info->next) {
slouken@12953
   737
                device = HIDAPI_GetJoystickByInfo(info->path, info->vendor_id, info->product_id);
slouken@12953
   738
                if (device) {
slouken@12953
   739
                    device->seen = SDL_TRUE;
slouken@12953
   740
                } else {
slouken@12953
   741
                    HIDAPI_AddDevice(info);
slouken@12953
   742
                }
slouken@12088
   743
            }
slouken@12953
   744
            hid_free_enumeration(devs);
slouken@12088
   745
        }
slouken@12088
   746
    }
slouken@12088
   747
slouken@12088
   748
    /* Remove any devices that weren't seen */
slouken@12088
   749
    device = SDL_HIDAPI_devices;
slouken@12088
   750
    while (device) {
slouken@12088
   751
        SDL_HIDAPI_Device *next = device->next;
slouken@12088
   752
slouken@12088
   753
        if (!device->seen) {
slouken@12088
   754
            HIDAPI_DelDevice(device, SDL_TRUE);
slouken@12088
   755
        }
slouken@12088
   756
        device = next;
slouken@12088
   757
    }
slouken@12088
   758
}
slouken@12088
   759
slouken@12088
   760
SDL_bool
slouken@13160
   761
HIDAPI_IsDevicePresent(Uint16 vendor_id, Uint16 product_id, Uint16 version, const char *name)
slouken@12088
   762
{
slouken@12088
   763
    SDL_HIDAPI_Device *device;
slouken@12088
   764
slouken@12973
   765
    /* Make sure we're initialized, as this could be called from other drivers during startup */
slouken@12973
   766
    if (HIDAPI_JoystickInit() < 0) {
slouken@12973
   767
        return SDL_FALSE;
slouken@12973
   768
    }
slouken@12973
   769
slouken@12095
   770
    /* Don't update the device list for devices we know aren't supported */
slouken@13160
   771
    if (!HIDAPI_IsDeviceSupported(vendor_id, product_id, version, name)) {
slouken@12095
   772
        return SDL_FALSE;
slouken@12095
   773
    }
slouken@12095
   774
slouken@12088
   775
    /* Make sure the device list is completely up to date when we check for device presence */
slouken@12088
   776
    HIDAPI_UpdateDeviceList();
slouken@12088
   777
slouken@13160
   778
    /* Note that this isn't a perfect check - there may be multiple devices with 0 VID/PID,
slouken@13160
   779
       or a different name than we have it listed here, etc, but if we support the device
slouken@13160
   780
       and we have something similar in our device list, mark it as present.
slouken@13160
   781
     */
slouken@12088
   782
    device = SDL_HIDAPI_devices;
slouken@12088
   783
    while (device) {
slouken@12088
   784
        if (device->vendor_id == vendor_id && device->product_id == product_id && device->driver) {
slouken@12088
   785
            return SDL_TRUE;
slouken@12088
   786
        }
slouken@12088
   787
        device = device->next;
slouken@12088
   788
    }
slouken@12088
   789
    return SDL_FALSE;
slouken@12088
   790
}
slouken@12088
   791
slouken@12088
   792
static void
slouken@12088
   793
HIDAPI_JoystickDetect(void)
slouken@12088
   794
{
slouken@12109
   795
    HIDAPI_UpdateDiscovery();
slouken@12109
   796
    if (SDL_HIDAPI_discovery.m_bHaveDevicesChanged) {
slouken@12109
   797
        /* FIXME: We probably need to schedule an update in a few seconds as well */
slouken@12088
   798
        HIDAPI_UpdateDeviceList();
slouken@12109
   799
        SDL_HIDAPI_discovery.m_bHaveDevicesChanged = SDL_FALSE;
slouken@12088
   800
    }
slouken@12088
   801
}
slouken@12088
   802
slouken@12088
   803
static const char *
slouken@12088
   804
HIDAPI_JoystickGetDeviceName(int device_index)
slouken@12088
   805
{
slouken@12896
   806
    return HIDAPI_GetJoystickByIndex(device_index)->name;
slouken@12088
   807
}
slouken@12088
   808
slouken@12359
   809
static int
slouken@12359
   810
HIDAPI_JoystickGetDevicePlayerIndex(int device_index)
slouken@12359
   811
{
slouken@12896
   812
    return -1;
slouken@12359
   813
}
slouken@12359
   814
slouken@12088
   815
static SDL_JoystickGUID
slouken@12088
   816
HIDAPI_JoystickGetDeviceGUID(int device_index)
slouken@12088
   817
{
slouken@12896
   818
    return HIDAPI_GetJoystickByIndex(device_index)->guid;
slouken@12088
   819
}
slouken@12088
   820
slouken@12088
   821
static SDL_JoystickID
slouken@12088
   822
HIDAPI_JoystickGetDeviceInstanceID(int device_index)
slouken@12088
   823
{
slouken@12896
   824
    return HIDAPI_GetJoystickByIndex(device_index)->instance_id;
slouken@12088
   825
}
slouken@12088
   826
slouken@12088
   827
static int
slouken@12088
   828
HIDAPI_JoystickOpen(SDL_Joystick * joystick, int device_index)
slouken@12088
   829
{
slouken@12896
   830
    SDL_HIDAPI_Device *device = HIDAPI_GetJoystickByIndex(device_index);
slouken@12896
   831
    struct joystick_hwdata *hwdata;
slouken@12896
   832
slouken@12896
   833
    hwdata = (struct joystick_hwdata *)SDL_calloc(1, sizeof(*hwdata));
slouken@12896
   834
    if (!hwdata) {
slouken@12896
   835
        return SDL_OutOfMemory();
slouken@12896
   836
    }
slouken@12088
   837
slouken@12896
   838
    hwdata->driver = device->driver;
slouken@12896
   839
    hwdata->dev = hid_open_path(device->path, 0);
slouken@12896
   840
    if (!hwdata->dev) {
slouken@12896
   841
        SDL_free(hwdata);
slouken@12896
   842
        return SDL_SetError("Couldn't open HID device %s", device->path);
slouken@12896
   843
    }
slouken@12896
   844
    hwdata->mutex = SDL_CreateMutex();
slouken@12896
   845
slouken@12896
   846
    if (!device->driver->Init(joystick, hwdata->dev, device->vendor_id, device->product_id, &hwdata->context)) {
slouken@12896
   847
        hid_close(hwdata->dev);
slouken@12896
   848
        SDL_free(hwdata);
slouken@12088
   849
        return -1;
slouken@12088
   850
    }
slouken@12088
   851
slouken@12896
   852
    joystick->hwdata = hwdata;
slouken@12088
   853
    return 0;
slouken@12088
   854
}
slouken@12088
   855
slouken@12088
   856
static int
slouken@12088
   857
HIDAPI_JoystickRumble(SDL_Joystick * joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble, Uint32 duration_ms)
slouken@12088
   858
{
slouken@12896
   859
    struct joystick_hwdata *hwdata = joystick->hwdata;
slouken@12896
   860
    SDL_HIDAPI_DeviceDriver *driver = hwdata->driver;
slouken@12277
   861
    int result;
slouken@12277
   862
slouken@12896
   863
    SDL_LockMutex(hwdata->mutex);
slouken@12896
   864
    result = driver->Rumble(joystick, hwdata->dev, hwdata->context, low_frequency_rumble, high_frequency_rumble, duration_ms);
slouken@12896
   865
    SDL_UnlockMutex(hwdata->mutex);
slouken@12277
   866
    return result;
slouken@12088
   867
}
slouken@12088
   868
slouken@12088
   869
static void
slouken@12088
   870
HIDAPI_JoystickUpdate(SDL_Joystick * joystick)
slouken@12088
   871
{
slouken@12896
   872
    struct joystick_hwdata *hwdata = joystick->hwdata;
slouken@12896
   873
    SDL_HIDAPI_DeviceDriver *driver = hwdata->driver;
slouken@12896
   874
    SDL_bool succeeded;
slouken@12896
   875
slouken@12896
   876
    SDL_LockMutex(hwdata->mutex);
slouken@12896
   877
    succeeded = driver->Update(joystick, hwdata->dev, hwdata->context);
slouken@12896
   878
    SDL_UnlockMutex(hwdata->mutex);
slouken@12896
   879
    
slouken@12896
   880
    if (!succeeded) {
slouken@12896
   881
        SDL_HIDAPI_Device *device;
slouken@12896
   882
        for (device = SDL_HIDAPI_devices; device; device = device->next) {
slouken@12896
   883
            if (device->instance_id == joystick->instance_id) {
slouken@12896
   884
                HIDAPI_DelDevice(device, SDL_TRUE);
slouken@12896
   885
                break;
slouken@12896
   886
            }
slouken@12896
   887
        }
slouken@12896
   888
    }
slouken@12088
   889
}
slouken@12088
   890
slouken@12088
   891
static void
slouken@12088
   892
HIDAPI_JoystickClose(SDL_Joystick * joystick)
slouken@12088
   893
{
slouken@12896
   894
    struct joystick_hwdata *hwdata = joystick->hwdata;
slouken@12896
   895
    SDL_HIDAPI_DeviceDriver *driver = hwdata->driver;
slouken@12896
   896
    driver->Quit(joystick, hwdata->dev, hwdata->context);
slouken@12896
   897
slouken@12896
   898
    hid_close(hwdata->dev);
slouken@12896
   899
    SDL_DestroyMutex(hwdata->mutex);
slouken@12896
   900
    SDL_free(hwdata);
slouken@12088
   901
    joystick->hwdata = NULL;
slouken@12088
   902
}
slouken@12088
   903
slouken@12088
   904
static void
slouken@12088
   905
HIDAPI_JoystickQuit(void)
slouken@12088
   906
{
slouken@12088
   907
    int i;
slouken@12088
   908
slouken@12110
   909
    HIDAPI_ShutdownDiscovery();
slouken@12110
   910
slouken@12088
   911
    while (SDL_HIDAPI_devices) {
slouken@12088
   912
        HIDAPI_DelDevice(SDL_HIDAPI_devices, SDL_FALSE);
slouken@12088
   913
    }
slouken@12088
   914
    for (i = 0; i < SDL_arraysize(SDL_HIDAPI_drivers); ++i) {
slouken@12088
   915
        SDL_HIDAPI_DeviceDriver *driver = SDL_HIDAPI_drivers[i];
slouken@12088
   916
        SDL_DelHintCallback(driver->hint, SDL_HIDAPIDriverHintChanged, NULL);
slouken@12088
   917
    }
slouken@12088
   918
    SDL_DelHintCallback(SDL_HINT_JOYSTICK_HIDAPI,
slouken@12088
   919
                        SDL_HIDAPIDriverHintChanged, NULL);
slouken@12088
   920
    SDL_HIDAPI_numjoysticks = 0;
slouken@12088
   921
slouken@12088
   922
    hid_exit();
aeikum@12972
   923
aeikum@12972
   924
    initialized = SDL_FALSE;
slouken@12088
   925
}
slouken@12088
   926
slouken@12088
   927
SDL_JoystickDriver SDL_HIDAPI_JoystickDriver =
slouken@12088
   928
{
slouken@12088
   929
    HIDAPI_JoystickInit,
slouken@12088
   930
    HIDAPI_JoystickGetCount,
slouken@12088
   931
    HIDAPI_JoystickDetect,
slouken@12088
   932
    HIDAPI_JoystickGetDeviceName,
slouken@12359
   933
    HIDAPI_JoystickGetDevicePlayerIndex,
slouken@12088
   934
    HIDAPI_JoystickGetDeviceGUID,
slouken@12088
   935
    HIDAPI_JoystickGetDeviceInstanceID,
slouken@12088
   936
    HIDAPI_JoystickOpen,
slouken@12088
   937
    HIDAPI_JoystickRumble,
slouken@12088
   938
    HIDAPI_JoystickUpdate,
slouken@12088
   939
    HIDAPI_JoystickClose,
slouken@12088
   940
    HIDAPI_JoystickQuit,
slouken@12088
   941
};
slouken@12088
   942
slouken@12088
   943
#endif /* SDL_JOYSTICK_HIDAPI */
slouken@12088
   944
slouken@12088
   945
/* vi: set ts=4 sw=4 expandtab: */