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> 49 static id connectObserver = nil;
50 static 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) {
96 device = device->
next;
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;
184 }
else if (controller.gamepad) {
198 vendor = VENDOR_APPLE;
206 else if (controller.microGamepad) {
207 GCMicroGamepad *gamepad = controller.microGamepad;
214 if ([gamepad respondsToSelector:
@selector(buttonMenu)] && gamepad.buttonMenu) {
223 vendor = VENDOR_APPLE;
253 controller.playerIndex = -1;
266 if (controller && !controller.extendedGamepad && !controller.gamepad && controller.microGamepad) {
272 while (device !=
NULL) {
276 device = device->
next;
280 if (device ==
NULL) {
300 }
else if (controller) {
304 if (deviceList ==
NULL) {
309 lastdevice = lastdevice->
next;
326 if (device ==
NULL) {
332 while (item !=
NULL) {
333 if (item == device) {
343 }
else if (device == deviceList) {
344 deviceList = device->
next;
351 #ifdef SDL_JOYSTICK_MFI 356 GCController *controller = CFBridgingRelease((__bridge CFTypeRef)(device->
controller));
357 controller.controllerPausedHandler = nil;
375 SDL_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;
460 return device ? device->
name :
"Unknown";
467 return device ? (int)device->
controller.playerIndex : -1;
494 if (device ==
NULL) {
495 return SDL_SetError(
"Could not open Joystick: no hardware device for the specified index");
498 joystick->hwdata =
device;
501 joystick->naxes = device->
naxes;
502 joystick->nhats = device->
nhats;
503 joystick->nbuttons = device->
nbuttons;
504 joystick->nballs = 0;
517 [motionManager startAccelerometerUpdates];
520 #ifdef SDL_JOYSTICK_MFI 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 585 IOS_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;
793 if (device ==
NULL) {
809 if (device ==
NULL) {
818 [motionManager stopAccelerometerUpdates];
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);
856 while (deviceList !=
NULL) {
static void IOS_MFIJoystickUpdate(SDL_Joystick *joystick)
#define SDL_HINT_TV_REMOTE_AS_JOYSTICK
A variable controlling whether the Android / tvOS remotes should be listed as joystick devices...
static int IOS_JoystickGetCount(void)
void SDL_PrivateJoystickRemoved(SDL_JoystickID device_instance)
static void IOS_JoystickUpdate(SDL_Joystick *joystick)
static SDL_JoystickDeviceItem * IOS_RemoveJoystickDevice(SDL_JoystickDeviceItem *device)
int SDL_PrivateJoystickHat(SDL_Joystick *joystick, Uint8 hat, Uint8 value)
static SDL_JoystickDeviceItem * deviceList
static int IOS_JoystickInit(void)
#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_PrivateJoystickButton(SDL_Joystick *joystick, Uint8 button, Uint8 state)
static void IOS_JoystickDetect(void)
static const char * accelerometerName
struct joystick_hwdata * next
static SDL_JoystickDeviceItem * GetDeviceForIndex(int device_index)
int SDL_PrivateJoystickAxis(SDL_Joystick *joystick, Uint8 axis, Sint16 value)
GLuint const GLchar * name
#define SDL_GetHintBoolean
#define SDL_HARDWARE_BUS_BLUETOOTH
SDL_JoystickDriver SDL_IOS_JoystickDriver
static SDL_AudioDeviceID device
static SDL_JoystickID IOS_JoystickGetDeviceInstanceID(int device_index)
static const char * IOS_JoystickGetDeviceName(int device_index)
static void IOS_JoystickQuit(void)
#define SDL_HINT_ACCELEROMETER_AS_JOYSTICK
A variable controlling whether the Android / iOS built-in accelerometer should be listed as a joystic...
static void IOS_AddMFIJoystickDevice(SDL_JoystickDeviceItem *device, GCController *controller)
static int IOS_JoystickOpen(SDL_Joystick *joystick, int device_index)
static SDL_JoystickGUID IOS_JoystickGetDeviceGUID(int device_index)
static void IOS_AccelerometerUpdate(SDL_Joystick *joystick)
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)
#define SDL_IPHONE_MAX_GFORCE
void SDL_PrivateJoystickAdded(SDL_JoystickID device_instance)
SDL_JoystickID SDL_GetNextJoystickInstanceID()
int SDL_AppleTVRemoteOpenedAsJoystick
#define SDL_AddHintCallback
#define SDL_DelHintCallback
static int IOS_JoystickGetDevicePlayerIndex(int device_index)
static void IOS_AddJoystickDevice(GCController *controller, SDL_bool accelerometer)
#define SDL_arraysize(array)
SDL_bool uses_pause_handler
static int IOS_JoystickRumble(SDL_Joystick *joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble, Uint32 duration_ms)
#define SDL_Unsupported()
GCController __unsafe_unretained * controller
static void IOS_JoystickClose(SDL_Joystick *joystick)
static CMMotionManager * motionManager