src/joystick/iphoneos/SDL_sysjoystick.m
author Sam Lantinga <slouken@libsdl.org>
Fri, 22 Sep 2017 08:30:52 -0700
changeset 11532 4af4e3986438
parent 11284 3db78361e751
child 11811 5d94cb6b24d3
permissions -rw-r--r--
Added stubs for simple Steam Controller support
slouken@2765
     1
/*
slouken@5535
     2
  Simple DirectMedia Layer
slouken@10737
     3
  Copyright (C) 1997-2017 Sam Lantinga <slouken@libsdl.org>
slouken@2765
     4
slouken@5535
     5
  This software is provided 'as-is', without any express or implied
slouken@5535
     6
  warranty.  In no event will the authors be held liable for any damages
slouken@5535
     7
  arising from the use of this software.
slouken@2765
     8
slouken@5535
     9
  Permission is granted to anyone to use this software for any purpose,
slouken@5535
    10
  including commercial applications, and to alter it and redistribute it
slouken@5535
    11
  freely, subject to the following restrictions:
slouken@2765
    12
slouken@5535
    13
  1. The origin of this software must not be misrepresented; you must not
slouken@5535
    14
     claim that you wrote the original software. If you use this software
slouken@5535
    15
     in a product, an acknowledgment in the product documentation would be
slouken@5535
    16
     appreciated but is not required.
slouken@5535
    17
  2. Altered source versions must be plainly marked as such, and must not be
slouken@5535
    18
     misrepresented as being the original software.
slouken@5535
    19
  3. This notice may not be removed or altered from any source distribution.
slouken@2765
    20
*/
icculus@8093
    21
#include "../../SDL_internal.h"
slouken@2765
    22
philipp@8138
    23
/* This is the iOS implementation of the SDL joystick API */
slime73@9876
    24
#include "SDL_sysjoystick_c.h"
slime73@9876
    25
slime73@9876
    26
/* needed for SDL_IPHONE_MAX_GFORCE macro */
slime73@9876
    27
#include "SDL_config_iphoneos.h"
slouken@2765
    28
slouken@11532
    29
#include "SDL_assert.h"
icculus@10228
    30
#include "SDL_events.h"
slouken@2765
    31
#include "SDL_joystick.h"
slime73@9489
    32
#include "SDL_hints.h"
slouken@8921
    33
#include "SDL_stdinc.h"
slouken@2765
    34
#include "../SDL_sysjoystick.h"
slouken@2765
    35
#include "../SDL_joystick_c.h"
slouken@11532
    36
#include "../steam/SDL_steamcontroller.h"
slouken@11532
    37
slouken@2765
    38
slime73@10340
    39
#if !SDL_EVENTS_DISABLED
slime73@10340
    40
#include "../../events/SDL_events_c.h"
slime73@10340
    41
#endif
slime73@10340
    42
slime73@10340
    43
#if !TARGET_OS_TV
slouken@8921
    44
#import <CoreMotion/CoreMotion.h>
slime73@10340
    45
#endif
slouken@8921
    46
slime73@9876
    47
#ifdef SDL_JOYSTICK_MFI
slime73@9876
    48
#import <GameController/GameController.h>
slouken@8921
    49
slime73@9876
    50
static id connectObserver = nil;
slime73@9876
    51
static id disconnectObserver = nil;
slime73@9876
    52
#endif /* SDL_JOYSTICK_MFI */
slouken@8921
    53
slime73@10340
    54
#if !TARGET_OS_TV
slime73@9876
    55
static const char *accelerometerName = "iOS Accelerometer";
slouken@8921
    56
static CMMotionManager *motionManager = nil;
slime73@10340
    57
#endif /* !TARGET_OS_TV */
slime73@9876
    58
slime73@9876
    59
static SDL_JoystickDeviceItem *deviceList = NULL;
slime73@9876
    60
slime73@9489
    61
static int numjoysticks = 0;
slime73@9876
    62
static SDL_JoystickID instancecounter = 0;
slime73@9876
    63
slime73@9876
    64
static SDL_JoystickDeviceItem *
slime73@9876
    65
GetDeviceForIndex(int device_index)
slime73@9876
    66
{
slime73@9876
    67
    SDL_JoystickDeviceItem *device = deviceList;
slime73@9876
    68
    int i = 0;
slime73@9876
    69
slime73@9876
    70
    while (i < device_index) {
slime73@9876
    71
        if (device == NULL) {
slime73@9876
    72
            return NULL;
slime73@9876
    73
        }
slime73@9876
    74
        device = device->next;
slime73@9876
    75
        i++;
slime73@9876
    76
    }
slime73@9876
    77
slime73@9876
    78
    return device;
slime73@9876
    79
}
slime73@9876
    80
slime73@9876
    81
static void
slime73@9876
    82
SDL_SYS_AddMFIJoystickDevice(SDL_JoystickDeviceItem *device, GCController *controller)
slime73@9876
    83
{
slime73@9876
    84
#ifdef SDL_JOYSTICK_MFI
slime73@9876
    85
    const char *name = NULL;
slime73@9876
    86
    /* Explicitly retain the controller because SDL_JoystickDeviceItem is a
slime73@9876
    87
     * struct, and ARC doesn't work with structs. */
slime73@9876
    88
    device->controller = (__bridge GCController *) CFBridgingRetain(controller);
slime73@9876
    89
slime73@9876
    90
    if (controller.vendorName) {
slime73@9876
    91
        name = controller.vendorName.UTF8String;
slime73@9876
    92
    }
slime73@9876
    93
slime73@9876
    94
    if (!name) {
slime73@9876
    95
        name = "MFi Gamepad";
slime73@9876
    96
    }
slime73@9876
    97
slime73@9876
    98
    device->name = SDL_strdup(name);
slime73@9876
    99
slime73@9876
   100
    device->guid.data[0] = 'M';
slime73@9876
   101
    device->guid.data[1] = 'F';
slime73@9876
   102
    device->guid.data[2] = 'i';
slime73@9876
   103
    device->guid.data[3] = 'G';
slime73@9876
   104
    device->guid.data[4] = 'a';
slime73@9876
   105
    device->guid.data[5] = 'm';
slime73@9876
   106
    device->guid.data[6] = 'e';
slime73@9876
   107
    device->guid.data[7] = 'p';
slime73@9876
   108
    device->guid.data[8] = 'a';
slime73@9876
   109
    device->guid.data[9] = 'd';
slime73@9876
   110
slime73@9876
   111
    if (controller.extendedGamepad) {
slime73@9876
   112
        device->guid.data[10] = 1;
slime73@9876
   113
    } else if (controller.gamepad) {
slime73@9876
   114
        device->guid.data[10] = 2;
slime73@9876
   115
    }
slime73@10340
   116
#if TARGET_OS_TV
slime73@10340
   117
    else if (controller.microGamepad) {
slime73@10340
   118
        device->guid.data[10] = 3;
slime73@10340
   119
    }
slime73@10340
   120
#endif /* TARGET_OS_TV */
slime73@9876
   121
slime73@9876
   122
    if (controller.extendedGamepad) {
slime73@9876
   123
        device->naxes = 6; /* 2 thumbsticks and 2 triggers */
slime73@9876
   124
        device->nhats = 1; /* d-pad */
slime73@9876
   125
        device->nbuttons = 7; /* ABXY, shoulder buttons, pause button */
slime73@9876
   126
    } else if (controller.gamepad) {
slime73@9876
   127
        device->naxes = 0; /* no traditional analog inputs */
slime73@9876
   128
        device->nhats = 1; /* d-pad */
slime73@9876
   129
        device->nbuttons = 7; /* ABXY, shoulder buttons, pause button */
slime73@9876
   130
    }
slime73@10340
   131
#if TARGET_OS_TV
slime73@10340
   132
    else if (controller.microGamepad) {
slime73@10340
   133
        device->naxes = 2; /* treat the touch surface as two axes */
slime73@10340
   134
        device->nhats = 0; /* apparently the touch surface-as-dpad is buggy */
slime73@10340
   135
        device->nbuttons = 3; /* AX, pause button */
slime73@10351
   136
slouken@10499
   137
        controller.microGamepad.allowsRotation = SDL_GetHintBoolean(SDL_HINT_APPLE_TV_REMOTE_ALLOW_ROTATION, SDL_FALSE);
slime73@10340
   138
    }
slime73@10340
   139
#endif /* TARGET_OS_TV */
slime73@9960
   140
slime73@9960
   141
    /* This will be set when the first button press of the controller is
slime73@9960
   142
     * detected. */
slime73@9960
   143
    controller.playerIndex = -1;
slime73@10340
   144
slime73@10340
   145
#endif /* SDL_JOYSTICK_MFI */
slime73@9876
   146
}
slime73@9876
   147
slime73@9876
   148
static void
slime73@9876
   149
SDL_SYS_AddJoystickDevice(GCController *controller, SDL_bool accelerometer)
slime73@9876
   150
{
slime73@9876
   151
    SDL_JoystickDeviceItem *device = deviceList;
slime73@9876
   152
slime73@9876
   153
    while (device != NULL) {
slime73@9876
   154
        if (device->controller == controller) {
slime73@9876
   155
            return;
slime73@9876
   156
        }
slime73@9876
   157
        device = device->next;
slime73@9876
   158
    }
slime73@9876
   159
slime73@9876
   160
    device = (SDL_JoystickDeviceItem *) SDL_malloc(sizeof(SDL_JoystickDeviceItem));
slime73@9876
   161
    if (device == NULL) {
slime73@9876
   162
        return;
slime73@9876
   163
    }
slime73@9876
   164
slime73@9876
   165
    SDL_zerop(device);
slime73@9876
   166
slime73@9876
   167
    device->accelerometer = accelerometer;
slime73@9876
   168
    device->instance_id = instancecounter++;
slime73@9876
   169
slime73@9876
   170
    if (accelerometer) {
slime73@10340
   171
#if TARGET_OS_TV
slime73@10340
   172
        SDL_free(device);
slime73@10340
   173
        return;
slime73@10340
   174
#else
slime73@9876
   175
        device->name = SDL_strdup(accelerometerName);
slime73@9876
   176
        device->naxes = 3; /* Device acceleration in the x, y, and z axes. */
slime73@9876
   177
        device->nhats = 0;
slime73@9876
   178
        device->nbuttons = 0;
slime73@9876
   179
slime73@9876
   180
        /* Use the accelerometer name as a GUID. */
slime73@9876
   181
        SDL_memcpy(&device->guid.data, device->name, SDL_min(sizeof(SDL_JoystickGUID), SDL_strlen(device->name)));
slime73@10340
   182
#endif /* TARGET_OS_TV */
slime73@9876
   183
    } else if (controller) {
slime73@9876
   184
        SDL_SYS_AddMFIJoystickDevice(device, controller);
slime73@9876
   185
    }
slime73@9876
   186
slime73@9876
   187
    if (deviceList == NULL) {
slime73@9876
   188
        deviceList = device;
slime73@9876
   189
    } else {
slime73@9876
   190
        SDL_JoystickDeviceItem *lastdevice = deviceList;
slime73@9876
   191
        while (lastdevice->next != NULL) {
slime73@9876
   192
            lastdevice = lastdevice->next;
slime73@9876
   193
        }
slime73@9876
   194
        lastdevice->next = device;
slime73@9876
   195
    }
slime73@9876
   196
slime73@9876
   197
    ++numjoysticks;
slime73@9876
   198
slouken@10226
   199
    SDL_PrivateJoystickAdded(numjoysticks - 1);
slime73@9876
   200
}
slime73@9876
   201
slime73@9879
   202
static SDL_JoystickDeviceItem *
slime73@9876
   203
SDL_SYS_RemoveJoystickDevice(SDL_JoystickDeviceItem *device)
slime73@9876
   204
{
slime73@9876
   205
    SDL_JoystickDeviceItem *prev = NULL;
slime73@9876
   206
    SDL_JoystickDeviceItem *next = NULL;
slime73@9876
   207
    SDL_JoystickDeviceItem *item = deviceList;
slime73@9876
   208
slime73@9876
   209
    if (device == NULL) {
slime73@9876
   210
        return NULL;
slime73@9876
   211
    }
slime73@9876
   212
slime73@9876
   213
    next = device->next;
slime73@9876
   214
slime73@9876
   215
    while (item != NULL) {
slime73@9876
   216
        if (item == device) {
slime73@9876
   217
            break;
slime73@9876
   218
        }
slime73@9876
   219
        prev = item;
slime73@9876
   220
        item = item->next;
slime73@9876
   221
    }
slime73@9876
   222
slime73@9876
   223
    /* Unlink the device item from the device list. */
slime73@9876
   224
    if (prev) {
slime73@9876
   225
        prev->next = device->next;
slime73@9876
   226
    } else if (device == deviceList) {
slime73@9876
   227
        deviceList = device->next;
slime73@9876
   228
    }
slime73@9876
   229
slime73@9876
   230
    if (device->joystick) {
slime73@9876
   231
        device->joystick->hwdata = NULL;
slime73@9876
   232
    }
slime73@9876
   233
slime73@9876
   234
#ifdef SDL_JOYSTICK_MFI
slime73@9876
   235
    @autoreleasepool {
slime73@9876
   236
        if (device->controller) {
slime73@9876
   237
            /* The controller was explicitly retained in the struct, so it
slime73@9876
   238
             * should be explicitly released before freeing the struct. */
slime73@9876
   239
            GCController *controller = CFBridgingRelease((__bridge CFTypeRef)(device->controller));
slime73@9876
   240
            controller.controllerPausedHandler = nil;
slime73@9876
   241
            device->controller = nil;
slime73@9876
   242
        }
slime73@9876
   243
    }
slime73@9876
   244
#endif /* SDL_JOYSTICK_MFI */
slime73@9876
   245
slime73@9876
   246
    --numjoysticks;
slime73@9876
   247
slouken@11532
   248
    SDL_PrivateJoystickRemoved(device->instance_id);
slime73@9876
   249
philipp@9878
   250
    SDL_free(device->name);
philipp@9878
   251
    SDL_free(device);
philipp@9878
   252
slime73@9876
   253
    return next;
slime73@9876
   254
}
slouken@2765
   255
slime73@10351
   256
#if TARGET_OS_TV
slouken@11284
   257
static void SDLCALL
slime73@10351
   258
SDL_AppleTVRemoteRotationHintChanged(void *udata, const char *name, const char *oldValue, const char *newValue)
slime73@10351
   259
{
slime73@10351
   260
    BOOL allowRotation = newValue != NULL && *newValue != '0';
slime73@10351
   261
slime73@10351
   262
    @autoreleasepool {
slime73@10351
   263
        for (GCController *controller in [GCController controllers]) {
slime73@10351
   264
            if (controller.microGamepad) {
slime73@10351
   265
                controller.microGamepad.allowsRotation = allowRotation;
slime73@10351
   266
            }
slime73@10351
   267
        }
slime73@10351
   268
    }
slime73@10351
   269
}
slime73@10351
   270
#endif /* TARGET_OS_TV */
slime73@10351
   271
slouken@11532
   272
static SDL_bool SteamControllerConnectedCallback(const char *name, SDL_JoystickGUID guid, int *device_instance)
slouken@11532
   273
{
slouken@11532
   274
    SDL_JoystickDeviceItem *device = (SDL_JoystickDeviceItem *)SDL_calloc(1, sizeof(SDL_JoystickDeviceItem));
slouken@11532
   275
    if (device == NULL) {
slouken@11532
   276
        return SDL_FALSE;
slouken@11532
   277
    }
slouken@11532
   278
slouken@11532
   279
    *device_instance = device->instance_id = instancecounter++;
slouken@11532
   280
	device->name = SDL_strdup(name);
slouken@11532
   281
	device->guid = guid;
slouken@11532
   282
	SDL_GetSteamControllerInputs(&device->nbuttons,
slouken@11532
   283
								 &device->naxes,
slouken@11532
   284
								 &device->nhats);
slouken@11532
   285
    device->m_bSteamController = SDL_TRUE;
slouken@11532
   286
slouken@11532
   287
    if (deviceList == NULL) {
slouken@11532
   288
        deviceList = device;
slouken@11532
   289
    } else {
slouken@11532
   290
        SDL_JoystickDeviceItem *lastdevice = deviceList;
slouken@11532
   291
        while (lastdevice->next != NULL) {
slouken@11532
   292
            lastdevice = lastdevice->next;
slouken@11532
   293
        }
slouken@11532
   294
        lastdevice->next = device;
slouken@11532
   295
    }
slouken@11532
   296
slouken@11532
   297
    ++numjoysticks;
slouken@11532
   298
slouken@11532
   299
    SDL_PrivateJoystickAdded(numjoysticks - 1);
slouken@11532
   300
slouken@11532
   301
    return SDL_TRUE;
slouken@11532
   302
}
slouken@11532
   303
slouken@11532
   304
static void SteamControllerDisconnectedCallback(int device_instance)
slouken@11532
   305
{
slouken@11532
   306
    SDL_JoystickDeviceItem *item;
slouken@11532
   307
slouken@11532
   308
	for (item = deviceList; item; item = item->next) {
slouken@11532
   309
        if (item->instance_id == device_instance) {
slouken@11532
   310
			SDL_SYS_RemoveJoystickDevice(item);
slouken@11532
   311
			break;
slouken@11532
   312
        }
slouken@11532
   313
    }
slouken@11532
   314
}
slouken@11532
   315
slouken@2765
   316
/* Function to scan the system for joysticks.
philipp@9311
   317
 * Joystick 0 should be the system default joystick.
slouken@2765
   318
 * It should return 0, or -1 on an unrecoverable fatal error.
slouken@2765
   319
 */
slouken@2765
   320
int
slouken@2765
   321
SDL_SYS_JoystickInit(void)
slouken@2765
   322
{
slime73@9876
   323
    @autoreleasepool {
slime73@9876
   324
        NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
slime73@10340
   325
slouken@11532
   326
        SDL_InitSteamControllers(SteamControllerConnectedCallback,
slouken@11532
   327
                                 SteamControllerDisconnectedCallback);
slouken@11532
   328
slime73@10340
   329
#if !TARGET_OS_TV
slouken@10499
   330
        if (SDL_GetHintBoolean(SDL_HINT_ACCELEROMETER_AS_JOYSTICK, SDL_TRUE)) {
slime73@9876
   331
            /* Default behavior, accelerometer as joystick */
slime73@9876
   332
            SDL_SYS_AddJoystickDevice(nil, SDL_TRUE);
slime73@9876
   333
        }
slime73@10340
   334
#endif /* !TARGET_OS_TV */
slime73@9876
   335
slime73@9876
   336
#ifdef SDL_JOYSTICK_MFI
slime73@9876
   337
        /* GameController.framework was added in iOS 7. */
slime73@9876
   338
        if (![GCController class]) {
slime73@9876
   339
            return numjoysticks;
slime73@9876
   340
        }
slime73@9876
   341
slime73@9876
   342
        for (GCController *controller in [GCController controllers]) {
slime73@9876
   343
            SDL_SYS_AddJoystickDevice(controller, SDL_FALSE);
slime73@9876
   344
        }
slime73@9876
   345
slime73@10351
   346
#if TARGET_OS_TV
slime73@10351
   347
        SDL_AddHintCallback(SDL_HINT_APPLE_TV_REMOTE_ALLOW_ROTATION,
slime73@10351
   348
                            SDL_AppleTVRemoteRotationHintChanged, NULL);
slime73@10351
   349
#endif /* TARGET_OS_TV */
slime73@10351
   350
slime73@9876
   351
        connectObserver = [center addObserverForName:GCControllerDidConnectNotification
slime73@9876
   352
                                              object:nil
slime73@9876
   353
                                               queue:nil
slime73@9876
   354
                                          usingBlock:^(NSNotification *note) {
slime73@9876
   355
                                              GCController *controller = note.object;
slime73@9876
   356
                                              SDL_SYS_AddJoystickDevice(controller, SDL_FALSE);
slime73@9876
   357
                                          }];
slime73@9876
   358
slime73@9876
   359
        disconnectObserver = [center addObserverForName:GCControllerDidDisconnectNotification
slime73@9876
   360
                                                 object:nil
slime73@9876
   361
                                                  queue:nil
slime73@9876
   362
                                             usingBlock:^(NSNotification *note) {
slime73@9876
   363
                                                 GCController *controller = note.object;
slime73@9876
   364
                                                 SDL_JoystickDeviceItem *device = deviceList;
slime73@9876
   365
                                                 while (device != NULL) {
slime73@9876
   366
                                                     if (device->controller == controller) {
slime73@9876
   367
                                                         SDL_SYS_RemoveJoystickDevice(device);
slime73@9876
   368
                                                         break;
slime73@9876
   369
                                                     }
slime73@9876
   370
                                                     device = device->next;
slime73@9876
   371
                                                 }
slime73@9876
   372
                                             }];
slime73@9876
   373
#endif /* SDL_JOYSTICK_MFI */
slime73@9489
   374
    }
slime73@9489
   375
slime73@9489
   376
    return numjoysticks;
slouken@2765
   377
}
slouken@2765
   378
philipp@10617
   379
int
philipp@10617
   380
SDL_SYS_NumJoysticks(void)
slouken@6707
   381
{
slime73@9489
   382
    return numjoysticks;
slouken@6707
   383
}
slouken@6707
   384
philipp@10617
   385
void
philipp@10617
   386
SDL_SYS_JoystickDetect(void)
slouken@6707
   387
{
slouken@11532
   388
    SDL_UpdateSteamControllers();
slouken@6707
   389
}
slouken@6707
   390
slouken@2765
   391
/* Function to get the device-dependent name of a joystick */
slouken@2765
   392
const char *
slouken@6707
   393
SDL_SYS_JoystickNameForDeviceIndex(int device_index)
slouken@2765
   394
{
slime73@9876
   395
    SDL_JoystickDeviceItem *device = GetDeviceForIndex(device_index);
slime73@9876
   396
    return device ? device->name : "Unknown";
slouken@6707
   397
}
slouken@6707
   398
slouken@6707
   399
/* Function to perform the mapping from device index to the instance id for this index */
slouken@6707
   400
SDL_JoystickID SDL_SYS_GetInstanceIdOfDeviceIndex(int device_index)
slouken@6707
   401
{
slime73@9876
   402
    SDL_JoystickDeviceItem *device = GetDeviceForIndex(device_index);
slime73@9876
   403
    return device ? device->instance_id : 0;
slouken@2765
   404
}
slouken@2765
   405
slouken@2765
   406
/* Function to open a joystick for use.
philipp@9380
   407
   The joystick to open is specified by the device index.
slouken@2765
   408
   This should fill the nbuttons and naxes fields of the joystick structure.
slouken@2765
   409
   It returns 0, or -1 if there is an error.
slouken@2765
   410
 */
slouken@2765
   411
int
slouken@6693
   412
SDL_SYS_JoystickOpen(SDL_Joystick * joystick, int device_index)
slouken@2765
   413
{
slime73@9876
   414
    SDL_JoystickDeviceItem *device = GetDeviceForIndex(device_index);
slime73@9876
   415
    if (device == NULL) {
slime73@9876
   416
        return SDL_SetError("Could not open Joystick: no hardware device for the specified index");
slime73@9876
   417
    }
slime73@9876
   418
slime73@9876
   419
    joystick->hwdata = device;
slime73@9876
   420
    joystick->instance_id = device->instance_id;
slime73@9876
   421
slime73@9876
   422
    joystick->naxes = device->naxes;
slime73@9876
   423
    joystick->nhats = device->nhats;
slime73@9876
   424
    joystick->nbuttons = device->nbuttons;
slouken@6707
   425
    joystick->nballs = 0;
slime73@9876
   426
slime73@9876
   427
    device->joystick = joystick;
slouken@8921
   428
slime73@9506
   429
    @autoreleasepool {
slime73@9876
   430
        if (device->accelerometer) {
slime73@10340
   431
#if !TARGET_OS_TV
slime73@9876
   432
            if (motionManager == nil) {
slime73@9876
   433
                motionManager = [[CMMotionManager alloc] init];
slime73@9876
   434
            }
slime73@9876
   435
slime73@9876
   436
            /* Shorter times between updates can significantly increase CPU usage. */
slime73@9876
   437
            motionManager.accelerometerUpdateInterval = 0.1;
slime73@9876
   438
            [motionManager startAccelerometerUpdates];
slime73@10340
   439
#endif /* !TARGET_OS_TV */
slime73@9876
   440
        } else {
slime73@9876
   441
#ifdef SDL_JOYSTICK_MFI
slime73@9876
   442
            GCController *controller = device->controller;
slime73@9960
   443
            controller.controllerPausedHandler = ^(GCController *c) {
slime73@9876
   444
                if (joystick->hwdata) {
slime73@9876
   445
                    ++joystick->hwdata->num_pause_presses;
slime73@9876
   446
                }
slime73@9876
   447
            };
slime73@9876
   448
#endif /* SDL_JOYSTICK_MFI */
slime73@9506
   449
        }
slouken@8921
   450
    }
slouken@8921
   451
slouken@6707
   452
    return 0;
slouken@6707
   453
}
slouken@6707
   454
philipp@9561
   455
/* Function to determine if this joystick is attached to the system right now */
slime73@9879
   456
SDL_bool
slime73@9879
   457
SDL_SYS_JoystickAttached(SDL_Joystick *joystick)
slouken@6707
   458
{
slime73@9876
   459
    return joystick->hwdata != NULL;
slouken@2765
   460
}
slouken@2765
   461
slime73@9879
   462
static void
slime73@9879
   463
SDL_SYS_AccelerometerUpdate(SDL_Joystick * joystick)
slouken@8921
   464
{
slime73@10340
   465
#if !TARGET_OS_TV
slouken@8921
   466
    const float maxgforce = SDL_IPHONE_MAX_GFORCE;
slouken@8921
   467
    const SInt16 maxsint16 = 0x7FFF;
slouken@8921
   468
    CMAcceleration accel;
slouken@8921
   469
slime73@9506
   470
    @autoreleasepool {
slime73@9876
   471
        if (!motionManager.isAccelerometerActive) {
slime73@9506
   472
            return;
slime73@9506
   473
        }
slime73@9506
   474
slime73@9506
   475
        accel = motionManager.accelerometerData.acceleration;
slouken@8921
   476
    }
slouken@8921
   477
slouken@8921
   478
    /*
slouken@8921
   479
     Convert accelerometer data from floating point to Sint16, which is what
slouken@8921
   480
     the joystick system expects.
slouken@8921
   481
slouken@8921
   482
     To do the conversion, the data is first clamped onto the interval
slouken@8921
   483
     [-SDL_IPHONE_MAX_G_FORCE, SDL_IPHONE_MAX_G_FORCE], then the data is multiplied
slouken@8921
   484
     by MAX_SINT16 so that it is mapped to the full range of an Sint16.
slouken@8921
   485
slouken@8921
   486
     You can customize the clamped range of this function by modifying the
slouken@8921
   487
     SDL_IPHONE_MAX_GFORCE macro in SDL_config_iphoneos.h.
slouken@8921
   488
slouken@8921
   489
     Once converted to Sint16, the accelerometer data no longer has coherent
slouken@8921
   490
     units. You can convert the data back to units of g-force by multiplying
slouken@8921
   491
     it in your application's code by SDL_IPHONE_MAX_GFORCE / 0x7FFF.
slouken@8921
   492
     */
slouken@8921
   493
slouken@8921
   494
    /* clamp the data */
slouken@8921
   495
    accel.x = SDL_min(SDL_max(accel.x, -maxgforce), maxgforce);
slouken@8921
   496
    accel.y = SDL_min(SDL_max(accel.y, -maxgforce), maxgforce);
slouken@8921
   497
    accel.z = SDL_min(SDL_max(accel.z, -maxgforce), maxgforce);
slouken@8921
   498
slouken@8921
   499
    /* pass in data mapped to range of SInt16 */
slime73@9876
   500
    SDL_PrivateJoystickAxis(joystick, 0,  (accel.x / maxgforce) * maxsint16);
slouken@8921
   501
    SDL_PrivateJoystickAxis(joystick, 1, -(accel.y / maxgforce) * maxsint16);
slime73@9876
   502
    SDL_PrivateJoystickAxis(joystick, 2,  (accel.z / maxgforce) * maxsint16);
slime73@10340
   503
#endif /* !TARGET_OS_TV */
slime73@9876
   504
}
slime73@9876
   505
slime73@9876
   506
#ifdef SDL_JOYSTICK_MFI
slime73@9876
   507
static Uint8
slime73@9876
   508
SDL_SYS_MFIJoystickHatStateForDPad(GCControllerDirectionPad *dpad)
slime73@9876
   509
{
slime73@9876
   510
    Uint8 hat = 0;
slime73@9876
   511
slime73@9876
   512
    if (dpad.up.isPressed) {
slime73@9876
   513
        hat |= SDL_HAT_UP;
slime73@9876
   514
    } else if (dpad.down.isPressed) {
slime73@9876
   515
        hat |= SDL_HAT_DOWN;
slime73@9876
   516
    }
slime73@9876
   517
slime73@9876
   518
    if (dpad.left.isPressed) {
slime73@9876
   519
        hat |= SDL_HAT_LEFT;
slime73@9876
   520
    } else if (dpad.right.isPressed) {
slime73@9876
   521
        hat |= SDL_HAT_RIGHT;
slime73@9876
   522
    }
slime73@9876
   523
slime73@9876
   524
    if (hat == 0) {
slime73@9876
   525
        return SDL_HAT_CENTERED;
slime73@9876
   526
    }
slime73@9876
   527
slime73@9876
   528
    return hat;
slime73@9876
   529
}
slime73@9876
   530
#endif
slime73@9876
   531
slime73@9876
   532
static void
slime73@9876
   533
SDL_SYS_MFIJoystickUpdate(SDL_Joystick * joystick)
slime73@9876
   534
{
slime73@10340
   535
#if SDL_JOYSTICK_MFI
slime73@9876
   536
    @autoreleasepool {
slime73@9876
   537
        GCController *controller = joystick->hwdata->controller;
slime73@9876
   538
        Uint8 hatstate = SDL_HAT_CENTERED;
slime73@9876
   539
        int i;
slime73@9960
   540
        int updateplayerindex = 0;
slime73@9876
   541
slime73@9876
   542
        if (controller.extendedGamepad) {
slime73@9876
   543
            GCExtendedGamepad *gamepad = controller.extendedGamepad;
slime73@9876
   544
slime73@9876
   545
            /* Axis order matches the XInput Windows mappings. */
slime73@9960
   546
            Sint16 axes[] = {
slime73@9960
   547
                (Sint16) (gamepad.leftThumbstick.xAxis.value * 32767),
slime73@9960
   548
                (Sint16) (gamepad.leftThumbstick.yAxis.value * -32767),
slime73@9960
   549
                (Sint16) ((gamepad.leftTrigger.value * 65535) - 32768),
slime73@9960
   550
                (Sint16) (gamepad.rightThumbstick.xAxis.value * 32767),
slime73@9960
   551
                (Sint16) (gamepad.rightThumbstick.yAxis.value * -32767),
slime73@9960
   552
                (Sint16) ((gamepad.rightTrigger.value * 65535) - 32768),
slime73@9960
   553
            };
slime73@9960
   554
slime73@9960
   555
            /* Button order matches the XInput Windows mappings. */
slime73@9960
   556
            Uint8 buttons[] = {
slime73@9960
   557
                gamepad.buttonA.isPressed, gamepad.buttonB.isPressed,
slime73@9960
   558
                gamepad.buttonX.isPressed, gamepad.buttonY.isPressed,
slime73@9960
   559
                gamepad.leftShoulder.isPressed,
slime73@9960
   560
                gamepad.rightShoulder.isPressed,
slime73@9960
   561
            };
slime73@9876
   562
slime73@9876
   563
            hatstate = SDL_SYS_MFIJoystickHatStateForDPad(gamepad.dpad);
slime73@9876
   564
slime73@9960
   565
            for (i = 0; i < SDL_arraysize(axes); i++) {
slime73@9960
   566
                /* The triggers (axes 2 and 5) are resting at -32768 but SDL
slime73@9960
   567
                 * initializes its values to 0. We only want to make sure the
slime73@9960
   568
                 * player index is up to date if the user actually moves an axis. */
slime73@9960
   569
                if ((i != 2 && i != 5) || axes[i] != -32768) {
slouken@10714
   570
                    updateplayerindex |= (joystick->axes[i].value != axes[i]);
slime73@9960
   571
                }
slime73@9960
   572
                SDL_PrivateJoystickAxis(joystick, i, axes[i]);
slime73@9960
   573
            }
slime73@9960
   574
slime73@9960
   575
            for (i = 0; i < SDL_arraysize(buttons); i++) {
slime73@9960
   576
                updateplayerindex |= (joystick->buttons[i] != buttons[i]);
slime73@9960
   577
                SDL_PrivateJoystickButton(joystick, i, buttons[i]);
slime73@9960
   578
            }
slime73@9876
   579
        } else if (controller.gamepad) {
slime73@9876
   580
            GCGamepad *gamepad = controller.gamepad;
slime73@9876
   581
slime73@9960
   582
            /* Button order matches the XInput Windows mappings. */
slime73@9960
   583
            Uint8 buttons[] = {
slime73@9960
   584
                gamepad.buttonA.isPressed, gamepad.buttonB.isPressed,
slime73@9960
   585
                gamepad.buttonX.isPressed, gamepad.buttonY.isPressed,
slime73@9960
   586
                gamepad.leftShoulder.isPressed,
slime73@9960
   587
                gamepad.rightShoulder.isPressed,
slime73@9960
   588
            };
slime73@9960
   589
slime73@9876
   590
            hatstate = SDL_SYS_MFIJoystickHatStateForDPad(gamepad.dpad);
slime73@9876
   591
slime73@9960
   592
            for (i = 0; i < SDL_arraysize(buttons); i++) {
slime73@9960
   593
                updateplayerindex |= (joystick->buttons[i] != buttons[i]);
slime73@9960
   594
                SDL_PrivateJoystickButton(joystick, i, buttons[i]);
slime73@9960
   595
            }
slime73@9876
   596
        }
slime73@10340
   597
#if TARGET_OS_TV
slime73@10340
   598
        else if (controller.microGamepad) {
slime73@10340
   599
            GCMicroGamepad *gamepad = controller.microGamepad;
slime73@10340
   600
slime73@10340
   601
            Sint16 axes[] = {
slime73@10340
   602
                (Sint16) (gamepad.dpad.xAxis.value * 32767),
slime73@10340
   603
                (Sint16) (gamepad.dpad.yAxis.value * -32767),
slime73@10340
   604
            };
slime73@10340
   605
slime73@10340
   606
            for (i = 0; i < SDL_arraysize(axes); i++) {
slouken@10873
   607
                updateplayerindex |= (joystick->axes[i].value != axes[i]);
slime73@10340
   608
                SDL_PrivateJoystickAxis(joystick, i, axes[i]);
slime73@10340
   609
            }
slime73@10340
   610
slime73@10340
   611
            /* Apparently the dpad values are not accurate enough to be useful. */
slime73@10340
   612
            /* hatstate = SDL_SYS_MFIJoystickHatStateForDPad(gamepad.dpad); */
slime73@10340
   613
slime73@10340
   614
            Uint8 buttons[] = {
slime73@10340
   615
                gamepad.buttonA.isPressed,
slime73@10340
   616
                gamepad.buttonX.isPressed,
slime73@10340
   617
            };
slime73@10340
   618
slime73@10340
   619
            for (i = 0; i < SDL_arraysize(buttons); i++) {
slime73@10340
   620
                updateplayerindex |= (joystick->buttons[i] != buttons[i]);
slime73@10340
   621
                SDL_PrivateJoystickButton(joystick, i, buttons[i]);
slime73@10340
   622
            }
slime73@10340
   623
slime73@10340
   624
            /* TODO: Figure out what to do with reportsAbsoluteDpadValues */
slime73@10340
   625
        }
slime73@10340
   626
#endif /* TARGET_OS_TV */
slime73@9876
   627
slime73@9960
   628
        if (joystick->nhats > 0) {
slime73@9960
   629
            updateplayerindex |= (joystick->hats[0] != hatstate);
slime73@9960
   630
            SDL_PrivateJoystickHat(joystick, 0, hatstate);
slime73@9960
   631
        }
slime73@9876
   632
slime73@9876
   633
        for (i = 0; i < joystick->hwdata->num_pause_presses; i++) {
slime73@9876
   634
            /* The pause button is always last. */
slime73@9876
   635
            Uint8 pausebutton = joystick->nbuttons - 1;
slime73@9876
   636
slime73@9960
   637
            SDL_PrivateJoystickButton(joystick, pausebutton, SDL_PRESSED);
slime73@9960
   638
            SDL_PrivateJoystickButton(joystick, pausebutton, SDL_RELEASED);
slime73@9960
   639
slime73@9960
   640
            updateplayerindex = YES;
slime73@9876
   641
        }
slime73@9876
   642
slime73@9876
   643
        joystick->hwdata->num_pause_presses = 0;
slime73@9960
   644
slime73@9960
   645
        if (updateplayerindex && controller.playerIndex == -1) {
slime73@9960
   646
            BOOL usedPlayerIndexSlots[4] = {NO, NO, NO, NO};
slime73@9960
   647
slime73@9960
   648
            /* Find the player index of all other connected controllers. */
slime73@9960
   649
            for (GCController *c in [GCController controllers]) {
slime73@9960
   650
                if (c != controller && c.playerIndex >= 0) {
slime73@9960
   651
                    usedPlayerIndexSlots[c.playerIndex] = YES;
slime73@9960
   652
                }
slime73@9960
   653
            }
slime73@9960
   654
slime73@9960
   655
            /* Set this controller's player index to the first unused index.
slime73@9960
   656
             * FIXME: This logic isn't great... but SDL doesn't expose this
slime73@9960
   657
             * concept in its external API, so we don't have much to go on. */
slime73@9960
   658
            for (i = 0; i < SDL_arraysize(usedPlayerIndexSlots); i++) {
slime73@9960
   659
                if (!usedPlayerIndexSlots[i]) {
slime73@9960
   660
                    controller.playerIndex = i;
slime73@9960
   661
                    break;
slime73@9960
   662
                }
slime73@9960
   663
            }
slime73@9960
   664
        }
slime73@9876
   665
    }
slime73@10340
   666
#endif /* SDL_JOYSTICK_MFI */
slouken@8921
   667
}
slouken@8921
   668
slouken@2765
   669
/* Function to update the state of a joystick - called as a device poll.
slouken@2765
   670
 * This function shouldn't update the joystick structure directly,
slouken@2765
   671
 * but instead should call SDL_PrivateJoystick*() to deliver events
slouken@2765
   672
 * and update joystick device state.
slouken@2765
   673
 */
slouken@2765
   674
void
slouken@2765
   675
SDL_SYS_JoystickUpdate(SDL_Joystick * joystick)
slouken@2765
   676
{
slime73@9876
   677
    SDL_JoystickDeviceItem *device = joystick->hwdata;
slime73@9876
   678
slime73@9876
   679
    if (device == NULL) {
slime73@9876
   680
        return;
slime73@9876
   681
    }
slouken@11532
   682
    
slouken@11532
   683
    if (device->m_bSteamController) {
slouken@11532
   684
        SDL_UpdateSteamController(joystick);
slouken@11532
   685
        return;
slouken@11532
   686
    }
slime73@9876
   687
slime73@9876
   688
    if (device->accelerometer) {
slime73@9876
   689
        SDL_SYS_AccelerometerUpdate(joystick);
slime73@9876
   690
    } else if (device->controller) {
slime73@9876
   691
        SDL_SYS_MFIJoystickUpdate(joystick);
slime73@9876
   692
    }
slouken@2765
   693
}
slouken@2765
   694
slouken@2765
   695
/* Function to close a joystick after use */
slouken@2765
   696
void
slouken@2765
   697
SDL_SYS_JoystickClose(SDL_Joystick * joystick)
slouken@2765
   698
{
slime73@9876
   699
    SDL_JoystickDeviceItem *device = joystick->hwdata;
slime73@9876
   700
slime73@9876
   701
    if (device == NULL) {
slime73@9876
   702
        return;
slime73@9876
   703
    }
slime73@9876
   704
slime73@9876
   705
    device->joystick = NULL;
slime73@9876
   706
slime73@9506
   707
    @autoreleasepool {
slime73@9876
   708
        if (device->accelerometer) {
slime73@10340
   709
#if !TARGET_OS_TV
slime73@9876
   710
            [motionManager stopAccelerometerUpdates];
slime73@10340
   711
#endif /* !TARGET_OS_TV */
slime73@9876
   712
        } else if (device->controller) {
slime73@9876
   713
#ifdef SDL_JOYSTICK_MFI
slime73@9876
   714
            GCController *controller = device->controller;
slime73@9876
   715
            controller.controllerPausedHandler = nil;
slime73@9960
   716
            controller.playerIndex = -1;
slime73@9876
   717
#endif
slime73@9876
   718
        }
slime73@9506
   719
    }
slouken@2765
   720
}
slouken@2765
   721
slouken@2765
   722
/* Function to perform any system-specific joystick related cleanup */
slouken@2765
   723
void
slouken@2765
   724
SDL_SYS_JoystickQuit(void)
slouken@2765
   725
{
slime73@9506
   726
    @autoreleasepool {
slime73@9876
   727
#ifdef SDL_JOYSTICK_MFI
slime73@9876
   728
        NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
slime73@9876
   729
slime73@9876
   730
        if (connectObserver) {
slime73@9876
   731
            [center removeObserver:connectObserver name:GCControllerDidConnectNotification object:nil];
slime73@9876
   732
            connectObserver = nil;
slime73@9876
   733
        }
slime73@9876
   734
slime73@9876
   735
        if (disconnectObserver) {
slime73@9876
   736
            [center removeObserver:disconnectObserver name:GCControllerDidDisconnectNotification object:nil];
slime73@9876
   737
            disconnectObserver = nil;
slime73@9876
   738
        }
slime73@10351
   739
slime73@10351
   740
#if TARGET_OS_TV
slime73@10351
   741
        SDL_DelHintCallback(SDL_HINT_APPLE_TV_REMOTE_ALLOW_ROTATION,
slime73@10351
   742
                            SDL_AppleTVRemoteRotationHintChanged, NULL);
slime73@10351
   743
#endif /* TARGET_OS_TV */
slime73@9876
   744
#endif /* SDL_JOYSTICK_MFI */
slime73@9876
   745
slime73@9876
   746
        while (deviceList != NULL) {
slime73@9876
   747
            SDL_SYS_RemoveJoystickDevice(deviceList);
slime73@9876
   748
        }
slime73@9876
   749
slime73@10340
   750
#if !TARGET_OS_TV
slouken@8921
   751
        motionManager = nil;
slime73@10340
   752
#endif /* !TARGET_OS_TV */
slouken@8921
   753
    }
slime73@9489
   754
slouken@11532
   755
    SDL_QuitSteamControllers();
slouken@11532
   756
slime73@9489
   757
    numjoysticks = 0;
slouken@2765
   758
}
slouken@6693
   759
slime73@9879
   760
SDL_JoystickGUID
slime73@9879
   761
SDL_SYS_JoystickGetDeviceGUID( int device_index )
slouken@6693
   762
{
slime73@9876
   763
    SDL_JoystickDeviceItem *device = GetDeviceForIndex(device_index);
slouken@6744
   764
    SDL_JoystickGUID guid;
slime73@9876
   765
    if (device) {
slime73@9876
   766
        guid = device->guid;
slime73@9876
   767
    } else {
slime73@9876
   768
        SDL_zero(guid);
slime73@9876
   769
    }
slouken@6693
   770
    return guid;
slouken@6693
   771
}
slouken@6693
   772
slime73@9879
   773
SDL_JoystickGUID
slime73@9879
   774
SDL_SYS_JoystickGetGUID(SDL_Joystick * joystick)
slouken@6693
   775
{
slouken@6744
   776
    SDL_JoystickGUID guid;
slime73@9876
   777
    if (joystick->hwdata) {
slime73@9876
   778
        guid = joystick->hwdata->guid;
slime73@9876
   779
    } else {
slime73@9876
   780
        SDL_zero(guid);
slime73@9876
   781
    }
slouken@6693
   782
    return guid;
slouken@6693
   783
}
slouken@6693
   784
slouken@2765
   785
/* vi: set ts=4 sw=4 expandtab: */