mFI on OSX (#14975)

This adds mFI as a controller driver for OSX, as well as adding rumble
support for mFI controllers. Also add support for the Home button.

Also fixed a couple warnings.
This commit is contained in:
warmenhoven 2023-02-13 15:49:35 -05:00 committed by GitHub
parent df05efe068
commit 5685b5a9a1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 224 additions and 35 deletions

View File

@ -645,7 +645,7 @@ static const enum joypad_driver_enum JOYPAD_DEFAULT_DRIVER = JOYPAD_ANDROID;
static const enum joypad_driver_enum JOYPAD_DEFAULT_DRIVER = JOYPAD_SDL;
#elif defined(DJGPP)
static const enum joypad_driver_enum JOYPAD_DEFAULT_DRIVER = JOYPAD_DOS;
#elif defined(IOS)
#elif defined(HAVE_MFI)
static const enum joypad_driver_enum JOYPAD_DEFAULT_DRIVER = JOYPAD_MFI;
#elif defined(HAVE_HID)
static const enum joypad_driver_enum JOYPAD_DEFAULT_DRIVER = JOYPAD_HID;

View File

@ -320,9 +320,9 @@
[self _drawCore];
[self _drawMenu:video_info];
#ifdef HAVE_OVERLAY
id<MTLRenderCommandEncoder> rce = _context.rce;
#ifdef HAVE_OVERLAY
if (_overlay.enabled)
{
[_context resetRenderViewport:_overlay.fullscreen ? kFullscreenViewport : kVideoViewport];

View File

@ -25,6 +25,7 @@
#include "../../tasks/tasks_internal.h"
#import <GameController/GameController.h>
#import <CoreHaptics/CoreHaptics.h>
#ifndef MAX_MFI_CONTROLLERS
#define MAX_MFI_CONTROLLERS 4
@ -35,19 +36,25 @@ enum
GCCONTROLLER_PLAYER_INDEX_UNSET = -1,
};
@class MFIRumbleController;
/* TODO/FIXME - static globals */
static uint32_t mfi_buttons[MAX_USERS];
static int16_t mfi_axes[MAX_USERS][4];
static uint32_t mfi_controllers[MAX_MFI_CONTROLLERS];
static MFIRumbleController *mfi_rumblers[MAX_MFI_CONTROLLERS];
static NSMutableArray *mfiControllers;
static bool mfi_inited;
static bool apple_gamecontroller_available(void)
{
#if defined(IOS)
int major, minor;
get_ios_version(&major, &minor);
if (major <= 6)
return false;
#endif
return true;
}
@ -100,19 +107,20 @@ static void apple_gamecontroller_joypad_poll_internal(GCController *controller)
*buttons |= gp.rightShoulder.pressed ? (1 << RETRO_DEVICE_ID_JOYPAD_R) : 0;
*buttons |= gp.leftTrigger.pressed ? (1 << RETRO_DEVICE_ID_JOYPAD_L2) : 0;
*buttons |= gp.rightTrigger.pressed ? (1 << RETRO_DEVICE_ID_JOYPAD_R2) : 0;
#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 120100 || __TV_OS_VERSION_MAX_ALLOWED >= 120100
if (@available(iOS 12.1, *))
#if OSX || __IPHONE_OS_VERSION_MAX_ALLOWED >= 120100 || __TV_OS_VERSION_MAX_ALLOWED >= 120100
if (@available(iOS 12.1, macOS 10.15, *))
{
*buttons |= gp.leftThumbstickButton.pressed ? (1 << RETRO_DEVICE_ID_JOYPAD_L3) : 0;
*buttons |= gp.rightThumbstickButton.pressed ? (1 << RETRO_DEVICE_ID_JOYPAD_R3) : 0;
}
#endif
#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 130000 || __TV_OS_VERSION_MAX_ALLOWED >= 130000
if (@available(iOS 13, *))
#if OSX || __IPHONE_OS_VERSION_MAX_ALLOWED >= 130000 || __TV_OS_VERSION_MAX_ALLOWED >= 130000
if (@available(iOS 13, tvOS 13, macOS 10.15, *))
{
/* Support "Options" button present in PS4 / XBox One controllers */
*buttons |= gp.buttonOptions.pressed ? (1 << RETRO_DEVICE_ID_JOYPAD_SELECT) : 0;
*buttons |= gp.buttonHome.pressed ? (1 << RARCH_FIRST_CUSTOM_BIND) : 0;
/* Support buttons that aren't supported by older mFi controller via "hotkey" combinations:
*
@ -171,37 +179,32 @@ static void apple_gamecontroller_joypad_poll(void)
apple_gamecontroller_joypad_poll_internal(controller);
}
/* GCGamepad is deprecated */
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated"
static void apple_gamecontroller_joypad_register(GCGamepad *gamepad)
static void apple_gamecontroller_joypad_register(GCController *controller)
{
#ifdef __IPHONE_14_0
/* Don't let tvOS or iOS do anything with **our** buttons!!
* iOS will start a screen recording if you hold or doubleclick
* the OPTIONS button, we don't want that. */
if (@available(iOS 14.0, tvOS 14.0, *))
if (@available(iOS 14.0, tvOS 14.0, macOS 10.15, *))
{
GCExtendedGamepad *gp = (GCExtendedGamepad *)gamepad.controller.extendedGamepad;
GCExtendedGamepad *gp = (GCExtendedGamepad *)controller.extendedGamepad;
gp.buttonOptions.preferredSystemGestureState = GCSystemGestureStateDisabled;
gp.buttonMenu.preferredSystemGestureState = GCSystemGestureStateDisabled;
gp.buttonHome.preferredSystemGestureState = GCSystemGestureStateDisabled;
}
#endif
gamepad.valueChangedHandler = ^(GCGamepad *updateGamepad, GCControllerElement *element)
{
apple_gamecontroller_joypad_poll_internal(updateGamepad.controller);
};
/* controllerPausedHandler is deprecated in favor
* of being able to deal with the menu
* button as any other button */
if (@available(iOS 13, *))
return;
/* GCGamepad is deprecated */
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated"
{
gamepad.controller.controllerPausedHandler = ^(GCController *controller)
controller.controllerPausedHandler = ^(GCController *controller)
{
uint32_t slot = (uint32_t)controller.playerIndex;
@ -256,14 +259,153 @@ static void apple_gamecontroller_joypad_register(GCGamepad *gamepad)
});
};
}
}
#pragma clang diagnostic pop
}
static void mfi_joypad_autodetect_add(unsigned autoconf_pad)
{
input_autoconfigure_connect("mFi Controller", NULL, mfi_joypad.ident, autoconf_pad, 0, 0);
}
#define MFI_RUMBLE_AVAIL API_AVAILABLE(macos(11.0), ios(14.0), tvos(14.0))
@interface MFIRumbleController : NSObject
@property (nonatomic, strong, readonly) GCController *controller;
@property (nonatomic, strong) CHHapticEngine *engine MFI_RUMBLE_AVAIL;
@property (nonatomic, strong, readonly) id<CHHapticPatternPlayer> strongPlayer MFI_RUMBLE_AVAIL;
@property (nonatomic, strong, readonly) id<CHHapticPatternPlayer> weakPlayer MFI_RUMBLE_AVAIL;
@end
@implementation MFIRumbleController
@synthesize strongPlayer = _strongPlayer;
@synthesize weakPlayer = _weakPlayer;
- (instancetype)initWithController:(GCController*)controller MFI_RUMBLE_AVAIL
{
if (self = [super init])
{
if (!controller.haptics)
return self;
_controller = controller;
[self setupEngine];
if (!self.engine)
return self;
_strongPlayer = [self createPlayerWithSharpness:1.0f];
_weakPlayer = [self createPlayerWithSharpness:0.5f];
}
return self;
}
- (void)setupEngine MFI_RUMBLE_AVAIL
{
if (self.engine)
return;
if (!self.controller)
return;
CHHapticEngine *engine = [self.controller.haptics createEngineWithLocality:GCHapticsLocalityDefault];
NSError *error;
[engine startAndReturnError:&error];
if (error)
return;
self.engine = engine;
__weak MFIRumbleController *weakSelf = self;
self.engine.stoppedHandler = ^(CHHapticEngineStoppedReason stoppedReason) {
MFIRumbleController *strongSelf = weakSelf;
if (!strongSelf)
return;
[strongSelf shutdown];
};
self.engine.resetHandler = ^{
MFIRumbleController *strongSelf = weakSelf;
if (!strongSelf)
return;
[strongSelf.engine startAndReturnError:nil];
};
}
- (id<CHHapticPatternPlayer>)createPlayerWithSharpness:(float)sharpness MFI_RUMBLE_AVAIL
{
if (!self.controller)
return nil;
[self setupEngine];
if (!self.engine)
return nil;
CHHapticEventParameter *sharp, *intense;
CHHapticEvent *event;
CHHapticPattern *pattern;
NSError *error;
sharp = [[CHHapticEventParameter alloc]
initWithParameterID:CHHapticEventParameterIDHapticSharpness
value:sharpness];
intense = [[CHHapticEventParameter alloc]
initWithParameterID:CHHapticEventParameterIDHapticIntensity
value:1.0f];
event = [[CHHapticEvent alloc]
initWithEventType:CHHapticEventTypeHapticContinuous
parameters:[NSArray arrayWithObjects:sharp, intense, nil]
relativeTime:0
duration:GCHapticDurationInfinite];
pattern = [[CHHapticPattern alloc]
initWithEvents:[NSArray arrayWithObject:event]
parameters:[[NSArray alloc] init]
error:&error];
if (error)
return nil;
id<CHHapticPatternPlayer> player = [self.engine createPlayerWithPattern:pattern error:&error];
if (error)
return nil;
player.isMuted = YES;
return player;
}
- (id<CHHapticPatternPlayer>)strongPlayer
{
_strongPlayer = _strongPlayer ?: [self createPlayerWithSharpness:1.0];
return _strongPlayer;
}
- (id<CHHapticPatternPlayer>)weakPlayer
{
_weakPlayer = _weakPlayer ?: [self createPlayerWithSharpness:0.5f];
return _weakPlayer;
}
- (void)shutdown
{
if (@available(iOS 14, tvOS 14, macOS 11, *)) {
if (self.weakPlayer)
[self.weakPlayer cancelAndReturnError:nil];
_weakPlayer = nil;
if (self.strongPlayer)
[self.strongPlayer cancelAndReturnError:nil];
_strongPlayer = nil;
if (self.engine)
[self.engine stopWithCompletionHandler:nil];
self.engine = nil;
}
}
@end
static void apple_gamecontroller_joypad_setup_haptics(GCController *controller)
{
if (@available(iOS 14, tvOS 14, macOS 11, *)) {
mfi_rumblers[controller.playerIndex] = [[MFIRumbleController alloc] initWithController:controller];
}
}
static void apple_gamecontroller_joypad_connect(GCController *controller)
{
signed desired_index = (int32_t)controller.playerIndex;
@ -298,10 +440,8 @@ static void apple_gamecontroller_joypad_connect(GCController *controller)
}
}
/* GCGamepad is deprecated */
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated"
[mfiControllers addObject:controller];
/* Move any non-game controllers (like the siri remote) to the end */
if (mfiControllers.count > 1)
{
@ -311,8 +451,8 @@ static void apple_gamecontroller_joypad_connect(GCController *controller)
for (GCController *connectedController in mfiControllers)
{
if ( connectedController.gamepad == nil
&& connectedController.extendedGamepad == nil )
if ( connectedController.microGamepad != nil
|| connectedController.extendedGamepad == nil )
connectedNonGameControllerIndex = index;
index++;
}
@ -327,10 +467,10 @@ static void apple_gamecontroller_joypad_connect(GCController *controller)
gc.playerIndex = newPlayerIndex++;
}
apple_gamecontroller_joypad_register(controller.gamepad);
apple_gamecontroller_joypad_register(controller);
apple_gamecontroller_joypad_setup_haptics(controller);
mfi_joypad_autodetect_add((unsigned)controller.playerIndex);
}
#pragma clang diagnostic pop
}
static void apple_gamecontroller_joypad_disconnect(GCController* controller)
@ -340,6 +480,7 @@ static void apple_gamecontroller_joypad_disconnect(GCController* controller)
if (pad == GCCONTROLLER_PLAYER_INDEX_UNSET)
return;
mfi_rumblers[pad] = nil;
mfi_controllers[pad] = 0;
if ([mfiControllers containsObject:controller])
{
@ -350,8 +491,7 @@ static void apple_gamecontroller_joypad_disconnect(GCController* controller)
void *apple_gamecontroller_joypad_init(void *data)
{
static bool inited = false;
if (inited)
if (mfi_inited)
return (void*)-1;
if (!apple_gamecontroller_available())
return NULL;
@ -372,7 +512,7 @@ void *apple_gamecontroller_joypad_init(void *data)
apple_gamecontroller_joypad_disconnect([note object]);
} ];
#endif
mfi_inited = true;
return (void*)-1;
}
@ -461,6 +601,43 @@ static int16_t apple_gamecontroller_joypad_state(
return ret;
}
static bool apple_gamecontroller_joypad_set_rumble(unsigned pad,
enum retro_rumble_effect type, uint16_t strength)
{
if (pad >= MAX_MFI_CONTROLLERS)
return false;
if (@available(iOS 14, tvOS 14, macOS 11, *)) {
MFIRumbleController *rumble = mfi_rumblers[pad];
if (!rumble)
return false;
id<CHHapticPatternPlayer> player = (type == RETRO_RUMBLE_STRONG ? rumble.strongPlayer : rumble.weakPlayer);
if (!player)
return false;
if (strength == 0)
{
player.isMuted = YES;
return true;
}
else
{
player.isMuted = NO;
float str = (float)strength / 65535.0f;
CHHapticDynamicParameter *param = [[CHHapticDynamicParameter alloc]
initWithParameterID:CHHapticDynamicParameterIDHapticIntensityControl
value:str
relativeTime:0];
NSError *error;
[player sendParameters:[NSArray arrayWithObject:param] atTime:0 error:&error];
if (!error)
[player startAtTime:0 error:&error];
return error;
}
return true;
} else {
return false;
}
}
static bool apple_gamecontroller_joypad_query_pad(unsigned pad)
{
return pad < MAX_USERS;
@ -483,7 +660,7 @@ input_device_driver_t mfi_joypad = {
apple_gamecontroller_joypad_get_buttons,
apple_gamecontroller_joypad_axis,
apple_gamecontroller_joypad_poll,
NULL,
apple_gamecontroller_joypad_set_rumble,
NULL,
apple_gamecontroller_joypad_name,
"mfi",

View File

@ -703,7 +703,7 @@ DECL_AXIS(r_x_minus, -2) \
DECL_AXIS(r_y_plus, +3) \
DECL_AXIS(r_y_minus, -3)
#define IOS_MFI_DEFAULT_BINDS \
#define MFI_DEFAULT_BINDS \
DECL_BTN(a, 8) \
DECL_BTN(b, 0) \
DECL_BTN(x, 9) \
@ -802,8 +802,8 @@ const char* const input_builtin_autoconfs[] =
#ifdef EMSCRIPTEN
DECL_AUTOCONF_PID(1, 1, "rwebpad", EMSCRIPTEN_DEFAULT_BINDS),
#endif
#if TARGET_OS_IPHONE
DECL_AUTOCONF_DEVICE("mFi Controller", "mfi", IOS_MFI_DEFAULT_BINDS),
#if HAVE_MFI
DECL_AUTOCONF_DEVICE("mFi Controller", "mfi", MFI_DEFAULT_BINDS),
#endif
NULL
};

View File

@ -40,7 +40,9 @@
05D7753520A567A400646447 /* griffin_cpp.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 05D7753320A5678300646447 /* griffin_cpp.cpp */; };
05D7753720A567A700646447 /* griffin_glslang.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 05D7753420A5678400646447 /* griffin_glslang.cpp */; };
072976DD296284F600D6E00C /* OpenGL.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 072976DC296284F600D6E00C /* OpenGL.framework */; };
0746953A2997393000CCB7BD /* GameController.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 074695362995C03900CCB7BD /* GameController.framework */; };
079371D0296392420059A71C /* libMoltenVK.dylib in Embed Libraries */ = {isa = PBXBuildFile; fileRef = 07B634CF296391FF00B3D78D /* libMoltenVK.dylib */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; };
0795A8C7299A095300D5035D /* CoreHaptics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0795A8C6299A095300D5035D /* CoreHaptics.framework */; };
07B634D0296391FF00B3D78D /* libMoltenVK.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 07B634CF296391FF00B3D78D /* libMoltenVK.dylib */; };
5061C8A41AE47E510080AE14 /* libz.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 5061C8A31AE47E510080AE14 /* libz.dylib */; };
509F0C9D1AA23AFC00619ECC /* griffin_objc.m in Sources */ = {isa = PBXBuildFile; fileRef = 509F0C9C1AA23AFC00619ECC /* griffin_objc.m */; };
@ -494,6 +496,8 @@
05F2874020F2BEEA00632D47 /* task_http.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = task_http.c; sourceTree = "<group>"; };
05F2874120F2BEEA00632D47 /* task_patch.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = task_patch.c; sourceTree = "<group>"; };
072976DC296284F600D6E00C /* OpenGL.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = OpenGL.framework; path = System/Library/Frameworks/OpenGL.framework; sourceTree = SDKROOT; };
074695362995C03900CCB7BD /* GameController.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = GameController.framework; path = System/Library/Frameworks/GameController.framework; sourceTree = SDKROOT; };
0795A8C6299A095300D5035D /* CoreHaptics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreHaptics.framework; path = System/Library/Frameworks/CoreHaptics.framework; sourceTree = SDKROOT; };
07B634CF296391FF00B3D78D /* libMoltenVK.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libMoltenVK.dylib; path = Frameworks/MoltenVK/dylib/macOS/libMoltenVK.dylib; sourceTree = "<group>"; };
089C165DFE840E0CC02AAC07 /* InfoPlist.strings */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = InfoPlist.strings; path = OSX/en.lproj/InfoPlist.strings; sourceTree = "<group>"; };
29B97324FDCFA39411CA2CEA /* AppKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AppKit.framework; path = /System/Library/Frameworks/AppKit.framework; sourceTree = "<absolute>"; };
@ -559,10 +563,12 @@
files = (
D27C508C2228362700113BC0 /* AudioToolbox.framework in Frameworks */,
072976DD296284F600D6E00C /* OpenGL.framework in Frameworks */,
0746953A2997393000CCB7BD /* GameController.framework in Frameworks */,
07B634D0296391FF00B3D78D /* libMoltenVK.dylib in Frameworks */,
D27C508B2228361D00113BC0 /* AVFoundation.framework in Frameworks */,
05A8E23C20A63CF50084ABDA /* QuartzCore.framework in Frameworks */,
05A8E23A20A63CED0084ABDA /* IOSurface.framework in Frameworks */,
0795A8C7299A095300D5035D /* CoreHaptics.framework in Frameworks */,
05A8E23820A63CB40084ABDA /* Metal.framework in Frameworks */,
05269A6220ABF20500C29F1E /* MetalKit.framework in Frameworks */,
5061C8A41AE47E510080AE14 /* libz.dylib in Frameworks */,
@ -1385,6 +1391,8 @@
29B97323FDCFA39411CA2CEA /* Frameworks */ = {
isa = PBXGroup;
children = (
0795A8C6299A095300D5035D /* CoreHaptics.framework */,
074695362995C03900CCB7BD /* GameController.framework */,
D27C50892228360D00113BC0 /* AudioToolbox.framework */,
07B634CF296391FF00B3D78D /* libMoltenVK.dylib */,
072976DC296284F600D6E00C /* OpenGL.framework */,
@ -1708,6 +1716,7 @@
"-DHAVE_COCOA_METAL",
"-DHAVE_OPENGL_CORE",
"-DHAVE_VULKAN",
"-DHAVE_MFI",
);
OTHER_CODE_SIGN_FLAGS = "--deep --timestamp";
OTHER_CPLUSPLUSFLAGS = "$(OTHER_CFLAGS)";
@ -1747,6 +1756,7 @@
"-DHAVE_COCOA_METAL",
"-DHAVE_OPENGL_CORE",
"-DHAVE_VULKAN",
"-DHAVE_MFI",
);
OTHER_CODE_SIGN_FLAGS = "--deep --timestamp";
OTHER_CPLUSPLUSFLAGS = "$(OTHER_CFLAGS)";

View File

@ -1840,6 +1840,7 @@
OTHER_CFLAGS = (
"-DDONT_WANT_ARM_OPTIMIZATIONS",
"-DENABLE_HLSL",
"-DGLES_SILENCE_DEPRECATION",
"-DGLSLANG_OSINCLUDE_UNIX",
"-DHAVE_7ZIP",
"-DHAVE_AUDIOMIXER",
@ -1992,6 +1993,7 @@
OTHER_CFLAGS = (
"-DDONT_WANT_ARM_OPTIMIZATIONS",
"-DENABLE_HLSL",
"-DGLES_SILENCE_DEPRECATION",
"-DGLSLANG_OSINCLUDE_UNIX",
"-DHAVE_7ZIP",
"-DHAVE_AUDIOMIXER",