21#include "../../SDL_internal.h"
34#include "../SDL_sysjoystick.h"
35#include "../SDL_joystick_c.h"
38#if !SDL_EVENTS_DISABLED
39#include "../../events/SDL_events_c.h"
43#import <CoreMotion/CoreMotion.h>
46#ifdef SDL_JOYSTICK_MFI
47#import <GameController/GameController.h>
49static id connectObserver = nil;
50static id disconnectObserver = nil;
52#include <Availability.h>
53#include <objc/message.h>
58@interface GCExtendedGamepad (SDL)
59#if (__IPHONE_OS_VERSION_MAX_ALLOWED < 121000) || (__APPLETV_OS_VERSION_MAX_ALLOWED < 121000) || (__MAC_OS_VERSION_MAX_ALLOWED < 1401000)
60@property (nonatomic, readonly, nullable) GCControllerButtonInput *leftThumbstickButton;
61@property (nonatomic, readonly, nullable) GCControllerButtonInput *rightThumbstickButton;
63#if (__IPHONE_OS_VERSION_MAX_ALLOWED < 130000) || (__APPLETV_OS_VERSION_MAX_ALLOWED < 130000) || (__MAC_OS_VERSION_MAX_ALLOWED < 1500000)
64@property (nonatomic, readonly) GCControllerButtonInput *buttonMenu;
65@property (nonatomic, readonly, nullable) GCControllerButtonInput *buttonOptions;
68@interface GCMicroGamepad (SDL)
69#if (__IPHONE_OS_VERSION_MAX_ALLOWED < 130000) || (__APPLETV_OS_VERSION_MAX_ALLOWED < 130000) || (__MAC_OS_VERSION_MAX_ALLOWED < 1500000)
70@property (nonatomic, readonly) GCControllerButtonInput *buttonMenu;
92 while (
i < device_index) {
106#ifdef SDL_JOYSTICK_MFI
107 const Uint16 VENDOR_APPLE = 0x05AC;
108 const Uint16 VENDOR_MICROSOFT = 0x045e;
109 const Uint16 VENDOR_SONY = 0x054C;
118 device->controller = (__bridge GCController *) CFBridgingRetain(controller);
120 if (controller.vendorName) {
121 name = controller.vendorName.UTF8String;
125 name =
"MFi Gamepad";
130 if (controller.extendedGamepad) {
131 GCExtendedGamepad *gamepad = controller.extendedGamepad;
144#pragma clang diagnostic push
145#pragma clang diagnostic ignored "-Wunguarded-availability-new"
146 if ([gamepad respondsToSelector:
@selector(leftThumbstickButton)] && gamepad.leftThumbstickButton) {
150 if ([gamepad respondsToSelector:
@selector(rightThumbstickButton)] && gamepad.rightThumbstickButton) {
154 if ([gamepad respondsToSelector:
@selector(buttonOptions)] && gamepad.buttonOptions) {
158 if ([gamepad respondsToSelector:
@selector(buttonMenu)] && gamepad.buttonMenu) {
166#pragma clang diagnostic pop
168 if ([controller.vendorName containsString:
@"Xbox"]) {
169 vendor = VENDOR_MICROSOFT;
171 }
else if ([controller.vendorName containsString:
@"DUALSHOCK"]) {
172 vendor = VENDOR_SONY;
175 vendor = VENDOR_APPLE;
182 device->nbuttons = nbuttons;
184 }
else if (controller.gamepad) {
198 vendor = VENDOR_APPLE;
203 device->nbuttons = nbuttons;
206 else if (controller.microGamepad) {
207 GCMicroGamepad *gamepad = controller.microGamepad;
214 if ([gamepad respondsToSelector:
@selector(buttonMenu)] && gamepad.buttonMenu) {
223 vendor = VENDOR_APPLE;
228 device->nbuttons = nbuttons;
247 device->guid.data[14] =
'm';
248 device->guid.data[15] = subtype;
253 controller.playerIndex = -1;
266 if (controller && !controller.extendedGamepad && !controller.gamepad && controller.microGamepad) {
273 if (
device->controller == controller) {
284 device->accelerometer = accelerometer;
300 }
else if (controller) {
309 lastdevice = lastdevice->
next;
332 while (item !=
NULL) {
351#ifdef SDL_JOYSTICK_MFI
356 GCController *controller = CFBridgingRelease((__bridge CFTypeRef)(
device->controller));
357 controller.controllerPausedHandler = nil;
375SDL_AppleTVRemoteRotationHintChanged(
void *udata,
const char *
name,
const char *oldValue,
const char *newValue)
377 BOOL allowRotation = newValue !=
NULL && *newValue !=
'0';
380 for (GCController *controller
in [GCController
controllers]) {
381 if (controller.microGamepad) {
382 controller.microGamepad.allowsRotation = allowRotation;
393 NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
402#ifdef SDL_JOYSTICK_MFI
404 if (![GCController
class]) {
408 for (GCController *controller
in [GCController
controllers]) {
414 SDL_AppleTVRemoteRotationHintChanged,
NULL);
417 connectObserver = [center addObserverForName:GCControllerDidConnectNotification
420 usingBlock:^(NSNotification *note) {
421 GCController *controller = note.object;
422 IOS_AddJoystickDevice(controller, SDL_FALSE);
425 disconnectObserver = [center addObserverForName:GCControllerDidDisconnectNotification
428 usingBlock:^(NSNotification *note) {
429 GCController *controller = note.object;
430 SDL_JoystickDeviceItem *device = deviceList;
431 while (device != NULL) {
432 if (device->controller == controller) {
433 IOS_RemoveJoystickDevice(device);
436 device = device->next;
467 return device ? (int)
device->controller.playerIndex : -1;
495 return SDL_SetError(
"Could not open Joystick: no hardware device for the specified index");
498 joystick->hwdata =
device;
499 joystick->instance_id =
device->instance_id;
501 joystick->naxes =
device->naxes;
502 joystick->nhats =
device->nhats;
503 joystick->nbuttons =
device->nbuttons;
504 joystick->nballs = 0;
506 device->joystick = joystick;
509 if (
device->accelerometer) {
517 [motionManager startAccelerometerUpdates];
520#ifdef SDL_JOYSTICK_MFI
521 if (
device->uses_pause_handler) {
522 GCController *controller =
device->controller;
523 controller.controllerPausedHandler = ^(GCController *
c) {
524 if (joystick->hwdata) {
525 ++joystick->hwdata->num_pause_presses;
544 const SInt16 maxsint16 = 0x7FFF;
545 CMAcceleration accel;
583#ifdef SDL_JOYSTICK_MFI
585IOS_MFIJoystickHatStateForDPad(GCControllerDirectionPad *dpad)
589 if (dpad.up.isPressed) {
591 }
else if (dpad.down.isPressed) {
595 if (dpad.left.isPressed) {
597 }
else if (dpad.right.isPressed) {
614 GCController *controller = joystick->hwdata->controller;
617 int updateplayerindex = 0;
618 int pause_button_index = 0;
620 if (controller.extendedGamepad) {
621 GCExtendedGamepad *gamepad = controller.extendedGamepad;
625 (
Sint16) (gamepad.leftThumbstick.xAxis.value * 32767),
626 (
Sint16) (gamepad.leftThumbstick.yAxis.value * -32767),
627 (
Sint16) ((gamepad.leftTrigger.value * 65535) - 32768),
628 (
Sint16) (gamepad.rightThumbstick.xAxis.value * 32767),
629 (
Sint16) (gamepad.rightThumbstick.yAxis.value * -32767),
630 (
Sint16) ((gamepad.rightTrigger.value * 65535) - 32768),
634 Uint8 buttons[joystick->nbuttons];
635 int button_count = 0;
638 buttons[button_count++] = gamepad.buttonA.isPressed;
639 buttons[button_count++] = gamepad.buttonB.isPressed;
640 buttons[button_count++] = gamepad.buttonX.isPressed;
641 buttons[button_count++] = gamepad.buttonY.isPressed;
642 buttons[button_count++] = gamepad.leftShoulder.isPressed;
643 buttons[button_count++] = gamepad.rightShoulder.isPressed;
646#pragma clang diagnostic push
647#pragma clang diagnostic ignored "-Wunguarded-availability-new"
649 buttons[button_count++] = gamepad.leftThumbstickButton.isPressed;
652 buttons[button_count++] = gamepad.rightThumbstickButton.isPressed;
655 buttons[button_count++] = gamepad.buttonOptions.isPressed;
659 if (joystick->hwdata->uses_pause_handler) {
660 pause_button_index = button_count;
661 buttons[button_count++] = joystick->delayed_guide_button;
663 buttons[button_count++] = gamepad.buttonMenu.isPressed;
666#pragma clang diagnostic pop
668 hatstate = IOS_MFIJoystickHatStateForDPad(gamepad.dpad);
674 if ((
i != 2 &&
i != 5) || axes[i] != -32768) {
675 updateplayerindex |= (joystick->axes[i].value != axes[i]);
680 for (
i = 0;
i < button_count;
i++) {
681 updateplayerindex |= (joystick->buttons[i] != buttons[i]);
684 }
else if (controller.gamepad) {
685 GCGamepad *gamepad = controller.gamepad;
688 Uint8 buttons[joystick->nbuttons];
689 int button_count = 0;
690 buttons[button_count++] = gamepad.buttonA.isPressed;
691 buttons[button_count++] = gamepad.buttonB.isPressed;
692 buttons[button_count++] = gamepad.buttonX.isPressed;
693 buttons[button_count++] = gamepad.buttonY.isPressed;
694 buttons[button_count++] = gamepad.leftShoulder.isPressed;
695 buttons[button_count++] = gamepad.rightShoulder.isPressed;
696 pause_button_index = button_count;
697 buttons[button_count++] = joystick->delayed_guide_button;
699 hatstate = IOS_MFIJoystickHatStateForDPad(gamepad.dpad);
701 for (
i = 0;
i < button_count;
i++) {
702 updateplayerindex |= (joystick->buttons[i] != buttons[i]);
707 else if (controller.microGamepad) {
708 GCMicroGamepad *gamepad = controller.microGamepad;
711 (
Sint16) (gamepad.dpad.xAxis.value * 32767),
712 (
Sint16) (gamepad.dpad.yAxis.value * -32767),
716 updateplayerindex |= (joystick->axes[i].value != axes[i]);
720 Uint8 buttons[joystick->nbuttons];
721 int button_count = 0;
722 buttons[button_count++] = gamepad.buttonA.isPressed;
723 buttons[button_count++] = gamepad.buttonX.isPressed;
724#pragma clang diagnostic push
725#pragma clang diagnostic ignored "-Wunguarded-availability-new"
728 if (joystick->hwdata->uses_pause_handler) {
729 pause_button_index = button_count;
730 buttons[button_count++] = joystick->delayed_guide_button;
732 buttons[button_count++] = gamepad.buttonMenu.isPressed;
735#pragma clang diagnostic pop
737 for (
i = 0;
i < button_count;
i++) {
738 updateplayerindex |= (joystick->buttons[i] != buttons[i]);
744 if (joystick->nhats > 0) {
745 updateplayerindex |= (joystick->hats[0] != hatstate);
749 if (joystick->hwdata->uses_pause_handler) {
750 for (
i = 0;
i < joystick->hwdata->num_pause_presses;
i++) {
753 updateplayerindex = YES;
755 joystick->hwdata->num_pause_presses = 0;
758 if (updateplayerindex && controller.playerIndex == -1) {
759 BOOL usedPlayerIndexSlots[4] = {NO, NO, NO, NO};
763 if (
c != controller &&
c.playerIndex >= 0) {
772 if (!usedPlayerIndexSlots[
i]) {
773 controller.playerIndex =
i;
797 if (
device->accelerometer) {
799 }
else if (
device->controller) {
816 if (
device->accelerometer) {
818 [motionManager stopAccelerometerUpdates];
820 }
else if (
device->controller) {
821#ifdef SDL_JOYSTICK_MFI
822 GCController *controller =
device->controller;
823 controller.controllerPausedHandler = nil;
824 controller.playerIndex = -1;
837#ifdef SDL_JOYSTICK_MFI
838 NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
840 if (connectObserver) {
841 [center removeObserver:connectObserver name:GCControllerDidConnectNotification object:nil];
842 connectObserver = nil;
845 if (disconnectObserver) {
846 [center removeObserver:disconnectObserver name:GCControllerDidDisconnectNotification object:nil];
847 disconnectObserver = nil;
852 SDL_AppleTVRemoteRotationHintChanged,
NULL);
#define SDL_IPHONE_MAX_GFORCE
#define SDL_DelHintCallback
#define SDL_GetHintBoolean
#define SDL_AddHintCallback
#define SDL_Unsupported()
@ SDL_CONTROLLER_BUTTON_B
@ SDL_CONTROLLER_BUTTON_BACK
@ SDL_CONTROLLER_BUTTON_LEFTSTICK
@ SDL_CONTROLLER_BUTTON_START
@ SDL_CONTROLLER_BUTTON_RIGHTSHOULDER
@ SDL_CONTROLLER_BUTTON_LEFTSHOULDER
@ SDL_CONTROLLER_BUTTON_X
@ SDL_CONTROLLER_BUTTON_RIGHTSTICK
@ SDL_CONTROLLER_BUTTON_Y
@ SDL_CONTROLLER_BUTTON_A
#define SDL_HINT_ACCELEROMETER_AS_JOYSTICK
A variable controlling whether the Android / iOS built-in accelerometer should be listed as a joystic...
#define SDL_HINT_TV_REMOTE_AS_JOYSTICK
A variable controlling whether the Android / tvOS remotes should be listed as joystick devices,...
#define SDL_HINT_APPLE_TV_REMOTE_ALLOW_ROTATION
A variable controlling whether the Apple TV remote's joystick axes will automatically match the rotat...
int SDL_PrivateJoystickHat(SDL_Joystick *joystick, Uint8 hat, Uint8 value)
void SDL_PrivateJoystickRemoved(SDL_JoystickID device_instance)
int SDL_PrivateJoystickAxis(SDL_Joystick *joystick, Uint8 axis, Sint16 value)
void SDL_PrivateJoystickAdded(SDL_JoystickID device_instance)
SDL_JoystickID SDL_GetNextJoystickInstanceID()
int SDL_PrivateJoystickButton(SDL_Joystick *joystick, Uint8 button, Uint8 state)
GLuint const GLchar * name
#define SDL_arraysize(array)
#define SDL_HARDWARE_BUS_BLUETOOTH
static int IOS_JoystickGetDevicePlayerIndex(int device_index)
static void IOS_JoystickDetect(void)
static SDL_JoystickDeviceItem * deviceList
static void IOS_JoystickClose(SDL_Joystick *joystick)
static void IOS_AccelerometerUpdate(SDL_Joystick *joystick)
static void IOS_AddMFIJoystickDevice(SDL_JoystickDeviceItem *device, GCController *controller)
static SDL_JoystickDeviceItem * GetDeviceForIndex(int device_index)
SDL_JoystickDriver SDL_IOS_JoystickDriver
static SDL_JoystickGUID IOS_JoystickGetDeviceGUID(int device_index)
static void IOS_JoystickUpdate(SDL_Joystick *joystick)
static int IOS_JoystickOpen(SDL_Joystick *joystick, int device_index)
static void IOS_AddJoystickDevice(GCController *controller, SDL_bool accelerometer)
static const char * IOS_JoystickGetDeviceName(int device_index)
static void IOS_MFIJoystickUpdate(SDL_Joystick *joystick)
static SDL_JoystickDeviceItem * IOS_RemoveJoystickDevice(SDL_JoystickDeviceItem *device)
static int IOS_JoystickGetCount(void)
static const char * accelerometerName
static CMMotionManager * motionManager
static void IOS_JoystickQuit(void)
static int IOS_JoystickInit(void)
static int IOS_JoystickRumble(SDL_Joystick *joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble, Uint32 duration_ms)
static SDL_JoystickID IOS_JoystickGetDeviceInstanceID(int device_index)
int SDL_AppleTVRemoteOpenedAsJoystick
return Display return Display Bool Bool int int int return Display XEvent Bool(*) XPointer return Display return Display Drawable _Xconst char unsigned int unsigned int return Display Pixmap Pixmap XColor XColor unsigned int unsigned int return Display _Xconst char char int char return Display Visual unsigned int int int char unsigned int unsigned int in i)
static SDL_AudioDeviceID device
struct joystick_hwdata * next