21 #include "../../SDL_internal.h"
27 #include "../../../include/SDL_config_iphoneos.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>
52 #include <Availability.h>
53 #include <objc/message.h>
59 #if (__IPHONE_OS_VERSION_MAX_ALLOWED < 121000) || (__APPLETV_OS_VERSION_MAX_ALLOWED < 121000) || (__MAC_OS_VERSION_MAX_ALLOWED < 1401000)
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;
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;
132 BOOL is_xbox = [controller.vendorName containsString: @"Xbox"];
133 BOOL is_ps4 = [controller.vendorName containsString: @"DUALSHOCK"];
135 BOOL is_MFi = (!is_xbox && !is_ps4);
149 #pragma clang diagnostic push
150 #pragma clang diagnostic ignored "-Wunguarded-availability-new"
151 if ([gamepad respondsToSelector:
@selector(
leftThumbstickButton)] && gamepad.leftThumbstickButton) {
155 if ([gamepad respondsToSelector:
@selector(
rightThumbstickButton)] && gamepad.rightThumbstickButton) {
159 if ([gamepad respondsToSelector:
@selector(
buttonOptions)] && gamepad.buttonOptions) {
163 BOOL has_direct_menu = [gamepad respondsToSelector:@selector(buttonMenu)] && gamepad.buttonMenu;
167 has_direct_menu =
FALSE;
172 if (!has_direct_menu) {
175 #pragma clang diagnostic pop
178 vendor = VENDOR_MICROSOFT;
181 vendor = VENDOR_SONY;
184 vendor = VENDOR_APPLE;
191 device->nbuttons = nbuttons;
193 }
else if (controller.gamepad) {
207 vendor = VENDOR_APPLE;
212 device->nbuttons = nbuttons;
215 else if (controller.microGamepad) {
226 vendor = VENDOR_APPLE;
231 device->nbuttons = nbuttons;
250 device->guid.data[14] =
'm';
251 device->guid.data[15] = subtype;
256 controller.playerIndex = -1;
269 if (controller && !controller.extendedGamepad && !controller.gamepad && controller.microGamepad) {
276 if (
device->controller == controller) {
287 device->accelerometer = accelerometer;
303 }
else if (controller) {
312 lastdevice = lastdevice->
next;
335 while (item !=
NULL) {
354 #ifdef SDL_JOYSTICK_MFI
359 GCController *controller = CFBridgingRelease((__bridge CFTypeRef)(
device->controller));
360 controller.controllerPausedHandler = nil;
378 SDL_AppleTVRemoteRotationHintChanged(
void *udata,
const char *
name,
const char *oldValue,
const char *newValue)
380 BOOL allowRotation = newValue !=
NULL && *newValue !=
'0';
383 for (GCController *controller
in [GCController
controllers]) {
384 if (controller.microGamepad) {
385 controller.microGamepad.allowsRotation = allowRotation;
396 NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
405 #ifdef SDL_JOYSTICK_MFI
407 if (![GCController
class]) {
411 for (GCController *controller
in [GCController
controllers]) {
417 SDL_AppleTVRemoteRotationHintChanged,
NULL);
420 connectObserver = [center addObserverForName:GCControllerDidConnectNotification
423 usingBlock:^(NSNotification *note) {
424 GCController *controller = note.object;
425 IOS_AddJoystickDevice(controller, SDL_FALSE);
431 usingBlock:^(NSNotification *note) {
432 GCController *controller = note.object;
433 SDL_JoystickDeviceItem *device = deviceList;
434 while (device != NULL) {
435 if (device->controller == controller) {
436 IOS_RemoveJoystickDevice(device);
439 device = device->next;
470 return device ? (int)
device->controller.playerIndex : -1;
478 device->controller.playerIndex = player_index;
507 return SDL_SetError(
"Could not open Joystick: no hardware device for the specified index");
510 joystick->hwdata =
device;
511 joystick->instance_id =
device->instance_id;
513 joystick->naxes =
device->naxes;
514 joystick->nhats =
device->nhats;
515 joystick->nbuttons =
device->nbuttons;
516 joystick->nballs = 0;
518 device->joystick = joystick;
521 if (
device->accelerometer) {
529 [motionManager startAccelerometerUpdates];
532 #ifdef SDL_JOYSTICK_MFI
533 if (
device->uses_pause_handler) {
534 GCController *controller =
device->controller;
535 controller.controllerPausedHandler = ^(GCController *
c) {
536 if (joystick->hwdata) {
537 ++joystick->hwdata->num_pause_presses;
556 const SInt16 maxsint16 = 0x7FFF;
557 CMAcceleration accel;
595 #ifdef SDL_JOYSTICK_MFI
601 if (dpad.up.isPressed) {
603 }
else if (dpad.down.isPressed) {
607 if (dpad.left.isPressed) {
609 }
else if (dpad.right.isPressed) {
626 GCController *controller = joystick->hwdata->controller;
629 int pause_button_index = 0;
631 if (controller.extendedGamepad) {
632 GCExtendedGamepad *gamepad = controller.extendedGamepad;
636 (
Sint16) (gamepad.leftThumbstick.xAxis.value * 32767),
637 (
Sint16) (gamepad.leftThumbstick.yAxis.value * -32767),
638 (
Sint16) ((gamepad.leftTrigger.value * 65535) - 32768),
639 (
Sint16) (gamepad.rightThumbstick.xAxis.value * 32767),
640 (
Sint16) (gamepad.rightThumbstick.yAxis.value * -32767),
641 (
Sint16) ((gamepad.rightTrigger.value * 65535) - 32768),
645 Uint8 buttons[joystick->nbuttons];
646 int button_count = 0;
649 buttons[button_count++] = gamepad.buttonA.isPressed;
650 buttons[button_count++] = gamepad.buttonB.isPressed;
651 buttons[button_count++] = gamepad.buttonX.isPressed;
652 buttons[button_count++] = gamepad.buttonY.isPressed;
653 buttons[button_count++] = gamepad.leftShoulder.isPressed;
654 buttons[button_count++] = gamepad.rightShoulder.isPressed;
657 #pragma clang diagnostic push
658 #pragma clang diagnostic ignored "-Wunguarded-availability-new"
660 buttons[button_count++] = gamepad.leftThumbstickButton.isPressed;
663 buttons[button_count++] = gamepad.rightThumbstickButton.isPressed;
666 buttons[button_count++] = gamepad.buttonOptions.isPressed;
670 if (joystick->hwdata->uses_pause_handler) {
671 pause_button_index = button_count;
672 buttons[button_count++] = joystick->delayed_guide_button;
674 buttons[button_count++] = gamepad.buttonMenu.isPressed;
677 #pragma clang diagnostic pop
685 for (
i = 0;
i < button_count;
i++) {
688 }
else if (controller.gamepad) {
689 GCGamepad *gamepad = controller.gamepad;
692 Uint8 buttons[joystick->nbuttons];
693 int button_count = 0;
694 buttons[button_count++] = gamepad.buttonA.isPressed;
695 buttons[button_count++] = gamepad.buttonB.isPressed;
696 buttons[button_count++] = gamepad.buttonX.isPressed;
697 buttons[button_count++] = gamepad.buttonY.isPressed;
698 buttons[button_count++] = gamepad.leftShoulder.isPressed;
699 buttons[button_count++] = gamepad.rightShoulder.isPressed;
700 pause_button_index = button_count;
701 buttons[button_count++] = joystick->delayed_guide_button;
705 for (
i = 0;
i < button_count;
i++) {
710 else if (controller.microGamepad) {
711 GCMicroGamepad *gamepad = controller.microGamepad;
714 (
Sint16) (gamepad.dpad.xAxis.value * 32767),
715 (
Sint16) (gamepad.dpad.yAxis.value * -32767),
722 Uint8 buttons[joystick->nbuttons];
723 int button_count = 0;
724 buttons[button_count++] = gamepad.buttonA.isPressed;
725 buttons[button_count++] = gamepad.buttonX.isPressed;
726 #pragma clang diagnostic push
727 #pragma clang diagnostic ignored "-Wunguarded-availability-new"
730 if (joystick->hwdata->uses_pause_handler) {
731 pause_button_index = button_count;
732 buttons[button_count++] = joystick->delayed_guide_button;
734 buttons[button_count++] = gamepad.buttonMenu.isPressed;
737 #pragma clang diagnostic pop
739 for (
i = 0;
i < button_count;
i++) {
745 if (joystick->nhats > 0) {
749 if (joystick->hwdata->uses_pause_handler) {
750 for (
i = 0;
i < joystick->hwdata->num_pause_presses;
i++) {
754 joystick->hwdata->num_pause_presses = 0;
775 if (
device->accelerometer) {
777 }
else if (
device->controller) {
794 if (
device->accelerometer) {
796 [motionManager stopAccelerometerUpdates];
798 }
else if (
device->controller) {
799 #ifdef SDL_JOYSTICK_MFI
800 GCController *controller =
device->controller;
801 controller.controllerPausedHandler = nil;
802 controller.playerIndex = -1;
815 #ifdef SDL_JOYSTICK_MFI
816 NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
819 [center removeObserver:connectObserver name:GCControllerDidConnectNotification object:nil];
824 [center removeObserver:disconnectObserver name:GCControllerDidDisconnectNotification object:nil];
830 SDL_AppleTVRemoteRotationHintChanged,
NULL);