src/joystick/iphoneos/SDL_sysjoystick.m
author Alex Szpakowski <slime73@gmail.com>
Sat, 17 Sep 2016 01:31:07 -0300
changeset 10351 12f90eb6b52b
parent 10341 75ac5b0ed013
child 10499 363c1c7e7a41
permissions -rw-r--r--
Added a new hint SDL_HINT_APPLE_TV_REMOTE_ALLOW_ROTATION.

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