Mac and Linux SDL2 binary snapshots
Edward Rudd
2021-06-15 dec7875a6e23212021e4d9080330a42832dfe02a
source/src/joystick/hidapi/SDL_hidapijoystick.c
@@ -22,11 +22,9 @@
#ifdef SDL_JOYSTICK_HIDAPI
#include "SDL_assert.h"
#include "SDL_atomic.h"
#include "SDL_endian.h"
#include "SDL_hints.h"
#include "SDL_log.h"
#include "SDL_thread.h"
#include "SDL_timer.h"
#include "SDL_joystick.h"
@@ -37,6 +35,7 @@
#if defined(__WIN32__)
#include "../../core/windows/SDL_windows.h"
#include "../windows/SDL_rawinputjoystick_c.h"
#endif
#if defined(__MACOSX__)
@@ -62,6 +61,9 @@
static SDL_HIDAPI_DeviceDriver *SDL_HIDAPI_drivers[] = {
#ifdef SDL_JOYSTICK_HIDAPI_PS4
    &SDL_HIDAPI_DriverPS4,
#endif
#ifdef SDL_JOYSTICK_HIDAPI_PS5
    &SDL_HIDAPI_DriverPS5,
#endif
#ifdef SDL_JOYSTICK_HIDAPI_STEAM
    &SDL_HIDAPI_DriverSteam,
@@ -189,7 +191,7 @@
#if defined(__WIN32__)
    SDL_HIDAPI_discovery.m_nThreadID = SDL_ThreadID();
    SDL_memset(&SDL_HIDAPI_discovery.m_wndClass, 0x0, sizeof(SDL_HIDAPI_discovery.m_wndClass));
    SDL_zero(SDL_HIDAPI_discovery.m_wndClass);
    SDL_HIDAPI_discovery.m_wndClass.hInstance = GetModuleHandle(NULL);
    SDL_HIDAPI_discovery.m_wndClass.lpszClassName = "SDL_HIDAPI_DEVICE_DETECTION";
    SDL_HIDAPI_discovery.m_wndClass.lpfnWndProc = ControllerWndProc;      /* This function is called by windows */
@@ -200,8 +202,8 @@
    {
        DEV_BROADCAST_DEVICEINTERFACE_A devBroadcast;
        SDL_memset( &devBroadcast, 0x0, sizeof( devBroadcast ) );
        SDL_zero(devBroadcast);
        devBroadcast.dbcc_size = sizeof( devBroadcast );
        devBroadcast.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE;
        devBroadcast.dbcc_classguid = GUID_DEVINTERFACE_USB_DEVICE;
@@ -387,6 +389,27 @@
#endif
}
void
HIDAPI_DumpPacket(const char *prefix, Uint8 *data, int size)
{
    int i;
    char *buffer;
    size_t length = SDL_strlen(prefix) + 11*(USB_PACKET_LENGTH/8) + (5*USB_PACKET_LENGTH*2) + 1 + 1;
    int start = 0, amount = size;
    buffer = (char *)SDL_malloc(length);
    SDL_snprintf(buffer, length, prefix, size);
    for (i = start; i < start+amount; ++i) {
        if ((i % 8) == 0) {
            SDL_snprintf(&buffer[SDL_strlen(buffer)], length - SDL_strlen(buffer), "\n%.2d:      ", i);
        }
        SDL_snprintf(&buffer[SDL_strlen(buffer)], length - SDL_strlen(buffer), " 0x%.2x", data[i]);
    }
    SDL_strlcat(buffer, "\n", length);
    SDL_Log("%s", buffer);
    SDL_free(buffer);
}
static void HIDAPI_JoystickDetect(void);
static void HIDAPI_JoystickClose(SDL_Joystick * joystick);
@@ -419,11 +442,13 @@
        return NULL;
    }
    if (device->usage_page && device->usage_page != USAGE_PAGE_GENERIC_DESKTOP) {
        return NULL;
    }
    if (device->usage && device->usage != USAGE_JOYSTICK && device->usage != USAGE_GAMEPAD && device->usage != USAGE_MULTIAXISCONTROLLER) {
        return NULL;
    if (device->vendor_id != USB_VENDOR_VALVE) {
        if (device->usage_page && device->usage_page != USAGE_PAGE_GENERIC_DESKTOP) {
            return NULL;
        }
        if (device->usage && device->usage != USAGE_JOYSTICK && device->usage != USAGE_GAMEPAD && device->usage != USAGE_MULTIAXISCONTROLLER) {
            return NULL;
        }
    }
    type = SDL_GetJoystickGameControllerType(device->name, device->vendor_id, device->product_id, device->interface_number, device->interface_class, device->interface_subclass, device->interface_protocol);
@@ -561,7 +586,7 @@
    }
#if defined(__MACOSX__) || defined(__IPHONEOS__) || defined(__TVOS__)
   /* The hidapi framwork is weak-linked on Apple platforms */
    /* The hidapi framwork is weak-linked on Apple platforms */
    int HID_API_EXPORT HID_API_CALL hid_init(void) __attribute__((weak_import));
    if (hid_init == NULL) {
@@ -615,7 +640,7 @@
void
HIDAPI_JoystickDisconnected(SDL_HIDAPI_Device *device, SDL_JoystickID joystickID)
{
    int i;
    int i, size;
    for (i = 0; i < device->num_joysticks; ++i) {
        if (device->joysticks[i] == joystickID) {
@@ -624,8 +649,10 @@
                HIDAPI_JoystickClose(joystick);
            }
            SDL_memmove(&device->joysticks[i], &device->joysticks[i+1], device->num_joysticks - i - 1);
            size = (device->num_joysticks - i - 1) * sizeof(SDL_JoystickID);
            SDL_memmove(&device->joysticks[i], &device->joysticks[i+1], size);
            --device->num_joysticks;
            --SDL_HIDAPI_numjoysticks;
            if (device->num_joysticks == 0) {
                SDL_free(device->joysticks);
@@ -644,6 +671,24 @@
HIDAPI_JoystickGetCount(void)
{
    return SDL_HIDAPI_numjoysticks;
}
static char *
HIDAPI_ConvertString(const wchar_t *wide_string)
{
    char *string = NULL;
    if (wide_string) {
        string = SDL_iconv_string("UTF-8", "WCHAR_T", (char*)wide_string, (SDL_wcslen(wide_string)+1)*sizeof(wchar_t));
        if (!string) {
            if (sizeof(wchar_t) == sizeof(Uint16)) {
                string = SDL_iconv_string("UTF-8", "UCS-2-INTERNAL", (char*)wide_string, (SDL_wcslen(wide_string)+1)*sizeof(wchar_t));
            } else if (sizeof(wchar_t) == sizeof(Uint32)) {
                string = SDL_iconv_string("UTF-8", "UCS-4-INTERNAL", (char*)wide_string, (SDL_wcslen(wide_string)+1)*sizeof(wchar_t));
            }
        }
    }
    return string;
}
static void
@@ -698,59 +743,64 @@
    device->dev_lock = SDL_CreateMutex();
    /* Need the device name before getting the driver to know whether to ignore this device */
    if (!device->name) {
        const char *name = SDL_GetCustomJoystickName(device->vendor_id, device->product_id);
        if (name) {
            device->name = SDL_strdup(name);
        }
    }
    if (!device->name && info->manufacturer_string && info->product_string) {
        const char *manufacturer_remapped;
        char *manufacturer_string = SDL_iconv_string("UTF-8", "WCHAR_T", (char*)info->manufacturer_string, (SDL_wcslen(info->manufacturer_string)+1)*sizeof(wchar_t));
        char *product_string = SDL_iconv_string("UTF-8", "WCHAR_T", (char*)info->product_string, (SDL_wcslen(info->product_string)+1)*sizeof(wchar_t));
        if (!manufacturer_string && !product_string) {
            if (sizeof(wchar_t) == sizeof(Uint16)) {
                manufacturer_string = SDL_iconv_string("UTF-8", "UCS-2-INTERNAL", (char*)info->manufacturer_string, (SDL_wcslen(info->manufacturer_string)+1)*sizeof(wchar_t));
                product_string = SDL_iconv_string("UTF-8", "UCS-2-INTERNAL", (char*)info->product_string, (SDL_wcslen(info->product_string)+1)*sizeof(wchar_t));
            } else if (sizeof(wchar_t) == sizeof(Uint32)) {
                manufacturer_string = SDL_iconv_string("UTF-8", "UCS-4-INTERNAL", (char*)info->manufacturer_string, (SDL_wcslen(info->manufacturer_string)+1)*sizeof(wchar_t));
                product_string = SDL_iconv_string("UTF-8", "UCS-4-INTERNAL", (char*)info->product_string, (SDL_wcslen(info->product_string)+1)*sizeof(wchar_t));
    {
        char *manufacturer_string = HIDAPI_ConvertString(info->manufacturer_string);
        char *product_string = HIDAPI_ConvertString(info->product_string);
        char *serial_number = HIDAPI_ConvertString(info->serial_number);
        device->name = SDL_CreateJoystickName(device->vendor_id, device->product_id, manufacturer_string, product_string);
        if (SDL_strncmp(device->name, "0x", 2) == 0) {
            /* Couldn't find a controller name, try to give it one based on device type */
            const char *name = NULL;
            switch (SDL_GetJoystickGameControllerType(NULL, device->vendor_id, device->product_id, device->interface_number, device->interface_class, device->interface_subclass, device->interface_protocol)) {
            case SDL_CONTROLLER_TYPE_XBOX360:
                name = "Xbox 360 Controller";
                break;
            case SDL_CONTROLLER_TYPE_XBOXONE:
                name = "Xbox One Controller";
                break;
            case SDL_CONTROLLER_TYPE_PS3:
                name = "PS3 Controller";
                break;
            case SDL_CONTROLLER_TYPE_PS4:
                name = "PS4 Controller";
                break;
            case SDL_CONTROLLER_TYPE_PS5:
                name = "PS5 Controller";
                break;
            case SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_PRO:
                name = "Nintendo Switch Pro Controller";
                break;
            default:
                break;
            }
            if (name) {
                SDL_free(device->name);
                device->name = SDL_strdup(name);
            }
        }
        manufacturer_remapped = SDL_GetCustomJoystickManufacturer(manufacturer_string);
        if (manufacturer_remapped != manufacturer_string) {
            SDL_free(manufacturer_string);
            manufacturer_string = SDL_strdup(manufacturer_remapped);
        }
        if (manufacturer_string && product_string) {
            size_t name_size = (SDL_strlen(manufacturer_string) + 1 + SDL_strlen(product_string) + 1);
            device->name = (char *)SDL_malloc(name_size);
            if (device->name) {
                if (SDL_strncasecmp(manufacturer_string, product_string, SDL_strlen(manufacturer_string)) == 0) {
                    SDL_strlcpy(device->name, product_string, name_size);
                } else {
                    SDL_snprintf(device->name, name_size, "%s %s", manufacturer_string, product_string);
                }
            }
        }
        if (manufacturer_string) {
            SDL_free(manufacturer_string);
        }
        if (product_string) {
            SDL_free(product_string);
        }
    }
    if (!device->name) {
        size_t name_size = (6 + 1 + 6 + 1);
        device->name = (char *)SDL_malloc(name_size);
        if (serial_number && *serial_number) {
            device->serial = serial_number;
        } else {
            SDL_free(serial_number);
        }
        if (!device->name) {
            SDL_free(device->serial);
            SDL_free(device->path);
            SDL_free(device);
            return;
        }
        SDL_snprintf(device->name, name_size, "0x%.4x/0x%.4x", info->vendor_id, info->product_id);
    }
    /* Add it to the list */
@@ -763,7 +813,7 @@
    HIDAPI_SetupDeviceDriver(device);
#ifdef DEBUG_HIDAPI
    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");
    SDL_Log("Added HIDAPI device '%s' VID 0x%.4x, PID 0x%.4x, version %d, serial %s, 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->serial ? device->serial : "NONE", 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");
#endif
}
@@ -772,6 +822,11 @@
HIDAPI_DelDevice(SDL_HIDAPI_Device *device)
{
    SDL_HIDAPI_Device *curr, *last;
#ifdef DEBUG_HIDAPI
    SDL_Log("Removing HIDAPI device '%s' VID 0x%.4x, PID 0x%.4x, version %d, serial %s, 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->serial ? device->serial : "NONE", 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");
#endif
    for (curr = SDL_HIDAPI_devices, last = NULL; curr; last = curr, curr = curr->next) {
        if (curr == device) {
            if (last) {
@@ -782,7 +837,13 @@
            HIDAPI_CleanupDeviceDriver(device);
            /* Make sure the rumble thread is done with this device */
            while (SDL_AtomicGet(&device->rumble_pending) > 0) {
                SDL_Delay(10);
            }
            SDL_DestroyMutex(device->dev_lock);
            SDL_free(device->serial);
            SDL_free(device->name);
            SDL_free(device->path);
            SDL_free(device);
@@ -836,6 +897,36 @@
    SDL_UnlockJoysticks();
}
static SDL_bool
HIDAPI_IsEquivalentToDevice(Uint16 vendor_id, Uint16 product_id, SDL_HIDAPI_Device *device)
{
    if (vendor_id == device->vendor_id && product_id == device->product_id) {
        return SDL_TRUE;
    }
    if (vendor_id == USB_VENDOR_MICROSOFT) {
        /* If we're looking for the wireless XBox 360 controller, also look for the dongle */
        if (product_id == 0x02a1 && device->product_id == 0x0719) {
            return SDL_TRUE;
        }
        /* If we're looking for the raw input Xbox One controller, match it against any other Xbox One controller */
        if (product_id == USB_PRODUCT_XBOX_ONE_RAW_INPUT_CONTROLLER &&
            SDL_GetJoystickGameControllerType(device->name, device->vendor_id, device->product_id, device->interface_number, device->interface_class, device->interface_subclass, device->interface_protocol) == SDL_CONTROLLER_TYPE_XBOXONE) {
            return SDL_TRUE;
        }
        /* If we're looking for an XInput controller, match it against any other Xbox controller */
        if (product_id == USB_PRODUCT_XBOX_ONE_XINPUT_CONTROLLER) {
            SDL_GameControllerType type = SDL_GetJoystickGameControllerType(device->name, device->vendor_id, device->product_id, device->interface_number, device->interface_class, device->interface_subclass, device->interface_protocol);
            if (type == SDL_CONTROLLER_TYPE_XBOX360 || type == SDL_CONTROLLER_TYPE_XBOXONE) {
                return SDL_TRUE;
            }
        }
    }
    return SDL_FALSE;
}
SDL_bool
HIDAPI_IsDevicePresent(Uint16 vendor_id, Uint16 product_id, Uint16 version, const char *name)
{
@@ -875,17 +966,13 @@
    SDL_LockJoysticks();
    device = SDL_HIDAPI_devices;
    while (device) {
        if (device->vendor_id == vendor_id && device->product_id == product_id && device->driver) {
        if (device->driver &&
            HIDAPI_IsEquivalentToDevice(vendor_id, product_id, device)) {
            result = SDL_TRUE;
        }
        device = device->next;
    }
    SDL_UnlockJoysticks();
    /* If we're looking for the wireless XBox 360 controller, also look for the dongle */
    if (!result && vendor_id == USB_VENDOR_MICROSOFT && product_id == 0x02a1) {
        return HIDAPI_IsDevicePresent(USB_VENDOR_MICROSOFT, 0x0719, version, name);
    }
#ifdef DEBUG_HIDAPI
    SDL_Log("HIDAPI_IsDevicePresent() returning %s for 0x%.4x / 0x%.4x\n", result ? "true" : "false", vendor_id, product_id);
@@ -920,7 +1007,9 @@
        while (device) {
            if (device->driver) {
                if (SDL_TryLockMutex(device->dev_lock) == 0) {
                    device->updating = SDL_TRUE;
                    device->driver->UpdateDevice(device);
                    device->updating = SDL_FALSE;
                    SDL_UnlockMutex(device->dev_lock);
                }
            }
@@ -1014,6 +1103,10 @@
        return -1;
    }
    if (!joystick->serial && device->serial) {
        joystick->serial = SDL_strdup(device->serial);
    }
    joystick->hwdata = hwdata;
    return 0;
}
@@ -1035,6 +1128,71 @@
    return result;
}
static int
HIDAPI_JoystickRumbleTriggers(SDL_Joystick * joystick, Uint16 left_rumble, Uint16 right_rumble)
{
    int result;
    if (joystick->hwdata) {
        SDL_HIDAPI_Device *device = joystick->hwdata->device;
        result = device->driver->RumbleJoystickTriggers(device, joystick, left_rumble, right_rumble);
    } else {
        SDL_SetError("Rumble failed, device disconnected");
        result = -1;
    }
    return result;
}
static SDL_bool
HIDAPI_JoystickHasLED(SDL_Joystick * joystick)
{
    SDL_bool result = SDL_FALSE;
    if (joystick->hwdata) {
        SDL_HIDAPI_Device *device = joystick->hwdata->device;
        result = device->driver->HasJoystickLED(device, joystick);
    }
    return result;
}
static int
HIDAPI_JoystickSetLED(SDL_Joystick * joystick, Uint8 red, Uint8 green, Uint8 blue)
{
    int result;
    if (joystick->hwdata) {
        SDL_HIDAPI_Device *device = joystick->hwdata->device;
        result = device->driver->SetJoystickLED(device, joystick, red, green, blue);
    } else {
        SDL_SetError("SetLED failed, device disconnected");
        result = -1;
    }
    return result;
}
static int
HIDAPI_JoystickSetSensorsEnabled(SDL_Joystick * joystick, SDL_bool enabled)
{
    int result;
    if (joystick->hwdata) {
        SDL_HIDAPI_Device *device = joystick->hwdata->device;
        result = device->driver->SetJoystickSensorsEnabled(device, joystick, enabled);
    } else {
        SDL_SetError("SetSensorsEnabled failed, device disconnected");
        result = -1;
    }
    return result;
}
static void
HIDAPI_JoystickUpdate(SDL_Joystick * joystick)
{
@@ -1046,10 +1204,21 @@
{
    if (joystick->hwdata) {
        SDL_HIDAPI_Device *device = joystick->hwdata->device;
        int i;
        /* Wait for pending rumble to complete */
        while (SDL_AtomicGet(&device->rumble_pending) > 0) {
            SDL_Delay(10);
        /* Wait up to 30 ms for pending rumble to complete */
        if (device->updating) {
            /* Unlock the device so rumble can complete */
            SDL_UnlockMutex(device->dev_lock);
        }
        for (i = 0; i < 3; ++i) {
            if (SDL_AtomicGet(&device->rumble_pending) > 0) {
                SDL_Delay(10);
            }
        }
        if (device->updating) {
            /* Relock the device */
            SDL_LockMutex(device->dev_lock);
        }
        device->driver->CloseJoystick(device, joystick);
@@ -1068,11 +1237,14 @@
    HIDAPI_ShutdownDiscovery();
    SDL_HIDAPI_QuitRumble();
    while (SDL_HIDAPI_devices) {
        HIDAPI_DelDevice(SDL_HIDAPI_devices);
    }
    SDL_HIDAPI_QuitRumble();
    /* Make sure the drivers cleaned up properly */
    SDL_assert(SDL_HIDAPI_numjoysticks == 0);
    for (i = 0; i < SDL_arraysize(SDL_HIDAPI_drivers); ++i) {
        SDL_HIDAPI_DeviceDriver *driver = SDL_HIDAPI_drivers[i];
@@ -1083,11 +1255,14 @@
    hid_exit();
    /* Make sure the drivers cleaned up properly */
    SDL_assert(SDL_HIDAPI_numjoysticks == 0);
    shutting_down = SDL_FALSE;
    initialized = SDL_FALSE;
}
static SDL_bool
HIDAPI_JoystickGetGamepadMapping(int device_index, SDL_GamepadMapping *out)
{
    return SDL_FALSE;
}
SDL_JoystickDriver SDL_HIDAPI_JoystickDriver =
@@ -1102,9 +1277,14 @@
    HIDAPI_JoystickGetDeviceInstanceID,
    HIDAPI_JoystickOpen,
    HIDAPI_JoystickRumble,
    HIDAPI_JoystickRumbleTriggers,
    HIDAPI_JoystickHasLED,
    HIDAPI_JoystickSetLED,
    HIDAPI_JoystickSetSensorsEnabled,
    HIDAPI_JoystickUpdate,
    HIDAPI_JoystickClose,
    HIDAPI_JoystickQuit,
    HIDAPI_JoystickGetGamepadMapping
};
#endif /* SDL_JOYSTICK_HIDAPI */