src/joystick/iphoneos/SDL_sysjoystick.m
author Ryan C. Gordon <icculus@icculus.org>
Fri, 26 Aug 2016 15:46:29 -0400
changeset 10228 9a277db2806d
parent 10226 cb13d22b7f09
child 10340 5724f5087acd
permissions -rw-r--r--
ios: patched to compile.
slouken@2765
     1
/*
slouken@5535
     2
  Simple DirectMedia Layer
slouken@9998
     3
  Copyright (C) 1997-2016 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
icculus@10228
    29
#include "SDL_events.h"
slouken@2765
    30
#include "SDL_joystick.h"
slime73@9489
    31
#include "SDL_hints.h"
slouken@8921
    32
#include "SDL_stdinc.h"
slouken@2765
    33
#include "../SDL_sysjoystick.h"
slouken@2765
    34
#include "../SDL_joystick_c.h"
slouken@2765
    35
slouken@8921
    36
#import <CoreMotion/CoreMotion.h>
slouken@8921
    37
slime73@9876
    38
#ifdef SDL_JOYSTICK_MFI
slime73@9876
    39
#import <GameController/GameController.h>
slouken@8921
    40
slime73@9876
    41
static id connectObserver = nil;
slime73@9876
    42
static id disconnectObserver = nil;
slime73@9876
    43
#endif /* SDL_JOYSTICK_MFI */
slouken@8921
    44
slime73@9876
    45
static const char *accelerometerName = "iOS Accelerometer";
slouken@8921
    46
static CMMotionManager *motionManager = nil;
slime73@9876
    47
slime73@9876
    48
static SDL_JoystickDeviceItem *deviceList = NULL;
slime73@9876
    49
slime73@9489
    50
static int numjoysticks = 0;
slime73@9876
    51
static SDL_JoystickID instancecounter = 0;
slime73@9876
    52
slime73@9876
    53
static SDL_JoystickDeviceItem *
slime73@9876
    54
GetDeviceForIndex(int device_index)
slime73@9876
    55
{
slime73@9876
    56
    SDL_JoystickDeviceItem *device = deviceList;
slime73@9876
    57
    int i = 0;
slime73@9876
    58
slime73@9876
    59
    while (i < device_index) {
slime73@9876
    60
        if (device == NULL) {
slime73@9876
    61
            return NULL;
slime73@9876
    62
        }
slime73@9876
    63
        device = device->next;
slime73@9876
    64
        i++;
slime73@9876
    65
    }
slime73@9876
    66
slime73@9876
    67
    return device;
slime73@9876
    68
}
slime73@9876
    69
slime73@9876
    70
static void
slime73@9876
    71
SDL_SYS_AddMFIJoystickDevice(SDL_JoystickDeviceItem *device, GCController *controller)
slime73@9876
    72
{
slime73@9876
    73
#ifdef SDL_JOYSTICK_MFI
slime73@9876
    74
    const char *name = NULL;
slime73@9876
    75
    /* Explicitly retain the controller because SDL_JoystickDeviceItem is a
slime73@9876
    76
     * struct, and ARC doesn't work with structs. */
slime73@9876
    77
    device->controller = (__bridge GCController *) CFBridgingRetain(controller);
slime73@9876
    78
slime73@9876
    79
    if (controller.vendorName) {
slime73@9876
    80
        name = controller.vendorName.UTF8String;
slime73@9876
    81
    }
slime73@9876
    82
slime73@9876
    83
    if (!name) {
slime73@9876
    84
        name = "MFi Gamepad";
slime73@9876
    85
    }
slime73@9876
    86
slime73@9876
    87
    device->name = SDL_strdup(name);
slime73@9876
    88
slime73@9876
    89
    device->guid.data[0] = 'M';
slime73@9876
    90
    device->guid.data[1] = 'F';
slime73@9876
    91
    device->guid.data[2] = 'i';
slime73@9876
    92
    device->guid.data[3] = 'G';
slime73@9876
    93
    device->guid.data[4] = 'a';
slime73@9876
    94
    device->guid.data[5] = 'm';
slime73@9876
    95
    device->guid.data[6] = 'e';
slime73@9876
    96
    device->guid.data[7] = 'p';
slime73@9876
    97
    device->guid.data[8] = 'a';
slime73@9876
    98
    device->guid.data[9] = 'd';
slime73@9876
    99
slime73@9876
   100
    if (controller.extendedGamepad) {
slime73@9876
   101
        device->guid.data[10] = 1;
slime73@9876
   102
    } else if (controller.gamepad) {
slime73@9876
   103
        device->guid.data[10] = 2;
slime73@9876
   104
    }
slime73@9876
   105
slime73@9876
   106
    if (controller.extendedGamepad) {
slime73@9876
   107
        device->naxes = 6; /* 2 thumbsticks and 2 triggers */
slime73@9876
   108
        device->nhats = 1; /* d-pad */
slime73@9876
   109
        device->nbuttons = 7; /* ABXY, shoulder buttons, pause button */
slime73@9876
   110
    } else if (controller.gamepad) {
slime73@9876
   111
        device->naxes = 0; /* no traditional analog inputs */
slime73@9876
   112
        device->nhats = 1; /* d-pad */
slime73@9876
   113
        device->nbuttons = 7; /* ABXY, shoulder buttons, pause button */
slime73@9876
   114
    }
slime73@9876
   115
    /* TODO: Handle micro profiles on tvOS. */
slime73@9960
   116
slime73@9960
   117
    /* This will be set when the first button press of the controller is
slime73@9960
   118
     * detected. */
slime73@9960
   119
    controller.playerIndex = -1;
slime73@9876
   120
#endif
slime73@9876
   121
}
slime73@9876
   122
slime73@9876
   123
static void
slime73@9876
   124
SDL_SYS_AddJoystickDevice(GCController *controller, SDL_bool accelerometer)
slime73@9876
   125
{
slime73@9876
   126
    SDL_JoystickDeviceItem *device = deviceList;
slime73@9876
   127
slime73@9876
   128
    while (device != NULL) {
slime73@9876
   129
        if (device->controller == controller) {
slime73@9876
   130
            return;
slime73@9876
   131
        }
slime73@9876
   132
        device = device->next;
slime73@9876
   133
    }
slime73@9876
   134
slime73@9876
   135
    device = (SDL_JoystickDeviceItem *) SDL_malloc(sizeof(SDL_JoystickDeviceItem));
slime73@9876
   136
    if (device == NULL) {
slime73@9876
   137
        return;
slime73@9876
   138
    }
slime73@9876
   139
slime73@9876
   140
    SDL_zerop(device);
slime73@9876
   141
slime73@9876
   142
    device->accelerometer = accelerometer;
slime73@9876
   143
    device->instance_id = instancecounter++;
slime73@9876
   144
slime73@9876
   145
    if (accelerometer) {
slime73@9876
   146
        device->name = SDL_strdup(accelerometerName);
slime73@9876
   147
        device->naxes = 3; /* Device acceleration in the x, y, and z axes. */
slime73@9876
   148
        device->nhats = 0;
slime73@9876
   149
        device->nbuttons = 0;
slime73@9876
   150
slime73@9876
   151
        /* Use the accelerometer name as a GUID. */
slime73@9876
   152
        SDL_memcpy(&device->guid.data, device->name, SDL_min(sizeof(SDL_JoystickGUID), SDL_strlen(device->name)));
slime73@9876
   153
    } else if (controller) {
slime73@9876
   154
        SDL_SYS_AddMFIJoystickDevice(device, controller);
slime73@9876
   155
    }
slime73@9876
   156
slime73@9876
   157
    if (deviceList == NULL) {
slime73@9876
   158
        deviceList = device;
slime73@9876
   159
    } else {
slime73@9876
   160
        SDL_JoystickDeviceItem *lastdevice = deviceList;
slime73@9876
   161
        while (lastdevice->next != NULL) {
slime73@9876
   162
            lastdevice = lastdevice->next;
slime73@9876
   163
        }
slime73@9876
   164
        lastdevice->next = device;
slime73@9876
   165
    }
slime73@9876
   166
slime73@9876
   167
    ++numjoysticks;
slime73@9876
   168
slouken@10226
   169
    SDL_PrivateJoystickAdded(numjoysticks - 1);
slime73@9876
   170
}
slime73@9876
   171
slime73@9879
   172
static SDL_JoystickDeviceItem *
slime73@9876
   173
SDL_SYS_RemoveJoystickDevice(SDL_JoystickDeviceItem *device)
slime73@9876
   174
{
slime73@9876
   175
    SDL_JoystickDeviceItem *prev = NULL;
slime73@9876
   176
    SDL_JoystickDeviceItem *next = NULL;
slime73@9876
   177
    SDL_JoystickDeviceItem *item = deviceList;
slime73@9876
   178
slime73@9876
   179
    if (device == NULL) {
slime73@9876
   180
        return NULL;
slime73@9876
   181
    }
slime73@9876
   182
slime73@9876
   183
    next = device->next;
slime73@9876
   184
slime73@9876
   185
    while (item != NULL) {
slime73@9876
   186
        if (item == device) {
slime73@9876
   187
            break;
slime73@9876
   188
        }
slime73@9876
   189
        prev = item;
slime73@9876
   190
        item = item->next;
slime73@9876
   191
    }
slime73@9876
   192
slime73@9876
   193
    /* Unlink the device item from the device list. */
slime73@9876
   194
    if (prev) {
slime73@9876
   195
        prev->next = device->next;
slime73@9876
   196
    } else if (device == deviceList) {
slime73@9876
   197
        deviceList = device->next;
slime73@9876
   198
    }
slime73@9876
   199
slime73@9876
   200
    if (device->joystick) {
slime73@9876
   201
        device->joystick->hwdata = NULL;
slime73@9876
   202
    }
slime73@9876
   203
slime73@9876
   204
#ifdef SDL_JOYSTICK_MFI
slime73@9876
   205
    @autoreleasepool {
slime73@9876
   206
        if (device->controller) {
slime73@9876
   207
            /* The controller was explicitly retained in the struct, so it
slime73@9876
   208
             * should be explicitly released before freeing the struct. */
slime73@9876
   209
            GCController *controller = CFBridgingRelease((__bridge CFTypeRef)(device->controller));
slime73@9876
   210
            controller.controllerPausedHandler = nil;
slime73@9876
   211
            device->controller = nil;
slime73@9876
   212
        }
slime73@9876
   213
    }
slime73@9876
   214
#endif /* SDL_JOYSTICK_MFI */
slime73@9876
   215
slime73@9876
   216
    --numjoysticks;
slime73@9876
   217
slouken@10226
   218
	SDL_PrivateJoystickRemoved(device->instance_id);
slime73@9876
   219
philipp@9878
   220
    SDL_free(device->name);
philipp@9878
   221
    SDL_free(device);
philipp@9878
   222
slime73@9876
   223
    return next;
slime73@9876
   224
}
slouken@2765
   225
slouken@2765
   226
/* Function to scan the system for joysticks.
philipp@9311
   227
 * Joystick 0 should be the system default joystick.
slouken@2765
   228
 * It should return 0, or -1 on an unrecoverable fatal error.
slouken@2765
   229
 */
slouken@2765
   230
int
slouken@2765
   231
SDL_SYS_JoystickInit(void)
slouken@2765
   232
{
slime73@9876
   233
    @autoreleasepool {
slime73@9876
   234
        NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
slime73@9876
   235
        const char *hint = SDL_GetHint(SDL_HINT_ACCELEROMETER_AS_JOYSTICK);
slime73@9876
   236
slime73@9876
   237
        if (!hint || SDL_atoi(hint)) {
slime73@9876
   238
            /* Default behavior, accelerometer as joystick */
slime73@9876
   239
            SDL_SYS_AddJoystickDevice(nil, SDL_TRUE);
slime73@9876
   240
        }
slime73@9876
   241
slime73@9876
   242
#ifdef SDL_JOYSTICK_MFI
slime73@9876
   243
        /* GameController.framework was added in iOS 7. */
slime73@9876
   244
        if (![GCController class]) {
slime73@9876
   245
            return numjoysticks;
slime73@9876
   246
        }
slime73@9876
   247
slime73@9876
   248
        for (GCController *controller in [GCController controllers]) {
slime73@9876
   249
            SDL_SYS_AddJoystickDevice(controller, SDL_FALSE);
slime73@9876
   250
        }
slime73@9876
   251
slime73@9876
   252
        connectObserver = [center addObserverForName:GCControllerDidConnectNotification
slime73@9876
   253
                                              object:nil
slime73@9876
   254
                                               queue:nil
slime73@9876
   255
                                          usingBlock:^(NSNotification *note) {
slime73@9876
   256
                                              GCController *controller = note.object;
slime73@9876
   257
                                              SDL_SYS_AddJoystickDevice(controller, SDL_FALSE);
slime73@9876
   258
                                          }];
slime73@9876
   259
slime73@9876
   260
        disconnectObserver = [center addObserverForName:GCControllerDidDisconnectNotification
slime73@9876
   261
                                                 object:nil
slime73@9876
   262
                                                  queue:nil
slime73@9876
   263
                                             usingBlock:^(NSNotification *note) {
slime73@9876
   264
                                                 GCController *controller = note.object;
slime73@9876
   265
                                                 SDL_JoystickDeviceItem *device = deviceList;
slime73@9876
   266
                                                 while (device != NULL) {
slime73@9876
   267
                                                     if (device->controller == controller) {
slime73@9876
   268
                                                         SDL_SYS_RemoveJoystickDevice(device);
slime73@9876
   269
                                                         break;
slime73@9876
   270
                                                     }
slime73@9876
   271
                                                     device = device->next;
slime73@9876
   272
                                                 }
slime73@9876
   273
                                             }];
slime73@9876
   274
#endif /* SDL_JOYSTICK_MFI */
slime73@9489
   275
    }
slime73@9489
   276
slime73@9489
   277
    return numjoysticks;
slouken@2765
   278
}
slouken@2765
   279
slouken@6707
   280
int SDL_SYS_NumJoysticks()
slouken@6707
   281
{
slime73@9489
   282
    return numjoysticks;
slouken@6707
   283
}
slouken@6707
   284
slouken@6707
   285
void SDL_SYS_JoystickDetect()
slouken@6707
   286
{
slouken@6707
   287
}
slouken@6707
   288
slouken@2765
   289
/* Function to get the device-dependent name of a joystick */
slouken@2765
   290
const char *
slouken@6707
   291
SDL_SYS_JoystickNameForDeviceIndex(int device_index)
slouken@2765
   292
{
slime73@9876
   293
    SDL_JoystickDeviceItem *device = GetDeviceForIndex(device_index);
slime73@9876
   294
    return device ? device->name : "Unknown";
slouken@6707
   295
}
slouken@6707
   296
slouken@6707
   297
/* Function to perform the mapping from device index to the instance id for this index */
slouken@6707
   298
SDL_JoystickID SDL_SYS_GetInstanceIdOfDeviceIndex(int device_index)
slouken@6707
   299
{
slime73@9876
   300
    SDL_JoystickDeviceItem *device = GetDeviceForIndex(device_index);
slime73@9876
   301
    return device ? device->instance_id : 0;
slouken@2765
   302
}
slouken@2765
   303
slouken@2765
   304
/* Function to open a joystick for use.
philipp@9380
   305
   The joystick to open is specified by the device index.
slouken@2765
   306
   This should fill the nbuttons and naxes fields of the joystick structure.
slouken@2765
   307
   It returns 0, or -1 if there is an error.
slouken@2765
   308
 */
slouken@2765
   309
int
slouken@6693
   310
SDL_SYS_JoystickOpen(SDL_Joystick * joystick, int device_index)
slouken@2765
   311
{
slime73@9876
   312
    SDL_JoystickDeviceItem *device = GetDeviceForIndex(device_index);
slime73@9876
   313
    if (device == NULL) {
slime73@9876
   314
        return SDL_SetError("Could not open Joystick: no hardware device for the specified index");
slime73@9876
   315
    }
slime73@9876
   316
slime73@9876
   317
    joystick->hwdata = device;
slime73@9876
   318
    joystick->instance_id = device->instance_id;
slime73@9876
   319
slime73@9876
   320
    joystick->naxes = device->naxes;
slime73@9876
   321
    joystick->nhats = device->nhats;
slime73@9876
   322
    joystick->nbuttons = device->nbuttons;
slouken@6707
   323
    joystick->nballs = 0;
slime73@9876
   324
slime73@9876
   325
    device->joystick = joystick;
slouken@8921
   326
slime73@9506
   327
    @autoreleasepool {
slime73@9876
   328
        if (device->accelerometer) {
slime73@9876
   329
            if (motionManager == nil) {
slime73@9876
   330
                motionManager = [[CMMotionManager alloc] init];
slime73@9876
   331
            }
slime73@9876
   332
slime73@9876
   333
            /* Shorter times between updates can significantly increase CPU usage. */
slime73@9876
   334
            motionManager.accelerometerUpdateInterval = 0.1;
slime73@9876
   335
            [motionManager startAccelerometerUpdates];
slime73@9876
   336
        } else {
slime73@9876
   337
#ifdef SDL_JOYSTICK_MFI
slime73@9876
   338
            GCController *controller = device->controller;
slime73@9960
   339
            controller.controllerPausedHandler = ^(GCController *c) {
slime73@9876
   340
                if (joystick->hwdata) {
slime73@9876
   341
                    ++joystick->hwdata->num_pause_presses;
slime73@9876
   342
                }
slime73@9876
   343
            };
slime73@9876
   344
#endif /* SDL_JOYSTICK_MFI */
slime73@9506
   345
        }
slouken@8921
   346
    }
slouken@8921
   347
slouken@6707
   348
    return 0;
slouken@6707
   349
}
slouken@6707
   350
philipp@9561
   351
/* Function to determine if this joystick is attached to the system right now */
slime73@9879
   352
SDL_bool
slime73@9879
   353
SDL_SYS_JoystickAttached(SDL_Joystick *joystick)
slouken@6707
   354
{
slime73@9876
   355
    return joystick->hwdata != NULL;
slouken@2765
   356
}
slouken@2765
   357
slime73@9879
   358
static void
slime73@9879
   359
SDL_SYS_AccelerometerUpdate(SDL_Joystick * joystick)
slouken@8921
   360
{
slouken@8921
   361
    const float maxgforce = SDL_IPHONE_MAX_GFORCE;
slouken@8921
   362
    const SInt16 maxsint16 = 0x7FFF;
slouken@8921
   363
    CMAcceleration accel;
slouken@8921
   364
slime73@9506
   365
    @autoreleasepool {
slime73@9876
   366
        if (!motionManager.isAccelerometerActive) {
slime73@9506
   367
            return;
slime73@9506
   368
        }
slime73@9506
   369
slime73@9506
   370
        accel = motionManager.accelerometerData.acceleration;
slouken@8921
   371
    }
slouken@8921
   372
slouken@8921
   373
    /*
slouken@8921
   374
     Convert accelerometer data from floating point to Sint16, which is what
slouken@8921
   375
     the joystick system expects.
slouken@8921
   376
slouken@8921
   377
     To do the conversion, the data is first clamped onto the interval
slouken@8921
   378
     [-SDL_IPHONE_MAX_G_FORCE, SDL_IPHONE_MAX_G_FORCE], then the data is multiplied
slouken@8921
   379
     by MAX_SINT16 so that it is mapped to the full range of an Sint16.
slouken@8921
   380
slouken@8921
   381
     You can customize the clamped range of this function by modifying the
slouken@8921
   382
     SDL_IPHONE_MAX_GFORCE macro in SDL_config_iphoneos.h.
slouken@8921
   383
slouken@8921
   384
     Once converted to Sint16, the accelerometer data no longer has coherent
slouken@8921
   385
     units. You can convert the data back to units of g-force by multiplying
slouken@8921
   386
     it in your application's code by SDL_IPHONE_MAX_GFORCE / 0x7FFF.
slouken@8921
   387
     */
slouken@8921
   388
slouken@8921
   389
    /* clamp the data */
slouken@8921
   390
    accel.x = SDL_min(SDL_max(accel.x, -maxgforce), maxgforce);
slouken@8921
   391
    accel.y = SDL_min(SDL_max(accel.y, -maxgforce), maxgforce);
slouken@8921
   392
    accel.z = SDL_min(SDL_max(accel.z, -maxgforce), maxgforce);
slouken@8921
   393
slouken@8921
   394
    /* pass in data mapped to range of SInt16 */
slime73@9876
   395
    SDL_PrivateJoystickAxis(joystick, 0,  (accel.x / maxgforce) * maxsint16);
slouken@8921
   396
    SDL_PrivateJoystickAxis(joystick, 1, -(accel.y / maxgforce) * maxsint16);
slime73@9876
   397
    SDL_PrivateJoystickAxis(joystick, 2,  (accel.z / maxgforce) * maxsint16);
slime73@9876
   398
}
slime73@9876
   399
slime73@9876
   400
#ifdef SDL_JOYSTICK_MFI
slime73@9876
   401
static Uint8
slime73@9876
   402
SDL_SYS_MFIJoystickHatStateForDPad(GCControllerDirectionPad *dpad)
slime73@9876
   403
{
slime73@9876
   404
    Uint8 hat = 0;
slime73@9876
   405
slime73@9876
   406
    if (dpad.up.isPressed) {
slime73@9876
   407
        hat |= SDL_HAT_UP;
slime73@9876
   408
    } else if (dpad.down.isPressed) {
slime73@9876
   409
        hat |= SDL_HAT_DOWN;
slime73@9876
   410
    }
slime73@9876
   411
slime73@9876
   412
    if (dpad.left.isPressed) {
slime73@9876
   413
        hat |= SDL_HAT_LEFT;
slime73@9876
   414
    } else if (dpad.right.isPressed) {
slime73@9876
   415
        hat |= SDL_HAT_RIGHT;
slime73@9876
   416
    }
slime73@9876
   417
slime73@9876
   418
    if (hat == 0) {
slime73@9876
   419
        return SDL_HAT_CENTERED;
slime73@9876
   420
    }
slime73@9876
   421
slime73@9876
   422
    return hat;
slime73@9876
   423
}
slime73@9876
   424
#endif
slime73@9876
   425
slime73@9876
   426
static void
slime73@9876
   427
SDL_SYS_MFIJoystickUpdate(SDL_Joystick * joystick)
slime73@9876
   428
{
slime73@9876
   429
#ifdef SDL_JOYSTICK_MFI
slime73@9876
   430
    @autoreleasepool {
slime73@9876
   431
        GCController *controller = joystick->hwdata->controller;
slime73@9876
   432
        Uint8 hatstate = SDL_HAT_CENTERED;
slime73@9876
   433
        int i;
slime73@9960
   434
        int updateplayerindex = 0;
slime73@9876
   435
slime73@9876
   436
        if (controller.extendedGamepad) {
slime73@9876
   437
            GCExtendedGamepad *gamepad = controller.extendedGamepad;
slime73@9876
   438
slime73@9876
   439
            /* Axis order matches the XInput Windows mappings. */
slime73@9960
   440
            Sint16 axes[] = {
slime73@9960
   441
                (Sint16) (gamepad.leftThumbstick.xAxis.value * 32767),
slime73@9960
   442
                (Sint16) (gamepad.leftThumbstick.yAxis.value * -32767),
slime73@9960
   443
                (Sint16) ((gamepad.leftTrigger.value * 65535) - 32768),
slime73@9960
   444
                (Sint16) (gamepad.rightThumbstick.xAxis.value * 32767),
slime73@9960
   445
                (Sint16) (gamepad.rightThumbstick.yAxis.value * -32767),
slime73@9960
   446
                (Sint16) ((gamepad.rightTrigger.value * 65535) - 32768),
slime73@9960
   447
            };
slime73@9960
   448
slime73@9960
   449
            /* Button order matches the XInput Windows mappings. */
slime73@9960
   450
            Uint8 buttons[] = {
slime73@9960
   451
                gamepad.buttonA.isPressed, gamepad.buttonB.isPressed,
slime73@9960
   452
                gamepad.buttonX.isPressed, gamepad.buttonY.isPressed,
slime73@9960
   453
                gamepad.leftShoulder.isPressed,
slime73@9960
   454
                gamepad.rightShoulder.isPressed,
slime73@9960
   455
            };
slime73@9876
   456
slime73@9876
   457
            hatstate = SDL_SYS_MFIJoystickHatStateForDPad(gamepad.dpad);
slime73@9876
   458
slime73@9960
   459
            for (i = 0; i < SDL_arraysize(axes); i++) {
slime73@9960
   460
                /* The triggers (axes 2 and 5) are resting at -32768 but SDL
slime73@9960
   461
                 * initializes its values to 0. We only want to make sure the
slime73@9960
   462
                 * player index is up to date if the user actually moves an axis. */
slime73@9960
   463
                if ((i != 2 && i != 5) || axes[i] != -32768) {
slime73@9960
   464
                    updateplayerindex |= (joystick->axes[i] != axes[i]);
slime73@9960
   465
                }
slime73@9960
   466
                SDL_PrivateJoystickAxis(joystick, i, axes[i]);
slime73@9960
   467
            }
slime73@9960
   468
slime73@9960
   469
            for (i = 0; i < SDL_arraysize(buttons); i++) {
slime73@9960
   470
                updateplayerindex |= (joystick->buttons[i] != buttons[i]);
slime73@9960
   471
                SDL_PrivateJoystickButton(joystick, i, buttons[i]);
slime73@9960
   472
            }
slime73@9876
   473
        } else if (controller.gamepad) {
slime73@9876
   474
            GCGamepad *gamepad = controller.gamepad;
slime73@9876
   475
slime73@9960
   476
            /* Button order matches the XInput Windows mappings. */
slime73@9960
   477
            Uint8 buttons[] = {
slime73@9960
   478
                gamepad.buttonA.isPressed, gamepad.buttonB.isPressed,
slime73@9960
   479
                gamepad.buttonX.isPressed, gamepad.buttonY.isPressed,
slime73@9960
   480
                gamepad.leftShoulder.isPressed,
slime73@9960
   481
                gamepad.rightShoulder.isPressed,
slime73@9960
   482
            };
slime73@9960
   483
slime73@9876
   484
            hatstate = SDL_SYS_MFIJoystickHatStateForDPad(gamepad.dpad);
slime73@9876
   485
slime73@9960
   486
            for (i = 0; i < SDL_arraysize(buttons); i++) {
slime73@9960
   487
                updateplayerindex |= (joystick->buttons[i] != buttons[i]);
slime73@9960
   488
                SDL_PrivateJoystickButton(joystick, i, buttons[i]);
slime73@9960
   489
            }
slime73@9876
   490
        }
slime73@9876
   491
        /* TODO: Handle micro profiles on tvOS. */
slime73@9876
   492
slime73@9960
   493
        if (joystick->nhats > 0) {
slime73@9960
   494
            updateplayerindex |= (joystick->hats[0] != hatstate);
slime73@9960
   495
            SDL_PrivateJoystickHat(joystick, 0, hatstate);
slime73@9960
   496
        }
slime73@9876
   497
slime73@9876
   498
        for (i = 0; i < joystick->hwdata->num_pause_presses; i++) {
slime73@9876
   499
            /* The pause button is always last. */
slime73@9876
   500
            Uint8 pausebutton = joystick->nbuttons - 1;
slime73@9876
   501
slime73@9960
   502
            SDL_PrivateJoystickButton(joystick, pausebutton, SDL_PRESSED);
slime73@9960
   503
            SDL_PrivateJoystickButton(joystick, pausebutton, SDL_RELEASED);
slime73@9960
   504
slime73@9960
   505
            updateplayerindex = YES;
slime73@9876
   506
        }
slime73@9876
   507
slime73@9876
   508
        joystick->hwdata->num_pause_presses = 0;
slime73@9960
   509
slime73@9960
   510
        if (updateplayerindex && controller.playerIndex == -1) {
slime73@9960
   511
            BOOL usedPlayerIndexSlots[4] = {NO, NO, NO, NO};
slime73@9960
   512
slime73@9960
   513
            /* Find the player index of all other connected controllers. */
slime73@9960
   514
            for (GCController *c in [GCController controllers]) {
slime73@9960
   515
                if (c != controller && c.playerIndex >= 0) {
slime73@9960
   516
                    usedPlayerIndexSlots[c.playerIndex] = YES;
slime73@9960
   517
                }
slime73@9960
   518
            }
slime73@9960
   519
slime73@9960
   520
            /* Set this controller's player index to the first unused index.
slime73@9960
   521
             * FIXME: This logic isn't great... but SDL doesn't expose this
slime73@9960
   522
             * concept in its external API, so we don't have much to go on. */
slime73@9960
   523
            for (i = 0; i < SDL_arraysize(usedPlayerIndexSlots); i++) {
slime73@9960
   524
                if (!usedPlayerIndexSlots[i]) {
slime73@9960
   525
                    controller.playerIndex = i;
slime73@9960
   526
                    break;
slime73@9960
   527
                }
slime73@9960
   528
            }
slime73@9960
   529
        }
slime73@9876
   530
    }
slime73@9876
   531
#endif
slouken@8921
   532
}
slouken@8921
   533
slouken@2765
   534
/* Function to update the state of a joystick - called as a device poll.
slouken@2765
   535
 * This function shouldn't update the joystick structure directly,
slouken@2765
   536
 * but instead should call SDL_PrivateJoystick*() to deliver events
slouken@2765
   537
 * and update joystick device state.
slouken@2765
   538
 */
slouken@2765
   539
void
slouken@2765
   540
SDL_SYS_JoystickUpdate(SDL_Joystick * joystick)
slouken@2765
   541
{
slime73@9876
   542
    SDL_JoystickDeviceItem *device = joystick->hwdata;
slime73@9876
   543
slime73@9876
   544
    if (device == NULL) {
slime73@9876
   545
        return;
slime73@9876
   546
    }
slime73@9876
   547
slime73@9876
   548
    if (device->accelerometer) {
slime73@9876
   549
        SDL_SYS_AccelerometerUpdate(joystick);
slime73@9876
   550
    } else if (device->controller) {
slime73@9876
   551
        SDL_SYS_MFIJoystickUpdate(joystick);
slime73@9876
   552
    }
slouken@2765
   553
}
slouken@2765
   554
slouken@2765
   555
/* Function to close a joystick after use */
slouken@2765
   556
void
slouken@2765
   557
SDL_SYS_JoystickClose(SDL_Joystick * joystick)
slouken@2765
   558
{
slime73@9876
   559
    SDL_JoystickDeviceItem *device = joystick->hwdata;
slime73@9876
   560
slime73@9876
   561
    if (device == NULL) {
slime73@9876
   562
        return;
slime73@9876
   563
    }
slime73@9876
   564
slime73@9876
   565
    device->joystick = NULL;
slime73@9876
   566
slime73@9506
   567
    @autoreleasepool {
slime73@9876
   568
        if (device->accelerometer) {
slime73@9876
   569
            [motionManager stopAccelerometerUpdates];
slime73@9876
   570
        } else if (device->controller) {
slime73@9876
   571
#ifdef SDL_JOYSTICK_MFI
slime73@9876
   572
            GCController *controller = device->controller;
slime73@9876
   573
            controller.controllerPausedHandler = nil;
slime73@9960
   574
            controller.playerIndex = -1;
slime73@9876
   575
#endif
slime73@9876
   576
        }
slime73@9506
   577
    }
slouken@2765
   578
}
slouken@2765
   579
slouken@2765
   580
/* Function to perform any system-specific joystick related cleanup */
slouken@2765
   581
void
slouken@2765
   582
SDL_SYS_JoystickQuit(void)
slouken@2765
   583
{
slime73@9506
   584
    @autoreleasepool {
slime73@9876
   585
#ifdef SDL_JOYSTICK_MFI
slime73@9876
   586
        NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
slime73@9876
   587
slime73@9876
   588
        if (connectObserver) {
slime73@9876
   589
            [center removeObserver:connectObserver name:GCControllerDidConnectNotification object:nil];
slime73@9876
   590
            connectObserver = nil;
slime73@9876
   591
        }
slime73@9876
   592
slime73@9876
   593
        if (disconnectObserver) {
slime73@9876
   594
            [center removeObserver:disconnectObserver name:GCControllerDidDisconnectNotification object:nil];
slime73@9876
   595
            disconnectObserver = nil;
slime73@9876
   596
        }
slime73@9876
   597
#endif /* SDL_JOYSTICK_MFI */
slime73@9876
   598
slime73@9876
   599
        while (deviceList != NULL) {
slime73@9876
   600
            SDL_SYS_RemoveJoystickDevice(deviceList);
slime73@9876
   601
        }
slime73@9876
   602
slouken@8921
   603
        motionManager = nil;
slouken@8921
   604
    }
slime73@9489
   605
slime73@9489
   606
    numjoysticks = 0;
slouken@2765
   607
}
slouken@6693
   608
slime73@9879
   609
SDL_JoystickGUID
slime73@9879
   610
SDL_SYS_JoystickGetDeviceGUID( int device_index )
slouken@6693
   611
{
slime73@9876
   612
    SDL_JoystickDeviceItem *device = GetDeviceForIndex(device_index);
slouken@6744
   613
    SDL_JoystickGUID guid;
slime73@9876
   614
    if (device) {
slime73@9876
   615
        guid = device->guid;
slime73@9876
   616
    } else {
slime73@9876
   617
        SDL_zero(guid);
slime73@9876
   618
    }
slouken@6693
   619
    return guid;
slouken@6693
   620
}
slouken@6693
   621
slime73@9879
   622
SDL_JoystickGUID
slime73@9879
   623
SDL_SYS_JoystickGetGUID(SDL_Joystick * joystick)
slouken@6693
   624
{
slouken@6744
   625
    SDL_JoystickGUID guid;
slime73@9876
   626
    if (joystick->hwdata) {
slime73@9876
   627
        guid = joystick->hwdata->guid;
slime73@9876
   628
    } else {
slime73@9876
   629
        SDL_zero(guid);
slime73@9876
   630
    }
slouken@6693
   631
    return guid;
slouken@6693
   632
}
slouken@6693
   633
slouken@2765
   634
/* vi: set ts=4 sw=4 expandtab: */