Mac and Linux SDL2 binary snapshots
Edward Rudd
2019-04-09 9cd2e9ec8fc0127393dfce9c0359d500c8c238be
source/src/joystick/darwin/SDL_sysjoystick.c
@@ -22,20 +22,18 @@
#ifdef SDL_JOYSTICK_IOKIT
#include <IOKit/hid/IOHIDLib.h>
/* For force feedback testing. */
#include <ForceFeedback/ForceFeedback.h>
#include <ForceFeedback/ForceFeedbackConstants.h>
#include "SDL_events.h"
#include "SDL_joystick.h"
#include "../SDL_sysjoystick.h"
#include "../SDL_joystick_c.h"
#include "SDL_sysjoystick_c.h"
#include "SDL_events.h"
#include "../hidapi/SDL_hidapijoystick_c.h"
#include "../../haptic/darwin/SDL_syshaptic_c.h"    /* For haptic hot plugging */
#define SDL_JOYSTICK_RUNLOOP_MODE CFSTR("SDLJoystick")
#define CONVERT_MAGNITUDE(x)    (((x)*10000) / 0x7FFF)
/* The base object of the HID Manager API */
static IOHIDManagerRef hidman = NULL;
@@ -43,8 +41,60 @@
/* Linked list of all available devices */
static recDevice *gpDeviceList = NULL;
/* static incrementing counter for new joystick devices seen on the system. Devices should start with index 0 */
static int s_joystick_instance_id = -1;
void FreeRumbleEffectData(FFEFFECT *effect)
{
    if (!effect) {
        return;
    }
    SDL_free(effect->rgdwAxes);
    SDL_free(effect->rglDirection);
    SDL_free(effect->lpvTypeSpecificParams);
    SDL_free(effect);
}
FFEFFECT *CreateRumbleEffectData(Sint16 magnitude, Uint32 duration_ms)
{
    FFEFFECT *effect;
    FFPERIODIC *periodic;
    /* Create the effect */
    effect = (FFEFFECT *)SDL_calloc(1, sizeof(*effect));
    if (!effect) {
        return NULL;
    }
    effect->dwSize = sizeof(*effect);
    effect->dwGain = 10000;
    effect->dwFlags = FFEFF_OBJECTOFFSETS;
    effect->dwDuration = duration_ms * 1000; /* In microseconds. */
    effect->dwTriggerButton = FFEB_NOTRIGGER;
    effect->cAxes = 2;
    effect->rgdwAxes = (DWORD *)SDL_calloc(effect->cAxes, sizeof(DWORD));
    if (!effect->rgdwAxes) {
        FreeRumbleEffectData(effect);
        return NULL;
    }
    effect->rglDirection = (LONG *)SDL_calloc(effect->cAxes, sizeof(LONG));
    if (!effect->rglDirection) {
        FreeRumbleEffectData(effect);
        return NULL;
    }
    effect->dwFlags |= FFEFF_CARTESIAN;
    periodic = (FFPERIODIC *)SDL_calloc(1, sizeof(*periodic));
    if (!periodic) {
        FreeRumbleEffectData(effect);
        return NULL;
    }
    periodic->dwMagnitude = CONVERT_MAGNITUDE(magnitude);
    periodic->dwPeriod = 1000000;
    effect->cbTypeSpecificParams = sizeof(*periodic);
    effect->lpvTypeSpecificParams = periodic;
    return effect;
}
static recDevice *GetDeviceForIndex(int device_index)
{
@@ -157,6 +207,19 @@
    recDevice *device = (recDevice *) ctx;
    device->removed = SDL_TRUE;
    device->deviceRef = NULL; // deviceRef was invalidated due to the remove
    if (device->ffeffect_ref) {
        FFDeviceReleaseEffect(device->ffdevice, device->ffeffect_ref);
        device->ffeffect_ref = NULL;
    }
    if (device->ffeffect) {
        FreeRumbleEffectData(device->ffeffect);
        device->ffeffect = NULL;
    }
    if (device->ffdevice) {
        FFReleaseDevice(device->ffdevice);
        device->ffdevice = NULL;
        device->ff_initialized = SDL_FALSE;
    }
#if SDL_HAPTIC_IOKIT
    MacHaptic_MaybeRemoveDevice(device->ffservice);
#endif
@@ -333,8 +396,6 @@
static SDL_bool
GetDeviceInfo(IOHIDDeviceRef hidDevice, recDevice *pDevice)
{
    const Uint16 BUS_USB = 0x03;
    const Uint16 BUS_BLUETOOTH = 0x05;
    Sint32 vendor = 0;
    Sint32 product = 0;
    Sint32 version = 0;
@@ -389,10 +450,17 @@
        CFNumberGetValue(refCF, kCFNumberSInt32Type, &version);
    }
#ifdef SDL_JOYSTICK_HIDAPI
    if (HIDAPI_IsDevicePresent(vendor, product, version)) {
        /* The HIDAPI driver is taking care of this device */
        return 0;
    }
#endif
    SDL_memset(pDevice->guid.data, 0, sizeof(pDevice->guid.data));
    if (vendor && product) {
        *guid16++ = SDL_SwapLE16(BUS_USB);
        *guid16++ = SDL_SwapLE16(SDL_HARDWARE_BUS_USB);
        *guid16++ = 0;
        *guid16++ = SDL_SwapLE16((Uint16)vendor);
        *guid16++ = 0;
@@ -401,7 +469,7 @@
        *guid16++ = SDL_SwapLE16((Uint16)version);
        *guid16++ = 0;
    } else {
        *guid16++ = SDL_SwapLE16(BUS_BLUETOOTH);
        *guid16++ = SDL_SwapLE16(SDL_HARDWARE_BUS_BLUETOOTH);
        *guid16++ = 0;
        SDL_strlcpy((char*)guid16, pDevice->product, sizeof(pDevice->guid.data) - 4);
    }
@@ -444,7 +512,6 @@
    }
    device = (recDevice *) SDL_calloc(1, sizeof(recDevice));
    if (!device) {
        SDL_OutOfMemory();
        return;
@@ -455,8 +522,7 @@
        return;   /* not a device we care about, probably. */
    }
    if (SDL_IsGameControllerNameAndGUID(device->product, device->guid) &&
        SDL_ShouldIgnoreGameController(device->product, device->guid)) {
    if (SDL_ShouldIgnoreJoystick(device->product, device->guid)) {
        SDL_free(device);
        return;
    }
@@ -466,16 +532,16 @@
    IOHIDDeviceScheduleWithRunLoop(ioHIDDeviceObject, CFRunLoopGetCurrent(), SDL_JOYSTICK_RUNLOOP_MODE);
    /* Allocate an instance ID for this device */
    device->instance_id = ++s_joystick_instance_id;
    device->instance_id = SDL_GetNextJoystickInstanceID();
    /* We have to do some storage of the io_service_t for SDL_HapticOpenFromJoystick */
    ioservice = IOHIDDeviceGetService(ioHIDDeviceObject);
#if SDL_HAPTIC_IOKIT
    if ((ioservice) && (FFIsForceFeedback(ioservice) == FF_OK)) {
        device->ffservice = ioservice;
#if SDL_HAPTIC_IOKIT
        MacHaptic_MaybeAddDevice(ioservice);
    }
#endif
    }
    /* Add device to the end of the list */
    if ( !gpDeviceList ) {
@@ -492,7 +558,7 @@
        ++device_index;  /* bump by one since we counted by pNext. */
    }
    SDL_PrivateJoystickAdded(device_index);
    SDL_PrivateJoystickAdded(device->instance_id);
}
static SDL_bool
@@ -577,13 +643,8 @@
}
/* Function to scan the system for joysticks.
 * Joystick 0 should be the system default joystick.
 * This function should return the number of available joysticks, or -1
 * on an unrecoverable fatal error.
 */
int
SDL_SYS_JoystickInit(void)
static int
DARWIN_JoystickInit(void)
{
    if (gpDeviceList) {
        return SDL_SetError("Joystick: Device list already inited.");
@@ -593,12 +654,11 @@
        return SDL_SetError("Joystick: Couldn't initialize HID Manager");
    }
    return SDL_SYS_NumJoysticks();
    return 0;
}
/* Function to return the number of joystick devices plugged in right now */
int
SDL_SYS_NumJoysticks(void)
static int
DARWIN_JoystickGetCount(void)
{
    recDevice *device = gpDeviceList;
    int nJoySticks = 0;
@@ -613,10 +673,8 @@
    return nJoySticks;
}
/* Function to cause any queued joystick insertions to be processed
 */
void
SDL_SYS_JoystickDetect(void)
static void
DARWIN_JoystickDetect(void)
{
    recDevice *device = gpDeviceList;
    while (device) {
@@ -627,37 +685,49 @@
        }
    }
   /* run this after the checks above so we don't set device->removed and delete the device before
      SDL_SYS_JoystickUpdate can run to clean up the SDL_Joystick object that owns this device */
   while (CFRunLoopRunInMode(SDL_JOYSTICK_RUNLOOP_MODE,0,TRUE) == kCFRunLoopRunHandledSource) {
      /* no-op. Pending callbacks will fire in CFRunLoopRunInMode(). */
   }
    /* run this after the checks above so we don't set device->removed and delete the device before
       DARWIN_JoystickUpdate can run to clean up the SDL_Joystick object that owns this device */
    while (CFRunLoopRunInMode(SDL_JOYSTICK_RUNLOOP_MODE,0,TRUE) == kCFRunLoopRunHandledSource) {
        /* no-op. Pending callbacks will fire in CFRunLoopRunInMode(). */
    }
}
/* Function to get the device-dependent name of a joystick */
const char *
SDL_SYS_JoystickNameForDeviceIndex(int device_index)
DARWIN_JoystickGetDeviceName(int device_index)
{
    recDevice *device = GetDeviceForIndex(device_index);
    return device ? device->product : "UNKNOWN";
}
/* Function to return the instance id of the joystick at device_index
 */
SDL_JoystickID
SDL_SYS_GetInstanceIdOfDeviceIndex(int device_index)
static int
DARWIN_JoystickGetDevicePlayerIndex(int device_index)
{
    return -1;
}
static SDL_JoystickGUID
DARWIN_JoystickGetDeviceGUID( int device_index )
{
    recDevice *device = GetDeviceForIndex(device_index);
    SDL_JoystickGUID guid;
    if (device) {
        guid = device->guid;
    } else {
        SDL_zero(guid);
    }
    return guid;
}
static SDL_JoystickID
DARWIN_JoystickGetDeviceInstanceID(int device_index)
{
    recDevice *device = GetDeviceForIndex(device_index);
    return device ? device->instance_id : 0;
}
/* Function to open a joystick for use.
 * The joystick to open is specified by the device index.
 * This should fill the nbuttons and naxes fields of the joystick structure.
 * It returns 0, or -1 if there is an error.
 */
int
SDL_SYS_JoystickOpen(SDL_Joystick * joystick, int device_index)
static int
DARWIN_JoystickOpen(SDL_Joystick * joystick, int device_index)
{
    recDevice *device = GetDeviceForIndex(device_index);
@@ -672,22 +742,138 @@
    return 0;
}
/* Function to query if the joystick is currently attached
 * It returns SDL_TRUE if attached, SDL_FALSE otherwise.
/*
 * Like strerror but for force feedback errors.
 */
SDL_bool
SDL_SYS_JoystickAttached(SDL_Joystick * joystick)
static const char *
FFStrError(unsigned int err)
{
    return joystick->hwdata != NULL;
    switch (err) {
    case FFERR_DEVICEFULL:
        return "device full";
    /* This should be valid, but for some reason isn't defined... */
    /* case FFERR_DEVICENOTREG:
        return "device not registered"; */
    case FFERR_DEVICEPAUSED:
        return "device paused";
    case FFERR_DEVICERELEASED:
        return "device released";
    case FFERR_EFFECTPLAYING:
        return "effect playing";
    case FFERR_EFFECTTYPEMISMATCH:
        return "effect type mismatch";
    case FFERR_EFFECTTYPENOTSUPPORTED:
        return "effect type not supported";
    case FFERR_GENERIC:
        return "undetermined error";
    case FFERR_HASEFFECTS:
        return "device has effects";
    case FFERR_INCOMPLETEEFFECT:
        return "incomplete effect";
    case FFERR_INTERNAL:
        return "internal fault";
    case FFERR_INVALIDDOWNLOADID:
        return "invalid download id";
    case FFERR_INVALIDPARAM:
        return "invalid parameter";
    case FFERR_MOREDATA:
        return "more data";
    case FFERR_NOINTERFACE:
        return "interface not supported";
    case FFERR_NOTDOWNLOADED:
        return "effect is not downloaded";
    case FFERR_NOTINITIALIZED:
        return "object has not been initialized";
    case FFERR_OUTOFMEMORY:
        return "out of memory";
    case FFERR_UNPLUGGED:
        return "device is unplugged";
    case FFERR_UNSUPPORTED:
        return "function call unsupported";
    case FFERR_UNSUPPORTEDAXIS:
        return "axis unsupported";
    default:
        return "unknown error";
    }
}
/* Function to update the state of a joystick - called as a device poll.
 * This function shouldn't update the joystick structure directly,
 * but instead should call SDL_PrivateJoystick*() to deliver events
 * and update joystick device state.
 */
void
SDL_SYS_JoystickUpdate(SDL_Joystick * joystick)
static int
DARWIN_JoystickInitRumble(recDevice *device, Sint16 magnitude, Uint32 duration_ms)
{
    HRESULT result;
    if (!device->ffdevice) {
        result = FFCreateDevice(device->ffservice, &device->ffdevice);
        if (result != FF_OK) {
            return SDL_SetError("Unable to create force feedback device from service: %s", FFStrError(result));
        }
    }
    /* Reset and then enable actuators */
    result = FFDeviceSendForceFeedbackCommand(device->ffdevice, FFSFFC_RESET);
    if (result != FF_OK) {
        return SDL_SetError("Unable to reset force feedback device: %s", FFStrError(result));
    }
    result = FFDeviceSendForceFeedbackCommand(device->ffdevice, FFSFFC_SETACTUATORSON);
    if (result != FF_OK) {
        return SDL_SetError("Unable to enable force feedback actuators: %s", FFStrError(result));
    }
    /* Create the effect */
    device->ffeffect = CreateRumbleEffectData(magnitude, duration_ms);
    if (!device->ffeffect) {
        return SDL_OutOfMemory();
    }
    result = FFDeviceCreateEffect(device->ffdevice, kFFEffectType_Sine_ID,
                               device->ffeffect, &device->ffeffect_ref);
    if (result != FF_OK) {
        return SDL_SetError("Haptic: Unable to create effect: %s", FFStrError(result));
    }
    return 0;
}
static int
DARWIN_JoystickRumble(SDL_Joystick * joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble, Uint32 duration_ms)
{
    HRESULT result;
    recDevice *device = joystick->hwdata;
    /* Scale and average the two rumble strengths */
    Sint16 magnitude = (Sint16)(((low_frequency_rumble / 2) + (high_frequency_rumble / 2)) / 2);
    if (!device->ffservice) {
        return SDL_Unsupported();
    }
    if (device->ff_initialized) {
        FFPERIODIC *periodic = ((FFPERIODIC *)device->ffeffect->lpvTypeSpecificParams);
        device->ffeffect->dwDuration = duration_ms * 1000; /* In microseconds. */
        periodic->dwMagnitude = CONVERT_MAGNITUDE(magnitude);
        result = FFEffectSetParameters(device->ffeffect_ref, device->ffeffect,
                                    (FFEP_DURATION | FFEP_TYPESPECIFICPARAMS));
        if (result != FF_OK) {
            return SDL_SetError("Unable to update rumble effect: %s", FFStrError(result));
        }
    } else {
        if (DARWIN_JoystickInitRumble(device, magnitude, duration_ms) < 0) {
            return -1;
        }
        device->ff_initialized = SDL_TRUE;
    }
    result = FFEffectStart(device->ffeffect_ref, 1, 0);
    if (result != FF_OK) {
        return SDL_SetError("Unable to run the rumble effect: %s", FFStrError(result));
    }
    return 0;
}
static void
DARWIN_JoystickUpdate(SDL_Joystick * joystick)
{
    recDevice *device = joystick->hwdata;
    recElement *element;
@@ -792,15 +978,13 @@
    }
}
/* Function to close a joystick after use */
void
SDL_SYS_JoystickClose(SDL_Joystick * joystick)
static void
DARWIN_JoystickClose(SDL_Joystick * joystick)
{
}
/* Function to perform any system-specific joystick related cleanup */
void
SDL_SYS_JoystickQuit(void)
static void
DARWIN_JoystickQuit(void)
{
    while (FreeDevice(gpDeviceList)) {
        /* spin */
@@ -814,23 +998,21 @@
    }
}
SDL_JoystickGUID SDL_SYS_JoystickGetDeviceGUID( int device_index )
SDL_JoystickDriver SDL_DARWIN_JoystickDriver =
{
    recDevice *device = GetDeviceForIndex(device_index);
    SDL_JoystickGUID guid;
    if (device) {
        guid = device->guid;
    } else {
        SDL_zero(guid);
    }
    return guid;
}
SDL_JoystickGUID SDL_SYS_JoystickGetGUID(SDL_Joystick *joystick)
{
    return joystick->hwdata->guid;
}
    DARWIN_JoystickInit,
    DARWIN_JoystickGetCount,
    DARWIN_JoystickDetect,
    DARWIN_JoystickGetDeviceName,
    DARWIN_JoystickGetDevicePlayerIndex,
    DARWIN_JoystickGetDeviceGUID,
    DARWIN_JoystickGetDeviceInstanceID,
    DARWIN_JoystickOpen,
    DARWIN_JoystickRumble,
    DARWIN_JoystickUpdate,
    DARWIN_JoystickClose,
    DARWIN_JoystickQuit,
};
#endif /* SDL_JOYSTICK_IOKIT */