src/joystick/hidapi/SDL_hidapijoystick.c
author Sam Lantinga <slouken@libsdl.org>
Fri, 20 Mar 2020 21:05:07 -0700
changeset 13662 d32d92e782ab
parent 13633 340324c76848
child 13696 ea20a7434b98
permissions -rw-r--r--
Allow Valve devices in driver check, we know they're well behaved controllers
slouken@12088
     1
/*
slouken@12088
     2
  Simple DirectMedia Layer
slouken@13422
     3
  Copyright (C) 1997-2020 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@13354
    25
#include "SDL_assert.h"
slouken@13413
    26
#include "SDL_atomic.h"
slouken@12088
    27
#include "SDL_endian.h"
slouken@12088
    28
#include "SDL_hints.h"
slouken@12088
    29
#include "SDL_log.h"
slouken@12109
    30
#include "SDL_thread.h"
slouken@12088
    31
#include "SDL_timer.h"
slouken@12088
    32
#include "SDL_joystick.h"
slouken@12088
    33
#include "../SDL_sysjoystick.h"
slouken@12088
    34
#include "SDL_hidapijoystick_c.h"
slouken@13481
    35
#include "SDL_hidapi_rumble.h"
slouken@13244
    36
#include "../../SDL_hints_c.h"
slouken@12088
    37
slouken@12109
    38
#if defined(__WIN32__)
slouken@12109
    39
#include "../../core/windows/SDL_windows.h"
slouken@13633
    40
#include "../windows/SDL_rawinputjoystick_c.h"
slouken@12109
    41
#endif
slouken@12109
    42
slouken@12109
    43
#if defined(__MACOSX__)
slouken@12109
    44
#include <CoreFoundation/CoreFoundation.h>
slouken@12109
    45
#include <mach/mach.h>
slouken@12109
    46
#include <IOKit/IOKitLib.h>
rofferom@13562
    47
#include <IOKit/hid/IOHIDDevice.h>
slouken@12109
    48
#include <IOKit/usb/USBSpec.h>
slouken@12109
    49
#endif
slouken@12109
    50
slouken@12109
    51
#if defined(__LINUX__)
slouken@12109
    52
#include "../../core/linux/SDL_udev.h"
slouken@12109
    53
#ifdef SDL_USE_LIBUDEV
slouken@12109
    54
#include <poll.h>
slouken@12109
    55
#endif
slouken@12109
    56
#endif
slouken@12088
    57
slouken@12896
    58
struct joystick_hwdata
slouken@12896
    59
{
slouken@13354
    60
    SDL_HIDAPI_Device *device;
slouken@12896
    61
};
slouken@12896
    62
slouken@12088
    63
static SDL_HIDAPI_DeviceDriver *SDL_HIDAPI_drivers[] = {
slouken@12088
    64
#ifdef SDL_JOYSTICK_HIDAPI_PS4
slouken@12088
    65
    &SDL_HIDAPI_DriverPS4,
slouken@12088
    66
#endif
slouken@12088
    67
#ifdef SDL_JOYSTICK_HIDAPI_STEAM
slouken@12088
    68
    &SDL_HIDAPI_DriverSteam,
slouken@12088
    69
#endif
slouken@12088
    70
#ifdef SDL_JOYSTICK_HIDAPI_SWITCH
slouken@12088
    71
    &SDL_HIDAPI_DriverSwitch,
slouken@12088
    72
#endif
slouken@12088
    73
#ifdef SDL_JOYSTICK_HIDAPI_XBOX360
slouken@12088
    74
    &SDL_HIDAPI_DriverXbox360,
slouken@13355
    75
    &SDL_HIDAPI_DriverXbox360W,
slouken@12088
    76
#endif
slouken@12088
    77
#ifdef SDL_JOYSTICK_HIDAPI_XBOXONE
slouken@12088
    78
    &SDL_HIDAPI_DriverXboxOne,
slouken@12088
    79
#endif
slouken@13356
    80
#ifdef SDL_JOYSTICK_HIDAPI_GAMECUBE
slouken@13356
    81
    &SDL_HIDAPI_DriverGameCube,
slouken@13356
    82
#endif
slouken@12088
    83
};
slouken@12953
    84
static int SDL_HIDAPI_numdrivers = 0;
slouken@13413
    85
static SDL_SpinLock SDL_HIDAPI_spinlock;
slouken@12088
    86
static SDL_HIDAPI_Device *SDL_HIDAPI_devices;
slouken@12088
    87
static int SDL_HIDAPI_numjoysticks = 0;
aeikum@12972
    88
static SDL_bool initialized = SDL_FALSE;
slouken@13354
    89
static SDL_bool shutting_down = SDL_FALSE;
slouken@12109
    90
slouken@12203
    91
#if defined(SDL_USE_LIBUDEV)
slouken@12203
    92
static const SDL_UDEV_Symbols * usyms = NULL;
slouken@12203
    93
#endif
slouken@12203
    94
slouken@12109
    95
static struct
slouken@12109
    96
{
slouken@12109
    97
    SDL_bool m_bHaveDevicesChanged;
slouken@12109
    98
    SDL_bool m_bCanGetNotifications;
slouken@12109
    99
    Uint32 m_unLastDetect;
slouken@12109
   100
slouken@12109
   101
#if defined(__WIN32__)
slouken@12110
   102
    SDL_threadID m_nThreadID;
slouken@12109
   103
    WNDCLASSEXA m_wndClass;
slouken@12109
   104
    HWND m_hwndMsg;
slouken@12109
   105
    HDEVNOTIFY m_hNotify;
slouken@12109
   106
    double m_flLastWin32MessageCheck;
slouken@12109
   107
#endif
slouken@12109
   108
slouken@12109
   109
#if defined(__MACOSX__)
slouken@12109
   110
    IONotificationPortRef m_notificationPort;
slouken@12109
   111
    mach_port_t m_notificationMach;
slouken@12109
   112
#endif
slouken@12109
   113
slouken@12109
   114
#if defined(SDL_USE_LIBUDEV)
slouken@12109
   115
    struct udev *m_pUdev;
slouken@12109
   116
    struct udev_monitor *m_pUdevMonitor;
slouken@12109
   117
    int m_nUdevFd;
slouken@12109
   118
#endif
slouken@12109
   119
} SDL_HIDAPI_discovery;
slouken@12109
   120
slouken@12109
   121
slouken@12109
   122
#ifdef __WIN32__
slouken@12109
   123
struct _DEV_BROADCAST_HDR
slouken@12109
   124
{
slouken@12109
   125
    DWORD       dbch_size;
slouken@12109
   126
    DWORD       dbch_devicetype;
slouken@12109
   127
    DWORD       dbch_reserved;
slouken@12109
   128
};
slouken@12109
   129
slouken@12109
   130
typedef struct _DEV_BROADCAST_DEVICEINTERFACE_A
slouken@12109
   131
{
slouken@12109
   132
    DWORD       dbcc_size;
slouken@12109
   133
    DWORD       dbcc_devicetype;
slouken@12109
   134
    DWORD       dbcc_reserved;
slouken@12109
   135
    GUID        dbcc_classguid;
slouken@12109
   136
    char        dbcc_name[ 1 ];
slouken@12109
   137
} DEV_BROADCAST_DEVICEINTERFACE_A, *PDEV_BROADCAST_DEVICEINTERFACE_A;
slouken@12109
   138
slouken@12109
   139
typedef struct  _DEV_BROADCAST_HDR      DEV_BROADCAST_HDR;
slouken@12109
   140
#define DBT_DEVICEARRIVAL               0x8000  /* system detected a new device */
slouken@12162
   141
#define DBT_DEVICEREMOVECOMPLETE        0x8004  /* device was removed from the system */
slouken@12109
   142
#define DBT_DEVTYP_DEVICEINTERFACE      0x00000005  /* device interface class */
slouken@12109
   143
#define DBT_DEVNODES_CHANGED            0x0007
slouken@12109
   144
#define DBT_CONFIGCHANGED               0x0018
slouken@12109
   145
#define DBT_DEVICETYPESPECIFIC          0x8005  /* type specific event */
slouken@12111
   146
#define DBT_DEVINSTSTARTED              0x8008  /* device installed and started */
slouken@12109
   147
slouken@12109
   148
#include <initguid.h>
slouken@12109
   149
DEFINE_GUID(GUID_DEVINTERFACE_USB_DEVICE, 0xA5DCBF10L, 0x6530, 0x11D2, 0x90, 0x1F, 0x00, 0xC0, 0x4F, 0xB9, 0x51, 0xED);
slouken@12109
   150
slouken@12109
   151
static LRESULT CALLBACK ControllerWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
slouken@12109
   152
{
slouken@12109
   153
    switch (message) {
slouken@12109
   154
    case WM_DEVICECHANGE:
slouken@12109
   155
        switch (wParam) {
slouken@12109
   156
        case DBT_DEVICEARRIVAL:
slouken@12162
   157
        case DBT_DEVICEREMOVECOMPLETE:
slouken@12109
   158
            if (((DEV_BROADCAST_HDR*)lParam)->dbch_devicetype == DBT_DEVTYP_DEVICEINTERFACE) {
slouken@12110
   159
                SDL_HIDAPI_discovery.m_bHaveDevicesChanged = SDL_TRUE;
slouken@12109
   160
            }
slouken@12109
   161
            break;
slouken@12109
   162
        }
slouken@12113
   163
        return TRUE;
slouken@12109
   164
    }
slouken@12109
   165
slouken@12109
   166
    return DefWindowProc(hwnd, message, wParam, lParam);
slouken@12109
   167
}
slouken@12109
   168
#endif /* __WIN32__ */
slouken@12109
   169
slouken@12109
   170
slouken@12109
   171
#if defined(__MACOSX__)
slouken@12110
   172
static void CallbackIOServiceFunc(void *context, io_iterator_t portIterator)
slouken@12109
   173
{
slouken@12109
   174
    /* Must drain the iterator, or we won't receive new notifications */
slouken@12110
   175
    io_object_t entry;
slouken@12110
   176
    while ((entry = IOIteratorNext(portIterator)) != 0) {
slouken@12109
   177
        IOObjectRelease(entry);
slouken@12109
   178
        *(SDL_bool*)context = SDL_TRUE;
slouken@12109
   179
    }
slouken@12109
   180
}
slouken@12109
   181
#endif /* __MACOSX__ */
slouken@12109
   182
slouken@12109
   183
static void
slouken@12109
   184
HIDAPI_InitializeDiscovery()
slouken@12109
   185
{
slouken@12109
   186
    SDL_HIDAPI_discovery.m_bHaveDevicesChanged = SDL_TRUE;
slouken@12109
   187
    SDL_HIDAPI_discovery.m_bCanGetNotifications = SDL_FALSE;
slouken@12109
   188
    SDL_HIDAPI_discovery.m_unLastDetect = 0;
slouken@12109
   189
slouken@12109
   190
#if defined(__WIN32__)
slouken@12109
   191
    SDL_HIDAPI_discovery.m_nThreadID = SDL_ThreadID();
slouken@12109
   192
slouken@12109
   193
    SDL_memset(&SDL_HIDAPI_discovery.m_wndClass, 0x0, sizeof(SDL_HIDAPI_discovery.m_wndClass));
slouken@12109
   194
    SDL_HIDAPI_discovery.m_wndClass.hInstance = GetModuleHandle(NULL);
slouken@12114
   195
    SDL_HIDAPI_discovery.m_wndClass.lpszClassName = "SDL_HIDAPI_DEVICE_DETECTION";
slouken@12109
   196
    SDL_HIDAPI_discovery.m_wndClass.lpfnWndProc = ControllerWndProc;      /* This function is called by windows */
slouken@12109
   197
    SDL_HIDAPI_discovery.m_wndClass.cbSize = sizeof(WNDCLASSEX);
slouken@12109
   198
slouken@12109
   199
    RegisterClassExA(&SDL_HIDAPI_discovery.m_wndClass);
slouken@12114
   200
    SDL_HIDAPI_discovery.m_hwndMsg = CreateWindowExA(0, "SDL_HIDAPI_DEVICE_DETECTION", NULL, 0, 0, 0, 0, 0, HWND_MESSAGE, NULL, NULL, NULL);
slouken@12109
   201
slouken@12110
   202
    {
slouken@12110
   203
        DEV_BROADCAST_DEVICEINTERFACE_A devBroadcast;
slouken@12110
   204
        SDL_memset( &devBroadcast, 0x0, sizeof( devBroadcast ) );
slouken@12109
   205
slouken@12110
   206
        devBroadcast.dbcc_size = sizeof( devBroadcast );
slouken@12110
   207
        devBroadcast.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE;
slouken@12110
   208
        devBroadcast.dbcc_classguid = GUID_DEVINTERFACE_USB_DEVICE;
slouken@12109
   209
slouken@12110
   210
        /* DEVICE_NOTIFY_ALL_INTERFACE_CLASSES is important, makes GUID_DEVINTERFACE_USB_DEVICE ignored,
slouken@12110
   211
         * but that seems to be necessary to get a notice after each individual usb input device actually
slouken@12110
   212
         * installs, rather than just as the composite device is seen.
slouken@12110
   213
         */
slouken@12110
   214
        SDL_HIDAPI_discovery.m_hNotify = RegisterDeviceNotification( SDL_HIDAPI_discovery.m_hwndMsg, &devBroadcast, DEVICE_NOTIFY_WINDOW_HANDLE | DEVICE_NOTIFY_ALL_INTERFACE_CLASSES );
slouken@12110
   215
        SDL_HIDAPI_discovery.m_bCanGetNotifications = ( SDL_HIDAPI_discovery.m_hNotify != 0 );
slouken@12110
   216
    }
slouken@12109
   217
#endif /* __WIN32__ */
slouken@12109
   218
slouken@12109
   219
#if defined(__MACOSX__)
slouken@12109
   220
    SDL_HIDAPI_discovery.m_notificationPort = IONotificationPortCreate(kIOMasterPortDefault);
slouken@12109
   221
    if (SDL_HIDAPI_discovery.m_notificationPort) {
slouken@12109
   222
        {
slouken@12109
   223
            io_iterator_t portIterator = 0;
slouken@12110
   224
            io_object_t entry;
rofferom@13562
   225
            IOReturn result = IOServiceAddMatchingNotification(
rofferom@13562
   226
                SDL_HIDAPI_discovery.m_notificationPort,
rofferom@13562
   227
                kIOFirstMatchNotification,
rofferom@13562
   228
                IOServiceMatching(kIOHIDDeviceKey),
rofferom@13562
   229
                CallbackIOServiceFunc, &SDL_HIDAPI_discovery.m_bHaveDevicesChanged, &portIterator);
rofferom@13562
   230
rofferom@13562
   231
            if (result == 0) {
slouken@12109
   232
                /* Must drain the existing iterator, or we won't receive new notifications */
slouken@12110
   233
                while ((entry = IOIteratorNext(portIterator)) != 0) {
slouken@12109
   234
                    IOObjectRelease(entry);
slouken@12109
   235
                }
slouken@12109
   236
            } else {
slouken@12109
   237
                IONotificationPortDestroy(SDL_HIDAPI_discovery.m_notificationPort);
slouken@12109
   238
                SDL_HIDAPI_discovery.m_notificationPort = nil;
slouken@12109
   239
            }
slouken@12109
   240
        }
slouken@12109
   241
        {
slouken@12109
   242
            io_iterator_t portIterator = 0;
slouken@12110
   243
            io_object_t entry;
rofferom@13562
   244
            IOReturn result = IOServiceAddMatchingNotification(
rofferom@13562
   245
                SDL_HIDAPI_discovery.m_notificationPort,
rofferom@13562
   246
                kIOTerminatedNotification,
rofferom@13562
   247
                IOServiceMatching(kIOHIDDeviceKey),
rofferom@13562
   248
                CallbackIOServiceFunc, &SDL_HIDAPI_discovery.m_bHaveDevicesChanged, &portIterator);
rofferom@13562
   249
rofferom@13562
   250
            if (result == 0) {
slouken@12109
   251
                /* Must drain the existing iterator, or we won't receive new notifications */
slouken@12110
   252
                while ((entry = IOIteratorNext(portIterator)) != 0) {
slouken@12109
   253
                    IOObjectRelease(entry);
slouken@12109
   254
                }
slouken@12109
   255
            } else {
slouken@12109
   256
                IONotificationPortDestroy(SDL_HIDAPI_discovery.m_notificationPort);
slouken@12109
   257
                SDL_HIDAPI_discovery.m_notificationPort = nil;
slouken@12109
   258
            }
slouken@12109
   259
        }
slouken@12109
   260
    }
slouken@12109
   261
slouken@12109
   262
    SDL_HIDAPI_discovery.m_notificationMach = MACH_PORT_NULL;
slouken@12109
   263
    if (SDL_HIDAPI_discovery.m_notificationPort) {
slouken@12109
   264
        SDL_HIDAPI_discovery.m_notificationMach = IONotificationPortGetMachPort(SDL_HIDAPI_discovery.m_notificationPort);
slouken@12109
   265
    }
slouken@12109
   266
slouken@12109
   267
    SDL_HIDAPI_discovery.m_bCanGetNotifications = (SDL_HIDAPI_discovery.m_notificationMach != MACH_PORT_NULL);
slouken@12109
   268
slouken@12109
   269
#endif // __MACOSX__
slouken@12109
   270
slouken@12109
   271
#if defined(SDL_USE_LIBUDEV)
slouken@12109
   272
    SDL_HIDAPI_discovery.m_pUdev = NULL;
slouken@12109
   273
    SDL_HIDAPI_discovery.m_pUdevMonitor = NULL;
slouken@12109
   274
    SDL_HIDAPI_discovery.m_nUdevFd = -1;
slouken@12109
   275
slouken@12203
   276
    usyms = SDL_UDEV_GetUdevSyms();
slouken@12203
   277
    if (usyms) {
slouken@12203
   278
        SDL_HIDAPI_discovery.m_pUdev = usyms->udev_new();
slouken@12203
   279
    }
slouken@12109
   280
    if (SDL_HIDAPI_discovery.m_pUdev) {
slouken@12203
   281
        SDL_HIDAPI_discovery.m_pUdevMonitor = usyms->udev_monitor_new_from_netlink(SDL_HIDAPI_discovery.m_pUdev, "udev");
slouken@12109
   282
        if (SDL_HIDAPI_discovery.m_pUdevMonitor) {
slouken@12203
   283
            usyms->udev_monitor_enable_receiving(SDL_HIDAPI_discovery.m_pUdevMonitor);
slouken@12203
   284
            SDL_HIDAPI_discovery.m_nUdevFd = usyms->udev_monitor_get_fd(SDL_HIDAPI_discovery.m_pUdevMonitor);
sezeroz@12181
   285
            SDL_HIDAPI_discovery.m_bCanGetNotifications = SDL_TRUE;
slouken@12109
   286
        }
slouken@12109
   287
    }
slouken@12109
   288
slouken@12109
   289
#endif /* SDL_USE_LIBUDEV */
slouken@12109
   290
}
slouken@12109
   291
slouken@12109
   292
static void
slouken@12109
   293
HIDAPI_UpdateDiscovery()
slouken@12109
   294
{
slouken@12109
   295
    if (!SDL_HIDAPI_discovery.m_bCanGetNotifications) {
slouken@12109
   296
        const Uint32 SDL_HIDAPI_DETECT_INTERVAL_MS = 3000;  /* Update every 3 seconds */
slouken@12109
   297
        Uint32 now = SDL_GetTicks();
slouken@12109
   298
        if (!SDL_HIDAPI_discovery.m_unLastDetect || SDL_TICKS_PASSED(now, SDL_HIDAPI_discovery.m_unLastDetect + SDL_HIDAPI_DETECT_INTERVAL_MS)) {
slouken@12109
   299
            SDL_HIDAPI_discovery.m_bHaveDevicesChanged = SDL_TRUE;
slouken@12109
   300
            SDL_HIDAPI_discovery.m_unLastDetect = now;
slouken@12109
   301
        }
slouken@12109
   302
        return;
slouken@12109
   303
    }
slouken@12109
   304
slouken@12363
   305
#if defined(__WIN32__)
slouken@12363
   306
#if 0 /* just let the usual SDL_PumpEvents loop dispatch these, fixing bug 4286. --ryan. */
slouken@12109
   307
    /* We'll only get messages on the same thread that created the window */
slouken@12109
   308
    if (SDL_ThreadID() == SDL_HIDAPI_discovery.m_nThreadID) {
slouken@12109
   309
        MSG msg;
slouken@12109
   310
        while (PeekMessage(&msg, SDL_HIDAPI_discovery.m_hwndMsg, 0, 0, PM_NOREMOVE)) {
slouken@12109
   311
            if (GetMessageA(&msg, SDL_HIDAPI_discovery.m_hwndMsg, 0, 0) != 0) {
slouken@12109
   312
                TranslateMessage(&msg);
slouken@12109
   313
                DispatchMessage(&msg);
slouken@12109
   314
            }
slouken@12109
   315
        }
slouken@12109
   316
    }
slouken@12109
   317
#endif
slouken@12363
   318
#endif /* __WIN32__ */
slouken@12109
   319
slouken@12109
   320
#if defined(__MACOSX__)
slouken@12109
   321
    if (SDL_HIDAPI_discovery.m_notificationPort) {
slouken@12109
   322
        struct { mach_msg_header_t hdr; char payload[ 4096 ]; } msg;
slouken@12109
   323
        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
   324
            IODispatchCalloutFromMessage(NULL, &msg.hdr, SDL_HIDAPI_discovery.m_notificationPort);
slouken@12109
   325
        }
slouken@12109
   326
    }
slouken@12109
   327
#endif
slouken@12109
   328
slouken@12109
   329
#if defined(SDL_USE_LIBUDEV)
slouken@12109
   330
    if (SDL_HIDAPI_discovery.m_nUdevFd >= 0) {
slouken@12109
   331
        /* Drain all notification events.
slouken@12109
   332
         * We don't expect a lot of device notifications so just
slouken@12109
   333
         * do a new discovery on any kind or number of notifications.
slouken@12109
   334
         * This could be made more restrictive if necessary.
slouken@12109
   335
         */
slouken@12109
   336
        for (;;) {
slouken@12109
   337
            struct pollfd PollUdev;
sezeroz@12181
   338
            struct udev_device *pUdevDevice;
slouken@12109
   339
slouken@12109
   340
            PollUdev.fd = SDL_HIDAPI_discovery.m_nUdevFd;
slouken@12109
   341
            PollUdev.events = POLLIN;
slouken@12109
   342
            if (poll(&PollUdev, 1, 0) != 1) {
slouken@12109
   343
                break;
slouken@12109
   344
            }
slouken@12109
   345
sezeroz@12181
   346
            SDL_HIDAPI_discovery.m_bHaveDevicesChanged = SDL_TRUE;
slouken@12109
   347
slouken@12203
   348
            pUdevDevice = usyms->udev_monitor_receive_device(SDL_HIDAPI_discovery.m_pUdevMonitor);
slouken@12109
   349
            if (pUdevDevice) {
slouken@12203
   350
                usyms->udev_device_unref(pUdevDevice);
slouken@12109
   351
            }
slouken@12109
   352
        }
slouken@12109
   353
    }
slouken@12109
   354
#endif
slouken@12109
   355
}
slouken@12109
   356
slouken@12109
   357
static void
slouken@12109
   358
HIDAPI_ShutdownDiscovery()
slouken@12109
   359
{
slouken@12109
   360
#if defined(__WIN32__)
slouken@12109
   361
    if (SDL_HIDAPI_discovery.m_hNotify)
slouken@12109
   362
        UnregisterDeviceNotification(SDL_HIDAPI_discovery.m_hNotify);
slouken@12109
   363
slouken@12109
   364
    if (SDL_HIDAPI_discovery.m_hwndMsg) {
slouken@12109
   365
        DestroyWindow(SDL_HIDAPI_discovery.m_hwndMsg);
slouken@12109
   366
    }
slouken@12109
   367
slouken@12109
   368
    UnregisterClassA(SDL_HIDAPI_discovery.m_wndClass.lpszClassName, SDL_HIDAPI_discovery.m_wndClass.hInstance);
slouken@12109
   369
#endif
slouken@12109
   370
slouken@12109
   371
#if defined(__MACOSX__)
slouken@12109
   372
    if (SDL_HIDAPI_discovery.m_notificationPort) {
slouken@12109
   373
        IONotificationPortDestroy(SDL_HIDAPI_discovery.m_notificationPort);
slouken@12109
   374
    }
slouken@12109
   375
#endif
slouken@12109
   376
slouken@12109
   377
#if defined(SDL_USE_LIBUDEV)
slouken@12203
   378
    if (usyms) {
sezeroz@12206
   379
        if (SDL_HIDAPI_discovery.m_pUdevMonitor) {
sezeroz@12206
   380
            usyms->udev_monitor_unref(SDL_HIDAPI_discovery.m_pUdevMonitor);
sezeroz@12206
   381
        }
sezeroz@12206
   382
        if (SDL_HIDAPI_discovery.m_pUdev) {
sezeroz@12206
   383
            usyms->udev_unref(SDL_HIDAPI_discovery.m_pUdev);
sezeroz@12206
   384
        }
slouken@12203
   385
        SDL_UDEV_ReleaseUdevSyms();
slouken@12203
   386
        usyms = NULL;
slouken@12109
   387
    }
slouken@12109
   388
#endif
slouken@12109
   389
}
slouken@12109
   390
slouken@13354
   391
static void HIDAPI_JoystickDetect(void);
slouken@13354
   392
static void HIDAPI_JoystickClose(SDL_Joystick * joystick);
slouken@13354
   393
slouken@12095
   394
static SDL_bool
slouken@13160
   395
HIDAPI_IsDeviceSupported(Uint16 vendor_id, Uint16 product_id, Uint16 version, const char *name)
slouken@12095
   396
{
slouken@12095
   397
    int i;
slouken@13431
   398
    SDL_GameControllerType type = SDL_GetJoystickGameControllerType(name, vendor_id, product_id, -1, 0, 0, 0);
slouken@12095
   399
slouken@12095
   400
    for (i = 0; i < SDL_arraysize(SDL_HIDAPI_drivers); ++i) {
slouken@12095
   401
        SDL_HIDAPI_DeviceDriver *driver = SDL_HIDAPI_drivers[i];
slouken@13431
   402
        if (driver->enabled && driver->IsSupportedDevice(name, type, vendor_id, product_id, version, -1, 0, 0, 0)) {
slouken@12095
   403
            return SDL_TRUE;
slouken@12095
   404
        }
slouken@12095
   405
    }
slouken@12095
   406
    return SDL_FALSE;
slouken@12095
   407
}
slouken@12095
   408
slouken@12088
   409
static SDL_HIDAPI_DeviceDriver *
slouken@12088
   410
HIDAPI_GetDeviceDriver(SDL_HIDAPI_Device *device)
slouken@12088
   411
{
slouken@12168
   412
    const Uint16 USAGE_PAGE_GENERIC_DESKTOP = 0x0001;
slouken@12168
   413
    const Uint16 USAGE_JOYSTICK = 0x0004;
slouken@12168
   414
    const Uint16 USAGE_GAMEPAD = 0x0005;
slouken@12168
   415
    const Uint16 USAGE_MULTIAXISCONTROLLER = 0x0008;
slouken@12088
   416
    int i;
slouken@13578
   417
    SDL_GameControllerType type;
slouken@12088
   418
slouken@12088
   419
    if (SDL_ShouldIgnoreJoystick(device->name, device->guid)) {
slouken@12088
   420
        return NULL;
slouken@12088
   421
    }
slouken@12088
   422
slouken@13633
   423
#ifdef SDL_JOYSTICK_RAWINPUT
slouken@13633
   424
    if (RAWINPUT_IsDevicePresent(device->vendor_id, device->product_id, device->version)) {
slouken@13633
   425
        /* The RAWINPUT driver is taking care of this device */
slouken@13633
   426
        return NULL;
slouken@13633
   427
    }
slouken@13633
   428
#endif
slouken@13633
   429
slouken@13662
   430
	if (device->vendor_id != USB_VENDOR_VALVE) {
slouken@13662
   431
        if (device->usage_page && device->usage_page != USAGE_PAGE_GENERIC_DESKTOP) {
slouken@13662
   432
            return NULL;
slouken@13662
   433
        }
slouken@13662
   434
        if (device->usage && device->usage != USAGE_JOYSTICK && device->usage != USAGE_GAMEPAD && device->usage != USAGE_MULTIAXISCONTROLLER) {
slouken@13662
   435
            return NULL;
slouken@13662
   436
        }
slouken@12168
   437
    }
slouken@12168
   438
slouken@13578
   439
    type = SDL_GetJoystickGameControllerType(device->name, device->vendor_id, device->product_id, device->interface_number, device->interface_class, device->interface_subclass, device->interface_protocol);
slouken@12088
   440
    for (i = 0; i < SDL_arraysize(SDL_HIDAPI_drivers); ++i) {
slouken@12088
   441
        SDL_HIDAPI_DeviceDriver *driver = SDL_HIDAPI_drivers[i];
slouken@13431
   442
        if (driver->enabled && driver->IsSupportedDevice(device->name, type, device->vendor_id, device->product_id, device->version, device->interface_number, device->interface_class, device->interface_subclass, device->interface_protocol)) {
slouken@12088
   443
            return driver;
slouken@12088
   444
        }
slouken@12088
   445
    }
slouken@12088
   446
    return NULL;
slouken@12088
   447
}
slouken@12088
   448
slouken@12088
   449
static SDL_HIDAPI_Device *
slouken@13354
   450
HIDAPI_GetDeviceByIndex(int device_index, SDL_JoystickID *pJoystickID)
slouken@12088
   451
{
slouken@12088
   452
    SDL_HIDAPI_Device *device = SDL_HIDAPI_devices;
slouken@12088
   453
    while (device) {
slouken@12088
   454
        if (device->driver) {
slouken@13354
   455
            if (device_index < device->num_joysticks) {
slouken@13354
   456
                if (pJoystickID) {
slouken@13354
   457
                    *pJoystickID = device->joysticks[device_index];
slouken@13354
   458
                }
slouken@13354
   459
                return device;
slouken@12088
   460
            }
slouken@13354
   461
            device_index -= device->num_joysticks;
slouken@12088
   462
        }
slouken@12088
   463
        device = device->next;
slouken@12088
   464
    }
slouken@13354
   465
    return NULL;
slouken@12088
   466
}
slouken@12088
   467
slouken@12088
   468
static SDL_HIDAPI_Device *
slouken@12088
   469
HIDAPI_GetJoystickByInfo(const char *path, Uint16 vendor_id, Uint16 product_id)
slouken@12088
   470
{
slouken@12088
   471
    SDL_HIDAPI_Device *device = SDL_HIDAPI_devices;
slouken@12088
   472
    while (device) {
slouken@12088
   473
        if (device->vendor_id == vendor_id && device->product_id == product_id &&
slouken@12088
   474
            SDL_strcmp(device->path, path) == 0) {
slouken@12088
   475
            break;
slouken@12088
   476
        }
slouken@12088
   477
        device = device->next;
slouken@12088
   478
    }
slouken@12088
   479
    return device;
slouken@12088
   480
}
slouken@12088
   481
slouken@13354
   482
static void
slouken@13354
   483
HIDAPI_SetupDeviceDriver(SDL_HIDAPI_Device *device)
slouken@13354
   484
{
slouken@13354
   485
    if (device->driver) {
slouken@13354
   486
        /* Already setup */
slouken@13354
   487
        return;
slouken@13354
   488
    }
slouken@13354
   489
slouken@13354
   490
    device->driver = HIDAPI_GetDeviceDriver(device);
slouken@13354
   491
    if (device->driver) {
slouken@13354
   492
        const char *name = device->driver->GetDeviceName(device->vendor_id, device->product_id);
slouken@13354
   493
        if (name) {
slouken@13354
   494
            SDL_free(device->name);
slouken@13354
   495
            device->name = SDL_strdup(name);
slouken@13354
   496
        }
slouken@13354
   497
    }
slouken@13354
   498
slouken@13354
   499
    /* Initialize the device, which may cause a connected event */
slouken@13354
   500
    if (device->driver && !device->driver->InitDevice(device)) {
slouken@13354
   501
        device->driver = NULL;
slouken@13354
   502
    }
slouken@13354
   503
}
slouken@13354
   504
slouken@13354
   505
static void
slouken@13354
   506
HIDAPI_CleanupDeviceDriver(SDL_HIDAPI_Device *device)
slouken@13354
   507
{
slouken@13354
   508
    if (!device->driver) {
slouken@13354
   509
        /* Already cleaned up */
slouken@13354
   510
        return;
slouken@13354
   511
    }
slouken@13354
   512
slouken@13354
   513
    /* Disconnect any joysticks */
zack@13375
   514
    while (device->num_joysticks) {
slouken@13633
   515
        HIDAPI_JoystickDisconnected(device, device->joysticks[0], SDL_FALSE);
slouken@13354
   516
    }
slouken@13354
   517
slouken@13354
   518
    device->driver->FreeDevice(device);
slouken@13354
   519
    device->driver = NULL;
slouken@13354
   520
}
slouken@13354
   521
slouken@12088
   522
static void SDLCALL
slouken@12088
   523
SDL_HIDAPIDriverHintChanged(void *userdata, const char *name, const char *oldValue, const char *hint)
slouken@12088
   524
{
slouken@12088
   525
    int i;
slouken@13447
   526
    SDL_HIDAPI_Device *device;
slouken@13244
   527
    SDL_bool enabled = SDL_GetStringBoolean(hint, SDL_TRUE);
slouken@12088
   528
slouken@12088
   529
    if (SDL_strcmp(name, SDL_HINT_JOYSTICK_HIDAPI) == 0) {
slouken@12088
   530
        for (i = 0; i < SDL_arraysize(SDL_HIDAPI_drivers); ++i) {
slouken@12088
   531
            SDL_HIDAPI_DeviceDriver *driver = SDL_HIDAPI_drivers[i];
slouken@12088
   532
            driver->enabled = SDL_GetHintBoolean(driver->hint, enabled);
slouken@12088
   533
        }
slouken@12088
   534
    } else {
slouken@12088
   535
        for (i = 0; i < SDL_arraysize(SDL_HIDAPI_drivers); ++i) {
slouken@12088
   536
            SDL_HIDAPI_DeviceDriver *driver = SDL_HIDAPI_drivers[i];
slouken@12088
   537
            if (SDL_strcmp(name, driver->hint) == 0) {
slouken@12088
   538
                driver->enabled = enabled;
slouken@12088
   539
            }
slouken@12088
   540
        }
slouken@12088
   541
    }
slouken@12088
   542
slouken@12953
   543
    SDL_HIDAPI_numdrivers = 0;
slouken@12953
   544
    for (i = 0; i < SDL_arraysize(SDL_HIDAPI_drivers); ++i) {
slouken@12953
   545
        SDL_HIDAPI_DeviceDriver *driver = SDL_HIDAPI_drivers[i];
slouken@12953
   546
        if (driver->enabled) {
slouken@12953
   547
            ++SDL_HIDAPI_numdrivers;
slouken@12953
   548
        }
slouken@12953
   549
    }
slouken@12953
   550
slouken@12088
   551
    /* Update device list if driver availability changes */
slouken@13413
   552
    SDL_LockJoysticks();
slouken@12896
   553
slouken@13447
   554
    for (device = SDL_HIDAPI_devices; device; device = device->next) {
slouken@13354
   555
        if (device->driver && !device->driver->enabled) {
slouken@13354
   556
            HIDAPI_CleanupDeviceDriver(device);
slouken@12088
   557
        }
slouken@13354
   558
        HIDAPI_SetupDeviceDriver(device);
slouken@12088
   559
    }
slouken@13354
   560
slouken@13413
   561
    SDL_UnlockJoysticks();
slouken@12088
   562
}
slouken@12088
   563
slouken@12088
   564
static int
slouken@12088
   565
HIDAPI_JoystickInit(void)
slouken@12088
   566
{
slouken@12088
   567
    int i;
slouken@12088
   568
aeikum@12972
   569
    if (initialized) {
aeikum@12972
   570
        return 0;
aeikum@12972
   571
    }
aeikum@12972
   572
slouken@13470
   573
#if defined(__MACOSX__) || defined(__IPHONEOS__) || defined(__TVOS__)
slouken@13470
   574
	/* The hidapi framwork is weak-linked on Apple platforms */
slouken@13469
   575
    int HID_API_EXPORT HID_API_CALL hid_init(void) __attribute__((weak_import));
slouken@13469
   576
slouken@13469
   577
    if (hid_init == NULL) {
slouken@13469
   578
        SDL_SetError("Couldn't initialize hidapi, framework not available");
slouken@13469
   579
        return -1;
slouken@13469
   580
    }
slouken@13470
   581
#endif /* __MACOSX__ || __IPHONEOS__ || __TVOS__ */
slouken@13469
   582
slouken@12088
   583
    if (hid_init() < 0) {
slouken@12088
   584
        SDL_SetError("Couldn't initialize hidapi");
slouken@12088
   585
        return -1;
slouken@12088
   586
    }
slouken@12088
   587
slouken@13633
   588
#ifdef __WINDOWS__
slouken@13633
   589
    /* On Windows, turns out HIDAPI for Xbox controllers doesn't allow background input, so off by default */
slouken@13633
   590
    SDL_SetHintWithPriority(SDL_HINT_JOYSTICK_HIDAPI_XBOX, "0", SDL_HINT_DEFAULT);
slouken@13633
   591
#endif
slouken@13633
   592
slouken@12088
   593
    for (i = 0; i < SDL_arraysize(SDL_HIDAPI_drivers); ++i) {
slouken@12088
   594
        SDL_HIDAPI_DeviceDriver *driver = SDL_HIDAPI_drivers[i];
slouken@12088
   595
        SDL_AddHintCallback(driver->hint, SDL_HIDAPIDriverHintChanged, NULL);
slouken@12088
   596
    }
slouken@12088
   597
    SDL_AddHintCallback(SDL_HINT_JOYSTICK_HIDAPI,
slouken@12088
   598
                        SDL_HIDAPIDriverHintChanged, NULL);
slouken@12109
   599
    HIDAPI_InitializeDiscovery();
slouken@12088
   600
    HIDAPI_JoystickDetect();
slouken@13356
   601
    HIDAPI_UpdateDevices();
aeikum@12972
   602
aeikum@12972
   603
    initialized = SDL_TRUE;
aeikum@12972
   604
slouken@12088
   605
    return 0;
slouken@12088
   606
}
slouken@12088
   607
slouken@13354
   608
SDL_bool
slouken@13633
   609
HIDAPI_JoystickConnected(SDL_HIDAPI_Device *device, SDL_JoystickID *pJoystickID, SDL_bool is_external)
slouken@13354
   610
{
slouken@13354
   611
    SDL_JoystickID joystickID;
slouken@13354
   612
    SDL_JoystickID *joysticks = (SDL_JoystickID *)SDL_realloc(device->joysticks, (device->num_joysticks + 1)*sizeof(*device->joysticks));
slouken@13354
   613
    if (!joysticks) {
slouken@13354
   614
        return SDL_FALSE;
slouken@13354
   615
    }
slouken@13354
   616
slouken@13354
   617
    joystickID = SDL_GetNextJoystickInstanceID();
slouken@13354
   618
    device->joysticks = joysticks;
slouken@13354
   619
    device->joysticks[device->num_joysticks++] = joystickID;
slouken@13633
   620
    if (!is_external) {
slouken@13633
   621
        ++SDL_HIDAPI_numjoysticks;
slouken@13633
   622
    }
slouken@13354
   623
slouken@13354
   624
    SDL_PrivateJoystickAdded(joystickID);
slouken@13354
   625
slouken@13354
   626
    if (pJoystickID) {
slouken@13354
   627
        *pJoystickID = joystickID;
slouken@13354
   628
    }
slouken@13354
   629
    return SDL_TRUE;
slouken@13354
   630
}
slouken@13354
   631
slouken@13354
   632
void
slouken@13633
   633
HIDAPI_JoystickDisconnected(SDL_HIDAPI_Device *device, SDL_JoystickID joystickID, SDL_bool is_external)
slouken@13354
   634
{
slouken@13354
   635
    int i;
slouken@13354
   636
slouken@13354
   637
    for (i = 0; i < device->num_joysticks; ++i) {
slouken@13354
   638
        if (device->joysticks[i] == joystickID) {
slouken@13401
   639
            SDL_Joystick *joystick = SDL_JoystickFromInstanceID(joystickID);
slouken@13633
   640
            if (joystick && !is_external) {
slouken@13401
   641
                HIDAPI_JoystickClose(joystick);
slouken@13401
   642
            }
slouken@13401
   643
slouken@13523
   644
            SDL_memmove(&device->joysticks[i], &device->joysticks[i+1], device->num_joysticks - i - 1);
slouken@13354
   645
            --device->num_joysticks;
slouken@13633
   646
            if (!is_external) {
slouken@13633
   647
                --SDL_HIDAPI_numjoysticks;
slouken@13633
   648
            }
slouken@13523
   649
            if (device->num_joysticks == 0) {
slouken@13523
   650
                SDL_free(device->joysticks);
slouken@13523
   651
                device->joysticks = NULL;
slouken@13523
   652
            }
slouken@13354
   653
slouken@13354
   654
            if (!shutting_down) {
slouken@13354
   655
                SDL_PrivateJoystickRemoved(joystickID);
slouken@13354
   656
            }
slouken@13354
   657
            return;
slouken@13354
   658
        }
slouken@13354
   659
    }
slouken@13354
   660
}
slouken@13354
   661
slouken@12088
   662
static int
slouken@12088
   663
HIDAPI_JoystickGetCount(void)
slouken@12088
   664
{
slouken@12088
   665
    return SDL_HIDAPI_numjoysticks;
slouken@12088
   666
}
slouken@12088
   667
slouken@12088
   668
static void
slouken@12088
   669
HIDAPI_AddDevice(struct hid_device_info *info)
slouken@12088
   670
{
slouken@12088
   671
    SDL_HIDAPI_Device *device;
slouken@12088
   672
    SDL_HIDAPI_Device *curr, *last = NULL;
slouken@12088
   673
slouken@12088
   674
    for (curr = SDL_HIDAPI_devices, last = NULL; curr; last = curr, curr = curr->next) {
slouken@12088
   675
        continue;
slouken@12088
   676
    }
slouken@12088
   677
slouken@12088
   678
    device = (SDL_HIDAPI_Device *)SDL_calloc(1, sizeof(*device));
slouken@12088
   679
    if (!device) {
slouken@12088
   680
        return;
slouken@12088
   681
    }
slouken@13354
   682
    device->path = SDL_strdup(info->path);
slouken@13354
   683
    if (!device->path) {
slouken@13354
   684
        SDL_free(device);
slouken@13354
   685
        return;
slouken@13354
   686
    }
slouken@12088
   687
    device->seen = SDL_TRUE;
slouken@12088
   688
    device->vendor_id = info->vendor_id;
slouken@12088
   689
    device->product_id = info->product_id;
slouken@12162
   690
    device->version = info->release_number;
slouken@12088
   691
    device->interface_number = info->interface_number;
slouken@13429
   692
    device->interface_class = info->interface_class;
slouken@13429
   693
    device->interface_subclass = info->interface_subclass;
slouken@13429
   694
    device->interface_protocol = info->interface_protocol;
slouken@12088
   695
    device->usage_page = info->usage_page;
slouken@12088
   696
    device->usage = info->usage;
slouken@12088
   697
    {
slouken@12088
   698
        /* FIXME: Is there any way to tell whether this is a Bluetooth device? */
slouken@12088
   699
        const Uint16 vendor = device->vendor_id;
slouken@12088
   700
        const Uint16 product = device->product_id;
slouken@12115
   701
        const Uint16 version = device->version;
slouken@12088
   702
        Uint16 *guid16 = (Uint16 *)device->guid.data;
slouken@12088
   703
slouken@12088
   704
        *guid16++ = SDL_SwapLE16(SDL_HARDWARE_BUS_USB);
slouken@12088
   705
        *guid16++ = 0;
slouken@12088
   706
        *guid16++ = SDL_SwapLE16(vendor);
slouken@12088
   707
        *guid16++ = 0;
slouken@12088
   708
        *guid16++ = SDL_SwapLE16(product);
slouken@12088
   709
        *guid16++ = 0;
slouken@12088
   710
        *guid16++ = SDL_SwapLE16(version);
slouken@12088
   711
        *guid16++ = 0;
slouken@12088
   712
slouken@12088
   713
        /* Note that this is a HIDAPI device for special handling elsewhere */
slouken@12088
   714
        device->guid.data[14] = 'h';
slouken@12088
   715
        device->guid.data[15] = 0;
slouken@12088
   716
    }
slouken@13481
   717
    device->dev_lock = SDL_CreateMutex();
slouken@12088
   718
slouken@12185
   719
    /* Need the device name before getting the driver to know whether to ignore this device */
slouken@13611
   720
    {
slouken@13619
   721
        char *manufacturer_string = NULL;
slouken@13619
   722
        char *product_string = NULL;
slouken@13619
   723
slouken@13619
   724
        if (info->manufacturer_string) {
slouken@13619
   725
            manufacturer_string = SDL_iconv_string("UTF-8", "WCHAR_T", (char*)info->manufacturer_string, (SDL_wcslen(info->manufacturer_string)+1)*sizeof(wchar_t));
slouken@13619
   726
            if (!manufacturer_string) {
slouken@13619
   727
                if (sizeof(wchar_t) == sizeof(Uint16)) {
slouken@13619
   728
                    manufacturer_string = SDL_iconv_string("UTF-8", "UCS-2-INTERNAL", (char*)info->manufacturer_string, (SDL_wcslen(info->manufacturer_string)+1)*sizeof(wchar_t));
slouken@13619
   729
                } else if (sizeof(wchar_t) == sizeof(Uint32)) {
slouken@13619
   730
                    manufacturer_string = SDL_iconv_string("UTF-8", "UCS-4-INTERNAL", (char*)info->manufacturer_string, (SDL_wcslen(info->manufacturer_string)+1)*sizeof(wchar_t));
slouken@13619
   731
                }
slouken@13619
   732
            }
slouken@13619
   733
        }
slouken@13619
   734
        if (info->product_string) {
slouken@13619
   735
            product_string = SDL_iconv_string("UTF-8", "WCHAR_T", (char*)info->product_string, (SDL_wcslen(info->product_string)+1)*sizeof(wchar_t));
slouken@13619
   736
            if (!product_string) {
slouken@13619
   737
                if (sizeof(wchar_t) == sizeof(Uint16)) {
slouken@13619
   738
                    product_string = SDL_iconv_string("UTF-8", "UCS-2-INTERNAL", (char*)info->product_string, (SDL_wcslen(info->product_string)+1)*sizeof(wchar_t));
slouken@13619
   739
                } else if (sizeof(wchar_t) == sizeof(Uint32)) {
slouken@13619
   740
                    product_string = SDL_iconv_string("UTF-8", "UCS-4-INTERNAL", (char*)info->product_string, (SDL_wcslen(info->product_string)+1)*sizeof(wchar_t));
slouken@13619
   741
                }
slouken@12088
   742
            }
slouken@12088
   743
        }
slouken@13332
   744
slouken@13611
   745
        device->name = SDL_CreateJoystickName(device->vendor_id, device->product_id, manufacturer_string, product_string);
slouken@13332
   746
slouken@12088
   747
        if (manufacturer_string) {
slouken@12088
   748
            SDL_free(manufacturer_string);
slouken@12088
   749
        }
slouken@12088
   750
        if (product_string) {
slouken@12088
   751
            SDL_free(product_string);
slouken@12088
   752
        }
slouken@13611
   753
slouken@12088
   754
        if (!device->name) {
slouken@13354
   755
            SDL_free(device->path);
slouken@12088
   756
            SDL_free(device);
slouken@12088
   757
            return;
slouken@12088
   758
        }
slouken@12088
   759
    }
slouken@12088
   760
slouken@12088
   761
    /* Add it to the list */
slouken@12088
   762
    if (last) {
slouken@12088
   763
        last->next = device;
slouken@12088
   764
    } else {
slouken@12088
   765
        SDL_HIDAPI_devices = device;
slouken@12088
   766
    }
slouken@12088
   767
slouken@13384
   768
    HIDAPI_SetupDeviceDriver(device);
slouken@13384
   769
slouken@13354
   770
#ifdef DEBUG_HIDAPI
slouken@13447
   771
    SDL_Log("Added HIDAPI device '%s' VID 0x%.4x, PID 0x%.4x, version %d, interface %d, interface_class %d, interface_subclass %d, interface_protocol %d, usage page 0x%.4x, usage 0x%.4x, path = %s, driver = %s (%s)\n", device->name, device->vendor_id, device->product_id, device->version, device->interface_number, device->interface_class, device->interface_subclass, device->interface_protocol, device->usage_page, device->usage, device->path, device->driver ? device->driver->hint : "NONE", device->driver && device->driver->enabled ? "ENABLED" : "DISABLED");
slouken@13354
   772
#endif
slouken@12088
   773
}
slouken@12088
   774
slouken@12088
   775
slouken@12088
   776
static void
slouken@13354
   777
HIDAPI_DelDevice(SDL_HIDAPI_Device *device)
slouken@12088
   778
{
slouken@12088
   779
    SDL_HIDAPI_Device *curr, *last;
slouken@12088
   780
    for (curr = SDL_HIDAPI_devices, last = NULL; curr; last = curr, curr = curr->next) {
slouken@12088
   781
        if (curr == device) {
slouken@12088
   782
            if (last) {
slouken@12088
   783
                last->next = curr->next;
slouken@12088
   784
            } else {
slouken@12088
   785
                SDL_HIDAPI_devices = curr->next;
slouken@12088
   786
            }
slouken@12088
   787
slouken@13354
   788
            HIDAPI_CleanupDeviceDriver(device);
slouken@12088
   789
slouken@13481
   790
            SDL_DestroyMutex(device->dev_lock);
slouken@12088
   791
            SDL_free(device->name);
slouken@12088
   792
            SDL_free(device->path);
slouken@12088
   793
            SDL_free(device);
slouken@12088
   794
            return;
slouken@12088
   795
        }
slouken@12088
   796
    }
slouken@12088
   797
}
slouken@12088
   798
slouken@12088
   799
static void
slouken@12088
   800
HIDAPI_UpdateDeviceList(void)
slouken@12088
   801
{
slouken@12088
   802
    SDL_HIDAPI_Device *device;
slouken@12088
   803
    struct hid_device_info *devs, *info;
slouken@12088
   804
slouken@13413
   805
    SDL_LockJoysticks();
slouken@13354
   806
slouken@12088
   807
    /* Prepare the existing device list */
slouken@12088
   808
    device = SDL_HIDAPI_devices;
slouken@12088
   809
    while (device) {
slouken@12088
   810
        device->seen = SDL_FALSE;
slouken@12088
   811
        device = device->next;
slouken@12088
   812
    }
slouken@12088
   813
slouken@12088
   814
    /* Enumerate the devices */
slouken@12953
   815
    if (SDL_HIDAPI_numdrivers > 0) {
slouken@12953
   816
        devs = hid_enumerate(0, 0);
slouken@12953
   817
        if (devs) {
slouken@12953
   818
            for (info = devs; info; info = info->next) {
slouken@12953
   819
                device = HIDAPI_GetJoystickByInfo(info->path, info->vendor_id, info->product_id);
slouken@12953
   820
                if (device) {
slouken@12953
   821
                    device->seen = SDL_TRUE;
slouken@12953
   822
                } else {
slouken@12953
   823
                    HIDAPI_AddDevice(info);
slouken@12953
   824
                }
slouken@12088
   825
            }
slouken@12953
   826
            hid_free_enumeration(devs);
slouken@12088
   827
        }
slouken@12088
   828
    }
slouken@12088
   829
slouken@12088
   830
    /* Remove any devices that weren't seen */
slouken@12088
   831
    device = SDL_HIDAPI_devices;
slouken@12088
   832
    while (device) {
slouken@12088
   833
        SDL_HIDAPI_Device *next = device->next;
slouken@12088
   834
slouken@12088
   835
        if (!device->seen) {
slouken@13354
   836
            HIDAPI_DelDevice(device);
slouken@12088
   837
        }
slouken@12088
   838
        device = next;
slouken@12088
   839
    }
slouken@13354
   840
slouken@13413
   841
    SDL_UnlockJoysticks();
slouken@12088
   842
}
slouken@12088
   843
slouken@12088
   844
SDL_bool
slouken@13160
   845
HIDAPI_IsDevicePresent(Uint16 vendor_id, Uint16 product_id, Uint16 version, const char *name)
slouken@12088
   846
{
slouken@12088
   847
    SDL_HIDAPI_Device *device;
slouken@13429
   848
    SDL_bool supported = SDL_FALSE;
slouken@13413
   849
    SDL_bool result = SDL_FALSE;
slouken@12088
   850
slouken@12973
   851
    /* Make sure we're initialized, as this could be called from other drivers during startup */
slouken@12973
   852
    if (HIDAPI_JoystickInit() < 0) {
slouken@12973
   853
        return SDL_FALSE;
slouken@12973
   854
    }
slouken@12973
   855
slouken@13429
   856
    /* Only update the device list for devices we know might be supported.
slouken@13429
   857
       If we did this for every device, it would hit the USB driver too hard and potentially 
slouken@13429
   858
       lock up the system. This won't catch devices that we support but can only detect using 
slouken@13429
   859
       USB interface details, like Xbox controllers, but hopefully the device list update is
slouken@13429
   860
       responsive enough to catch those.
slouken@13429
   861
     */
slouken@13429
   862
    supported = HIDAPI_IsDeviceSupported(vendor_id, product_id, version, name);
slouken@13429
   863
#if defined(SDL_JOYSTICK_HIDAPI_XBOX360) || defined(SDL_JOYSTICK_HIDAPI_XBOXONE)
slouken@13429
   864
    if (!supported &&
slouken@13429
   865
        (SDL_strstr(name, "Xbox") || SDL_strstr(name, "X-Box") || SDL_strstr(name, "XBOX"))) {
slouken@13429
   866
        supported = SDL_TRUE;
slouken@12095
   867
    }
slouken@13429
   868
#endif /* SDL_JOYSTICK_HIDAPI_XBOX360 || SDL_JOYSTICK_HIDAPI_XBOXONE */
slouken@13429
   869
    if (supported) {
slouken@13429
   870
        if (SDL_AtomicTryLock(&SDL_HIDAPI_spinlock)) {
slouken@13429
   871
            HIDAPI_UpdateDeviceList();
slouken@13429
   872
            SDL_AtomicUnlock(&SDL_HIDAPI_spinlock);
slouken@13429
   873
        }
slouken@13413
   874
    }
slouken@12088
   875
slouken@13160
   876
    /* Note that this isn't a perfect check - there may be multiple devices with 0 VID/PID,
slouken@13160
   877
       or a different name than we have it listed here, etc, but if we support the device
slouken@13160
   878
       and we have something similar in our device list, mark it as present.
slouken@13160
   879
     */
slouken@13413
   880
    SDL_LockJoysticks();
slouken@12088
   881
    device = SDL_HIDAPI_devices;
slouken@12088
   882
    while (device) {
slouken@12088
   883
        if (device->vendor_id == vendor_id && device->product_id == product_id && device->driver) {
slouken@13413
   884
            result = SDL_TRUE;
slouken@12088
   885
        }
slouken@12088
   886
        device = device->next;
slouken@12088
   887
    }
slouken@13413
   888
    SDL_UnlockJoysticks();
slouken@13413
   889
slouken@13425
   890
    /* If we're looking for the wireless XBox 360 controller, also look for the dongle */
slouken@13429
   891
    if (!result && vendor_id == USB_VENDOR_MICROSOFT && product_id == 0x02a1) {
slouken@13429
   892
        return HIDAPI_IsDevicePresent(USB_VENDOR_MICROSOFT, 0x0719, version, name);
slouken@13425
   893
    }
slouken@13425
   894
slouken@13425
   895
#ifdef DEBUG_HIDAPI
slouken@13425
   896
    SDL_Log("HIDAPI_IsDevicePresent() returning %s for 0x%.4x / 0x%.4x\n", result ? "true" : "false", vendor_id, product_id);
slouken@13425
   897
#endif
slouken@13413
   898
    return result;
slouken@12088
   899
}
slouken@12088
   900
slouken@12088
   901
static void
slouken@12088
   902
HIDAPI_JoystickDetect(void)
slouken@12088
   903
{
slouken@13633
   904
    int i;
slouken@13413
   905
    if (SDL_AtomicTryLock(&SDL_HIDAPI_spinlock)) {
slouken@13413
   906
        HIDAPI_UpdateDiscovery();
slouken@13413
   907
        if (SDL_HIDAPI_discovery.m_bHaveDevicesChanged) {
slouken@13413
   908
            /* FIXME: We probably need to schedule an update in a few seconds as well */
slouken@13413
   909
            HIDAPI_UpdateDeviceList();
slouken@13413
   910
            SDL_HIDAPI_discovery.m_bHaveDevicesChanged = SDL_FALSE;
slouken@13413
   911
        }
slouken@13413
   912
        SDL_AtomicUnlock(&SDL_HIDAPI_spinlock);
slouken@12088
   913
    }
slouken@13633
   914
    for (i = 0; i < SDL_arraysize(SDL_HIDAPI_drivers); ++i) {
slouken@13633
   915
        SDL_HIDAPI_DeviceDriver *driver = SDL_HIDAPI_drivers[i];
slouken@13633
   916
        if (driver->enabled && driver->PostUpdate) {
slouken@13633
   917
            driver->PostUpdate();
slouken@13633
   918
        }
slouken@13633
   919
    }
slouken@13356
   920
}
slouken@13356
   921
slouken@13356
   922
void
slouken@13356
   923
HIDAPI_UpdateDevices(void)
slouken@13356
   924
{
slouken@13356
   925
    SDL_HIDAPI_Device *device;
slouken@13354
   926
slouken@13354
   927
    /* Update the devices, which may change connected joysticks and send events */
slouken@13354
   928
slouken@13354
   929
    /* Prepare the existing device list */
slouken@13413
   930
    if (SDL_AtomicTryLock(&SDL_HIDAPI_spinlock)) {
slouken@13413
   931
        device = SDL_HIDAPI_devices;
slouken@13413
   932
        while (device) {
slouken@13413
   933
            if (device->driver) {
slouken@13481
   934
                if (SDL_TryLockMutex(device->dev_lock) == 0) {
slouken@13481
   935
                    device->driver->UpdateDevice(device);
slouken@13481
   936
                    SDL_UnlockMutex(device->dev_lock);
slouken@13481
   937
                }
slouken@13413
   938
            }
slouken@13413
   939
            device = device->next;
slouken@13354
   940
        }
slouken@13413
   941
        SDL_AtomicUnlock(&SDL_HIDAPI_spinlock);
slouken@13354
   942
    }
slouken@12088
   943
}
slouken@12088
   944
slouken@12088
   945
static const char *
slouken@12088
   946
HIDAPI_JoystickGetDeviceName(int device_index)
slouken@12088
   947
{
slouken@13354
   948
    SDL_HIDAPI_Device *device;
slouken@13354
   949
    const char *name = NULL;
slouken@13354
   950
slouken@13354
   951
    device = HIDAPI_GetDeviceByIndex(device_index, NULL);
slouken@13354
   952
    if (device) {
slouken@13354
   953
        /* FIXME: The device could be freed after this name is returned... */
slouken@13354
   954
        name = device->name;
slouken@13354
   955
    }
slouken@13354
   956
slouken@13354
   957
    return name;
slouken@12088
   958
}
slouken@12088
   959
slouken@12359
   960
static int
slouken@12359
   961
HIDAPI_JoystickGetDevicePlayerIndex(int device_index)
slouken@12359
   962
{
slouken@13369
   963
    SDL_HIDAPI_Device *device;
slouken@13369
   964
    SDL_JoystickID instance_id;
slouken@13369
   965
    int player_index = -1;
slouken@13369
   966
slouken@13369
   967
    device = HIDAPI_GetDeviceByIndex(device_index, &instance_id);
slouken@13369
   968
    if (device) {
slouken@13369
   969
        player_index = device->driver->GetDevicePlayerIndex(device, instance_id);
slouken@13369
   970
    }
slouken@13369
   971
slouken@13369
   972
    return player_index;
slouken@13369
   973
}
slouken@13369
   974
slouken@13369
   975
static void
slouken@13369
   976
HIDAPI_JoystickSetDevicePlayerIndex(int device_index, int player_index)
slouken@13369
   977
{
slouken@13369
   978
    SDL_HIDAPI_Device *device;
slouken@13369
   979
    SDL_JoystickID instance_id;
slouken@13369
   980
slouken@13369
   981
    device = HIDAPI_GetDeviceByIndex(device_index, &instance_id);
slouken@13369
   982
    if (device) {
slouken@13369
   983
        device->driver->SetDevicePlayerIndex(device, instance_id, player_index);
slouken@13369
   984
    }
slouken@12359
   985
}
slouken@12359
   986
slouken@12088
   987
static SDL_JoystickGUID
slouken@12088
   988
HIDAPI_JoystickGetDeviceGUID(int device_index)
slouken@12088
   989
{
slouken@13354
   990
    SDL_HIDAPI_Device *device;
slouken@13354
   991
    SDL_JoystickGUID guid;
slouken@13354
   992
slouken@13354
   993
    device = HIDAPI_GetDeviceByIndex(device_index, NULL);
slouken@13354
   994
    if (device) {
slouken@13354
   995
        SDL_memcpy(&guid, &device->guid, sizeof(guid));
slouken@13354
   996
    } else {
slouken@13354
   997
        SDL_zero(guid);
slouken@13354
   998
    }
slouken@13354
   999
slouken@13354
  1000
    return guid;
slouken@12088
  1001
}
slouken@12088
  1002
slouken@12088
  1003
static SDL_JoystickID
slouken@12088
  1004
HIDAPI_JoystickGetDeviceInstanceID(int device_index)
slouken@12088
  1005
{
slouken@13361
  1006
    SDL_JoystickID joystickID = -1;
slouken@13354
  1007
    HIDAPI_GetDeviceByIndex(device_index, &joystickID);
slouken@13354
  1008
    return joystickID;
slouken@12088
  1009
}
slouken@12088
  1010
slouken@12088
  1011
static int
slouken@12088
  1012
HIDAPI_JoystickOpen(SDL_Joystick * joystick, int device_index)
slouken@12088
  1013
{
slouken@13354
  1014
    SDL_JoystickID joystickID;
slouken@13354
  1015
    SDL_HIDAPI_Device *device = HIDAPI_GetDeviceByIndex(device_index, &joystickID);
slouken@12896
  1016
    struct joystick_hwdata *hwdata;
slouken@12896
  1017
slouken@12896
  1018
    hwdata = (struct joystick_hwdata *)SDL_calloc(1, sizeof(*hwdata));
slouken@12896
  1019
    if (!hwdata) {
slouken@12896
  1020
        return SDL_OutOfMemory();
slouken@12896
  1021
    }
slouken@13354
  1022
    hwdata->device = device;
slouken@12088
  1023
slouken@13354
  1024
    if (!device->driver->OpenJoystick(device, joystick)) {
slouken@12896
  1025
        SDL_free(hwdata);
slouken@12088
  1026
        return -1;
slouken@12088
  1027
    }
slouken@12088
  1028
slouken@12896
  1029
    joystick->hwdata = hwdata;
slouken@12088
  1030
    return 0;
slouken@12088
  1031
}
slouken@12088
  1032
slouken@12088
  1033
static int
slouken@13480
  1034
HIDAPI_JoystickRumble(SDL_Joystick * joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble)
slouken@12088
  1035
{
slouken@12277
  1036
    int result;
slouken@12277
  1037
slouken@13354
  1038
    if (joystick->hwdata) {
slouken@13354
  1039
        SDL_HIDAPI_Device *device = joystick->hwdata->device;
slouken@13354
  1040
slouken@13480
  1041
        result = device->driver->RumbleJoystick(device, joystick, low_frequency_rumble, high_frequency_rumble);
slouken@13354
  1042
    } else {
slouken@13354
  1043
        SDL_SetError("Rumble failed, device disconnected");
slouken@13354
  1044
        result = -1;
slouken@13354
  1045
    }
slouken@13354
  1046
slouken@12277
  1047
    return result;
slouken@12088
  1048
}
slouken@12088
  1049
slouken@12088
  1050
static void
slouken@12088
  1051
HIDAPI_JoystickUpdate(SDL_Joystick * joystick)
slouken@12088
  1052
{
slouken@13356
  1053
    /* This is handled in SDL_HIDAPI_UpdateDevices() */
slouken@12088
  1054
}
slouken@12088
  1055
slouken@12088
  1056
static void
slouken@12088
  1057
HIDAPI_JoystickClose(SDL_Joystick * joystick)
slouken@12088
  1058
{
slouken@13354
  1059
    if (joystick->hwdata) {
slouken@13354
  1060
        SDL_HIDAPI_Device *device = joystick->hwdata->device;
slouken@12896
  1061
slouken@13481
  1062
        /* Wait for pending rumble to complete */
slouken@13481
  1063
        while (SDL_AtomicGet(&device->rumble_pending) > 0) {
slouken@13481
  1064
            SDL_Delay(10);
slouken@13481
  1065
        }
slouken@13481
  1066
slouken@13354
  1067
        device->driver->CloseJoystick(device, joystick);
slouken@13354
  1068
slouken@13354
  1069
        SDL_free(joystick->hwdata);
slouken@13354
  1070
        joystick->hwdata = NULL;
slouken@13354
  1071
    }
slouken@12088
  1072
}
slouken@12088
  1073
slouken@12088
  1074
static void
slouken@12088
  1075
HIDAPI_JoystickQuit(void)
slouken@12088
  1076
{
slouken@12088
  1077
    int i;
slouken@12088
  1078
slouken@13354
  1079
    shutting_down = SDL_TRUE;
slouken@13354
  1080
slouken@12110
  1081
    HIDAPI_ShutdownDiscovery();
slouken@12110
  1082
slouken@12088
  1083
    while (SDL_HIDAPI_devices) {
slouken@13354
  1084
        HIDAPI_DelDevice(SDL_HIDAPI_devices);
slouken@12088
  1085
    }
slouken@13481
  1086
slouken@13481
  1087
    SDL_HIDAPI_QuitRumble();
slouken@13481
  1088
slouken@12088
  1089
    for (i = 0; i < SDL_arraysize(SDL_HIDAPI_drivers); ++i) {
slouken@12088
  1090
        SDL_HIDAPI_DeviceDriver *driver = SDL_HIDAPI_drivers[i];
slouken@12088
  1091
        SDL_DelHintCallback(driver->hint, SDL_HIDAPIDriverHintChanged, NULL);
slouken@12088
  1092
    }
slouken@12088
  1093
    SDL_DelHintCallback(SDL_HINT_JOYSTICK_HIDAPI,
slouken@12088
  1094
                        SDL_HIDAPIDriverHintChanged, NULL);
slouken@12088
  1095
slouken@12088
  1096
    hid_exit();
aeikum@12972
  1097
slouken@13354
  1098
    /* Make sure the drivers cleaned up properly */
slouken@13354
  1099
    SDL_assert(SDL_HIDAPI_numjoysticks == 0);
slouken@13354
  1100
slouken@13354
  1101
    shutting_down = SDL_FALSE;
aeikum@12972
  1102
    initialized = SDL_FALSE;
slouken@12088
  1103
}
slouken@12088
  1104
slouken@12088
  1105
SDL_JoystickDriver SDL_HIDAPI_JoystickDriver =
slouken@12088
  1106
{
slouken@12088
  1107
    HIDAPI_JoystickInit,
slouken@12088
  1108
    HIDAPI_JoystickGetCount,
slouken@12088
  1109
    HIDAPI_JoystickDetect,
slouken@12088
  1110
    HIDAPI_JoystickGetDeviceName,
slouken@12359
  1111
    HIDAPI_JoystickGetDevicePlayerIndex,
slouken@13369
  1112
    HIDAPI_JoystickSetDevicePlayerIndex,
slouken@12088
  1113
    HIDAPI_JoystickGetDeviceGUID,
slouken@12088
  1114
    HIDAPI_JoystickGetDeviceInstanceID,
slouken@12088
  1115
    HIDAPI_JoystickOpen,
slouken@12088
  1116
    HIDAPI_JoystickRumble,
slouken@12088
  1117
    HIDAPI_JoystickUpdate,
slouken@12088
  1118
    HIDAPI_JoystickClose,
slouken@12088
  1119
    HIDAPI_JoystickQuit,
slouken@12088
  1120
};
slouken@12088
  1121
slouken@12088
  1122
#endif /* SDL_JOYSTICK_HIDAPI */
slouken@12088
  1123
slouken@12088
  1124
/* vi: set ts=4 sw=4 expandtab: */