SDL  2.0
SDL_sysjoystick.m File Reference
#include "../../SDL_internal.h"
#include "SDL_sysjoystick_c.h"
#include "SDL_config_iphoneos.h"
#include "SDL_assert.h"
#include "SDL_events.h"
#include "SDL_joystick.h"
#include "SDL_hints.h"
#include "SDL_stdinc.h"
#include "../SDL_sysjoystick.h"
#include "../SDL_joystick_c.h"
#include "../../events/SDL_events_c.h"
#import <CoreMotion/CoreMotion.h>
+ Include dependency graph for SDL_sysjoystick.m:

Go to the source code of this file.

Functions

static SDL_JoystickDeviceItemGetDeviceForIndex (int device_index)
static void IOS_AddMFIJoystickDevice (SDL_JoystickDeviceItem *device, GCController *controller)
static void IOS_AddJoystickDevice (GCController *controller, SDL_bool accelerometer)
static SDL_JoystickDeviceItemIOS_RemoveJoystickDevice (SDL_JoystickDeviceItem *device)
static int IOS_JoystickInit (void)
static int IOS_JoystickGetCount (void)
static void IOS_JoystickDetect (void)
static const char * IOS_JoystickGetDeviceName (int device_index)
static int IOS_JoystickGetDevicePlayerIndex (int device_index)
static SDL_JoystickGUID IOS_JoystickGetDeviceGUID (int device_index)
static SDL_JoystickID IOS_JoystickGetDeviceInstanceID (int device_index)
static int IOS_JoystickOpen (SDL_Joystick *joystick, int device_index)
static void IOS_AccelerometerUpdate (SDL_Joystick *joystick)
static void IOS_MFIJoystickUpdate (SDL_Joystick *joystick)
static int IOS_JoystickRumble (SDL_Joystick *joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble, Uint32 duration_ms)
static void IOS_JoystickUpdate (SDL_Joystick *joystick)
static void IOS_JoystickClose (SDL_Joystick *joystick)
static void IOS_JoystickQuit (void)

Variables

static const char * accelerometerName = "iOS Accelerometer"
static CMMotionManager * motionManager = nil
static SDL_JoystickDeviceItemdeviceList = NULL
static int numjoysticks = 0
int SDL_AppleTVRemoteOpenedAsJoystick = 0
SDL_JoystickDriver SDL_IOS_JoystickDriver

Function Documentation

static SDL_JoystickDeviceItem* GetDeviceForIndex ( int  device_index)
static

Definition at line 64 of file SDL_sysjoystick.m.

References device, deviceList, i, recDevice::next, and NULL.

Referenced by IOS_JoystickGetDeviceGUID(), IOS_JoystickGetDeviceInstanceID(), IOS_JoystickGetDeviceName(), and IOS_JoystickOpen().

{
int i = 0;
while (i < device_index) {
if (device == NULL) {
return NULL;
}
device = device->next;
i++;
}
return device;
}
static void IOS_AccelerometerUpdate ( SDL_Joystick *  joystick)
static

Definition at line 435 of file SDL_sysjoystick.m.

References motionManager, SDL_IPHONE_MAX_GFORCE, SDL_max, SDL_min, and SDL_PrivateJoystickAxis().

Referenced by IOS_JoystickUpdate().

{
#if !TARGET_OS_TV
const float maxgforce = SDL_IPHONE_MAX_GFORCE;
const SInt16 maxsint16 = 0x7FFF;
CMAcceleration accel;
@autoreleasepool {
if (!motionManager.isAccelerometerActive) {
return;
}
accel = motionManager.accelerometerData.acceleration;
}
/*
Convert accelerometer data from floating point to Sint16, which is what
the joystick system expects.
To do the conversion, the data is first clamped onto the interval
[-SDL_IPHONE_MAX_G_FORCE, SDL_IPHONE_MAX_G_FORCE], then the data is multiplied
by MAX_SINT16 so that it is mapped to the full range of an Sint16.
You can customize the clamped range of this function by modifying the
SDL_IPHONE_MAX_GFORCE macro in SDL_config_iphoneos.h.
Once converted to Sint16, the accelerometer data no longer has coherent
units. You can convert the data back to units of g-force by multiplying
it in your application's code by SDL_IPHONE_MAX_GFORCE / 0x7FFF.
*/
/* clamp the data */
accel.x = SDL_min(SDL_max(accel.x, -maxgforce), maxgforce);
accel.y = SDL_min(SDL_max(accel.y, -maxgforce), maxgforce);
accel.z = SDL_min(SDL_max(accel.z, -maxgforce), maxgforce);
/* pass in data mapped to range of SInt16 */
SDL_PrivateJoystickAxis(joystick, 0, (accel.x / maxgforce) * maxsint16);
SDL_PrivateJoystickAxis(joystick, 1, -(accel.y / maxgforce) * maxsint16);
SDL_PrivateJoystickAxis(joystick, 2, (accel.z / maxgforce) * maxsint16);
#endif /* !TARGET_OS_TV */
}
static void IOS_AddJoystickDevice ( GCController *  controller,
SDL_bool  accelerometer 
)
static

Definition at line 157 of file SDL_sysjoystick.m.

References recDevice::accelerometer, accelerometerName, recDevice::controller, SDL_JoystickGUID::data, device, deviceList, recDevice::guid, recDevice::instance_id, IOS_AddMFIJoystickDevice(), recDevice::name, recDevice::naxes, recDevice::nbuttons, recDevice::next, recDevice::nhats, NULL, numjoysticks, SDL_calloc, SDL_free, SDL_GetHintBoolean, SDL_GetNextJoystickInstanceID(), SDL_HINT_TV_REMOTE_AS_JOYSTICK, SDL_memcpy, SDL_min, SDL_PrivateJoystickAdded(), SDL_strdup, SDL_strlen, and SDL_TRUE.

Referenced by IOS_JoystickInit().

{
#if TARGET_OS_TV
/* Ignore devices that aren't actually controllers (e.g. remotes), they'll be handled as keyboard input */
if (controller && !controller.extendedGamepad && !controller.gamepad && controller.microGamepad) {
return;
}
}
#endif
while (device != NULL) {
if (device->controller == controller) {
return;
}
device = device->next;
}
if (device == NULL) {
return;
}
device->accelerometer = accelerometer;
if (accelerometer) {
#if TARGET_OS_TV
SDL_free(device);
return;
#else
device->naxes = 3; /* Device acceleration in the x, y, and z axes. */
device->nhats = 0;
device->nbuttons = 0;
/* Use the accelerometer name as a GUID. */
SDL_memcpy(&device->guid.data, device->name, SDL_min(sizeof(SDL_JoystickGUID), SDL_strlen(device->name)));
#endif /* TARGET_OS_TV */
} else if (controller) {
IOS_AddMFIJoystickDevice(device, controller);
}
if (deviceList == NULL) {
} else {
while (lastdevice->next != NULL) {
lastdevice = lastdevice->next;
}
lastdevice->next = device;
}
}
static void IOS_AddMFIJoystickDevice ( SDL_JoystickDeviceItem device,
GCController *  controller 
)
static

Definition at line 81 of file SDL_sysjoystick.m.

References recDevice::controller, SDL_JoystickGUID::data, recDevice::guid, recDevice::name, recDevice::naxes, recDevice::nbuttons, recDevice::nhats, NULL, SDL_FALSE, SDL_GetHintBoolean, SDL_HARDWARE_BUS_BLUETOOTH, SDL_HINT_APPLE_TV_REMOTE_ALLOW_ROTATION, SDL_strdup, and SDL_SwapLE16.

Referenced by IOS_AddJoystickDevice().

{
#ifdef SDL_JOYSTICK_MFI
const Uint16 VENDOR_APPLE = 0x05AC;
Uint16 *guid16 = (Uint16 *)device->guid.data;
Uint16 vendor = 0;
Uint16 product = 0;
Uint16 version = 0;
Uint8 subtype = 0;
const char *name = NULL;
/* Explicitly retain the controller because SDL_JoystickDeviceItem is a
* struct, and ARC doesn't work with structs. */
device->controller = (__bridge GCController *) CFBridgingRetain(controller);
if (controller.vendorName) {
name = controller.vendorName.UTF8String;
}
if (!name) {
name = "MFi Gamepad";
}
device->name = SDL_strdup(name);
if (controller.extendedGamepad) {
vendor = VENDOR_APPLE;
product = 1;
subtype = 1;
device->naxes = 6; /* 2 thumbsticks and 2 triggers */
device->nhats = 1; /* d-pad */
device->nbuttons = 7; /* ABXY, shoulder buttons, pause button */
} else if (controller.gamepad) {
vendor = VENDOR_APPLE;
product = 2;
subtype = 2;
device->naxes = 0; /* no traditional analog inputs */
device->nhats = 1; /* d-pad */
device->nbuttons = 7; /* ABXY, shoulder buttons, pause button */
}
#if TARGET_OS_TV
else if (controller.microGamepad) {
vendor = VENDOR_APPLE;
product = 3;
subtype = 3;
device->naxes = 2; /* treat the touch surface as two axes */
device->nhats = 0; /* apparently the touch surface-as-dpad is buggy */
device->nbuttons = 3; /* AX, pause button */
controller.microGamepad.allowsRotation = SDL_GetHintBoolean(SDL_HINT_APPLE_TV_REMOTE_ALLOW_ROTATION, SDL_FALSE);
}
#endif /* TARGET_OS_TV */
/* We only need 16 bits for each of these; space them out to fill 128. */
/* Byteswap so devices get same GUID on little/big endian platforms. */
*guid16++ = 0;
*guid16++ = SDL_SwapLE16(vendor);
*guid16++ = 0;
*guid16++ = SDL_SwapLE16(product);
*guid16++ = 0;
*guid16++ = SDL_SwapLE16(version);
*guid16++ = 0;
/* Note that this is an MFI controller and what subtype it is */
device->guid.data[14] = 'm';
device->guid.data[15] = subtype;
/* This will be set when the first button press of the controller is
* detected. */
controller.playerIndex = -1;
#endif /* SDL_JOYSTICK_MFI */
}
static void IOS_JoystickClose ( SDL_Joystick *  joystick)
static

Definition at line 655 of file SDL_sysjoystick.m.

References recDevice::accelerometer, recDevice::controller, device, recDevice::joystick, NULL, recDevice::remote, and SDL_AppleTVRemoteOpenedAsJoystick.

{
SDL_JoystickDeviceItem *device = joystick->hwdata;
if (device == NULL) {
return;
}
device->joystick = NULL;
@autoreleasepool {
if (device->accelerometer) {
#if !TARGET_OS_TV
[motionManager stopAccelerometerUpdates];
#endif /* !TARGET_OS_TV */
} else if (device->controller) {
#ifdef SDL_JOYSTICK_MFI
GCController *controller = device->controller;
controller.controllerPausedHandler = nil;
controller.playerIndex = -1;
#endif
}
}
if (device->remote) {
}
}
static void IOS_JoystickDetect ( void  )
static

Definition at line 350 of file SDL_sysjoystick.m.

{
}
static int IOS_JoystickGetCount ( void  )
static

Definition at line 344 of file SDL_sysjoystick.m.

References numjoysticks.

{
return numjoysticks;
}
static SDL_JoystickGUID IOS_JoystickGetDeviceGUID ( int  device_index)
static

Definition at line 368 of file SDL_sysjoystick.m.

References device, GetDeviceForIndex(), recDevice::guid, and SDL_zero.

{
if (device) {
guid = device->guid;
} else {
SDL_zero(guid);
}
return guid;
}
static SDL_JoystickID IOS_JoystickGetDeviceInstanceID ( int  device_index)
static

Definition at line 381 of file SDL_sysjoystick.m.

References device, GetDeviceForIndex(), and recDevice::instance_id.

{
return device ? device->instance_id : -1;
}
static const char* IOS_JoystickGetDeviceName ( int  device_index)
static

Definition at line 355 of file SDL_sysjoystick.m.

References device, GetDeviceForIndex(), and recDevice::name.

{
return device ? device->name : "Unknown";
}
static int IOS_JoystickGetDevicePlayerIndex ( int  device_index)
static

Definition at line 362 of file SDL_sysjoystick.m.

{
return -1;
}
static int IOS_JoystickInit ( void  )
static

Definition at line 288 of file SDL_sysjoystick.m.

References sort_controllers::controllers, IOS_AddJoystickDevice(), NULL, SDL_AddHintCallback, SDL_FALSE, SDL_GetHintBoolean, SDL_HINT_ACCELEROMETER_AS_JOYSTICK, SDL_HINT_APPLE_TV_REMOTE_ALLOW_ROTATION, and SDL_TRUE.

{
@autoreleasepool {
NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
#if !TARGET_OS_TV
/* Default behavior, accelerometer as joystick */
}
#endif /* !TARGET_OS_TV */
#ifdef SDL_JOYSTICK_MFI
/* GameController.framework was added in iOS 7. */
if (![GCController class]) {
return 0;
}
for (GCController *controller in [GCController controllers]) {
}
#if TARGET_OS_TV
SDL_AppleTVRemoteRotationHintChanged, NULL);
#endif /* TARGET_OS_TV */
connectObserver = [center addObserverForName:GCControllerDidConnectNotification
object:nil
queue:nil
usingBlock:^(NSNotification *note) {
GCController *controller = note.object;
IOS_AddJoystickDevice(controller, SDL_FALSE);
}];
disconnectObserver = [center addObserverForName:GCControllerDidDisconnectNotification
object:nil
queue:nil
usingBlock:^(NSNotification *note) {
GCController *controller = note.object;
SDL_JoystickDeviceItem *device = deviceList;
while (device != NULL) {
if (device->controller == controller) {
IOS_RemoveJoystickDevice(device);
break;
}
device = device->next;
}
}];
#endif /* SDL_JOYSTICK_MFI */
}
return 0;
}
static int IOS_JoystickOpen ( SDL_Joystick *  joystick,
int  device_index 
)
static

Definition at line 388 of file SDL_sysjoystick.m.

References recDevice::accelerometer, recDevice::controller, device, GetDeviceForIndex(), recDevice::instance_id, recDevice::joystick, motionManager, recDevice::naxes, recDevice::nbuttons, recDevice::nhats, NULL, recDevice::remote, SDL_AppleTVRemoteOpenedAsJoystick, and SDL_SetError.

{
if (device == NULL) {
return SDL_SetError("Could not open Joystick: no hardware device for the specified index");
}
joystick->hwdata = device;
joystick->instance_id = device->instance_id;
joystick->naxes = device->naxes;
joystick->nhats = device->nhats;
joystick->nbuttons = device->nbuttons;
joystick->nballs = 0;
device->joystick = joystick;
@autoreleasepool {
if (device->accelerometer) {
#if !TARGET_OS_TV
if (motionManager == nil) {
motionManager = [[CMMotionManager alloc] init];
}
/* Shorter times between updates can significantly increase CPU usage. */
motionManager.accelerometerUpdateInterval = 0.1;
[motionManager startAccelerometerUpdates];
#endif /* !TARGET_OS_TV */
} else {
#ifdef SDL_JOYSTICK_MFI
GCController *controller = device->controller;
controller.controllerPausedHandler = ^(GCController *c) {
if (joystick->hwdata) {
++joystick->hwdata->num_pause_presses;
}
};
#endif /* SDL_JOYSTICK_MFI */
}
}
if (device->remote) {
}
return 0;
}
static void IOS_JoystickQuit ( void  )
static

Definition at line 684 of file SDL_sysjoystick.m.

References IOS_RemoveJoystickDevice(), motionManager, NULL, numjoysticks, SDL_DelHintCallback, and SDL_HINT_APPLE_TV_REMOTE_ALLOW_ROTATION.

{
@autoreleasepool {
#ifdef SDL_JOYSTICK_MFI
NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
if (connectObserver) {
[center removeObserver:connectObserver name:GCControllerDidConnectNotification object:nil];
connectObserver = nil;
}
if (disconnectObserver) {
[center removeObserver:disconnectObserver name:GCControllerDidDisconnectNotification object:nil];
disconnectObserver = nil;
}
#if TARGET_OS_TV
SDL_AppleTVRemoteRotationHintChanged, NULL);
#endif /* TARGET_OS_TV */
#endif /* SDL_JOYSTICK_MFI */
while (deviceList != NULL) {
}
#if !TARGET_OS_TV
#endif /* !TARGET_OS_TV */
}
}
static int IOS_JoystickRumble ( SDL_Joystick *  joystick,
Uint16  low_frequency_rumble,
Uint16  high_frequency_rumble,
Uint32  duration_ms 
)
static

Definition at line 633 of file SDL_sysjoystick.m.

References SDL_Unsupported.

{
return SDL_Unsupported();
}
static void IOS_JoystickUpdate ( SDL_Joystick *  joystick)
static

Definition at line 639 of file SDL_sysjoystick.m.

References recDevice::accelerometer, recDevice::controller, device, IOS_AccelerometerUpdate(), IOS_MFIJoystickUpdate(), and NULL.

{
SDL_JoystickDeviceItem *device = joystick->hwdata;
if (device == NULL) {
return;
}
if (device->accelerometer) {
} else if (device->controller) {
}
}
static void IOS_MFIJoystickUpdate ( SDL_Joystick *  joystick)
static

Definition at line 505 of file SDL_sysjoystick.m.

References sort_controllers::controllers, i, SDL_arraysize, SDL_HAT_CENTERED, SDL_PRESSED, SDL_PrivateJoystickAxis(), SDL_PrivateJoystickButton(), SDL_PrivateJoystickHat(), and SDL_RELEASED.

Referenced by IOS_JoystickUpdate().

{
#if SDL_JOYSTICK_MFI
@autoreleasepool {
GCController *controller = joystick->hwdata->controller;
Uint8 hatstate = SDL_HAT_CENTERED;
int i;
int updateplayerindex = 0;
if (controller.extendedGamepad) {
GCExtendedGamepad *gamepad = controller.extendedGamepad;
/* Axis order matches the XInput Windows mappings. */
Sint16 axes[] = {
(Sint16) (gamepad.leftThumbstick.xAxis.value * 32767),
(Sint16) (gamepad.leftThumbstick.yAxis.value * -32767),
(Sint16) ((gamepad.leftTrigger.value * 65535) - 32768),
(Sint16) (gamepad.rightThumbstick.xAxis.value * 32767),
(Sint16) (gamepad.rightThumbstick.yAxis.value * -32767),
(Sint16) ((gamepad.rightTrigger.value * 65535) - 32768),
};
/* Button order matches the XInput Windows mappings. */
Uint8 buttons[] = {
gamepad.buttonA.isPressed, gamepad.buttonB.isPressed,
gamepad.buttonX.isPressed, gamepad.buttonY.isPressed,
gamepad.leftShoulder.isPressed,
gamepad.rightShoulder.isPressed,
};
hatstate = IOS_MFIJoystickHatStateForDPad(gamepad.dpad);
for (i = 0; i < SDL_arraysize(axes); i++) {
/* The triggers (axes 2 and 5) are resting at -32768 but SDL
* initializes its values to 0. We only want to make sure the
* player index is up to date if the user actually moves an axis. */
if ((i != 2 && i != 5) || axes[i] != -32768) {
updateplayerindex |= (joystick->axes[i].value != axes[i]);
}
SDL_PrivateJoystickAxis(joystick, i, axes[i]);
}
for (i = 0; i < SDL_arraysize(buttons); i++) {
updateplayerindex |= (joystick->buttons[i] != buttons[i]);
SDL_PrivateJoystickButton(joystick, i, buttons[i]);
}
} else if (controller.gamepad) {
GCGamepad *gamepad = controller.gamepad;
/* Button order matches the XInput Windows mappings. */
Uint8 buttons[] = {
gamepad.buttonA.isPressed, gamepad.buttonB.isPressed,
gamepad.buttonX.isPressed, gamepad.buttonY.isPressed,
gamepad.leftShoulder.isPressed,
gamepad.rightShoulder.isPressed,
};
hatstate = IOS_MFIJoystickHatStateForDPad(gamepad.dpad);
for (i = 0; i < SDL_arraysize(buttons); i++) {
updateplayerindex |= (joystick->buttons[i] != buttons[i]);
SDL_PrivateJoystickButton(joystick, i, buttons[i]);
}
}
#if TARGET_OS_TV
else if (controller.microGamepad) {
GCMicroGamepad *gamepad = controller.microGamepad;
Sint16 axes[] = {
(Sint16) (gamepad.dpad.xAxis.value * 32767),
(Sint16) (gamepad.dpad.yAxis.value * -32767),
};
for (i = 0; i < SDL_arraysize(axes); i++) {
updateplayerindex |= (joystick->axes[i].value != axes[i]);
SDL_PrivateJoystickAxis(joystick, i, axes[i]);
}
Uint8 buttons[] = {
gamepad.buttonA.isPressed,
gamepad.buttonX.isPressed,
};
for (i = 0; i < SDL_arraysize(buttons); i++) {
updateplayerindex |= (joystick->buttons[i] != buttons[i]);
SDL_PrivateJoystickButton(joystick, i, buttons[i]);
}
}
#endif /* TARGET_OS_TV */
if (joystick->nhats > 0) {
updateplayerindex |= (joystick->hats[0] != hatstate);
SDL_PrivateJoystickHat(joystick, 0, hatstate);
}
for (i = 0; i < joystick->hwdata->num_pause_presses; i++) {
const Uint8 pausebutton = joystick->nbuttons - 1; /* The pause button is always last. */
SDL_PrivateJoystickButton(joystick, pausebutton, SDL_PRESSED);
SDL_PrivateJoystickButton(joystick, pausebutton, SDL_RELEASED);
updateplayerindex = YES;
}
joystick->hwdata->num_pause_presses = 0;
if (updateplayerindex && controller.playerIndex == -1) {
BOOL usedPlayerIndexSlots[4] = {NO, NO, NO, NO};
/* Find the player index of all other connected controllers. */
for (GCController *c in [GCController controllers]) {
if (c != controller && c.playerIndex >= 0) {
usedPlayerIndexSlots[c.playerIndex] = YES;
}
}
/* Set this controller's player index to the first unused index.
* FIXME: This logic isn't great... but SDL doesn't expose this
* concept in its external API, so we don't have much to go on. */
for (i = 0; i < SDL_arraysize(usedPlayerIndexSlots); i++) {
if (!usedPlayerIndexSlots[i]) {
controller.playerIndex = i;
break;
}
}
}
}
#endif /* SDL_JOYSTICK_MFI */
}
static SDL_JoystickDeviceItem* IOS_RemoveJoystickDevice ( SDL_JoystickDeviceItem device)
static

Definition at line 218 of file SDL_sysjoystick.m.

References recDevice::controller, deviceList, recDevice::instance_id, recDevice::joystick, recDevice::name, recDevice::next, NULL, numjoysticks, SDL_free, and SDL_PrivateJoystickRemoved().

Referenced by IOS_JoystickQuit().

{
if (device == NULL) {
return NULL;
}
next = device->next;
while (item != NULL) {
if (item == device) {
break;
}
prev = item;
item = item->next;
}
/* Unlink the device item from the device list. */
if (prev) {
prev->next = device->next;
} else if (device == deviceList) {
deviceList = device->next;
}
if (device->joystick) {
device->joystick->hwdata = NULL;
}
#ifdef SDL_JOYSTICK_MFI
@autoreleasepool {
if (device->controller) {
/* The controller was explicitly retained in the struct, so it
* should be explicitly released before freeing the struct. */
GCController *controller = CFBridgingRelease((__bridge CFTypeRef)(device->controller));
controller.controllerPausedHandler = nil;
device->controller = nil;
}
}
#endif /* SDL_JOYSTICK_MFI */
SDL_free(device->name);
SDL_free(device);
return next;
}

Variable Documentation

const char* accelerometerName = "iOS Accelerometer"
static

Definition at line 54 of file SDL_sysjoystick.m.

Referenced by IOS_AddJoystickDevice().

SDL_JoystickDeviceItem* deviceList = NULL
static
CMMotionManager* motionManager = nil
static

Definition at line 55 of file SDL_sysjoystick.m.

Referenced by IOS_AccelerometerUpdate(), IOS_JoystickOpen(), and IOS_JoystickQuit().

int numjoysticks = 0
static
int SDL_AppleTVRemoteOpenedAsJoystick = 0

Definition at line 61 of file SDL_sysjoystick.m.

Referenced by IOS_JoystickClose(), and IOS_JoystickOpen().