Added support for game controller rumble on iOS 14
parent
2d7b33cb41
commit
1b31e9f6dc
|
@ -3972,6 +3972,8 @@
|
||||||
F37DC5C0252FDE620002E6F7 /* SDL_sysurl.c in Sources */ = {isa = PBXBuildFile; fileRef = F37DC5BC252FDE620002E6F7 /* SDL_sysurl.c */; };
|
F37DC5C0252FDE620002E6F7 /* SDL_sysurl.c in Sources */ = {isa = PBXBuildFile; fileRef = F37DC5BC252FDE620002E6F7 /* SDL_sysurl.c */; };
|
||||||
F37DC5C1252FDE620002E6F7 /* SDL_sysurl.c in Sources */ = {isa = PBXBuildFile; fileRef = F37DC5BC252FDE620002E6F7 /* SDL_sysurl.c */; };
|
F37DC5C1252FDE620002E6F7 /* SDL_sysurl.c in Sources */ = {isa = PBXBuildFile; fileRef = F37DC5BC252FDE620002E6F7 /* SDL_sysurl.c */; };
|
||||||
F37DC5C2252FDE620002E6F7 /* SDL_sysurl.c in Sources */ = {isa = PBXBuildFile; fileRef = F37DC5BC252FDE620002E6F7 /* SDL_sysurl.c */; };
|
F37DC5C2252FDE620002E6F7 /* SDL_sysurl.c in Sources */ = {isa = PBXBuildFile; fileRef = F37DC5BC252FDE620002E6F7 /* SDL_sysurl.c */; };
|
||||||
|
F37DC5F325350EBC0002E6F7 /* CoreHaptics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F37DC5F225350EBC0002E6F7 /* CoreHaptics.framework */; };
|
||||||
|
F37DC5F525350ECC0002E6F7 /* CoreHaptics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F37DC5F425350ECC0002E6F7 /* CoreHaptics.framework */; };
|
||||||
F3950CD8212BC88D00F51292 /* SDL_sensor.h in Headers */ = {isa = PBXBuildFile; fileRef = F3950CD7212BC88D00F51292 /* SDL_sensor.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
F3950CD8212BC88D00F51292 /* SDL_sensor.h in Headers */ = {isa = PBXBuildFile; fileRef = F3950CD7212BC88D00F51292 /* SDL_sensor.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||||
F3950CD9212BC88D00F51292 /* SDL_sensor.h in Headers */ = {isa = PBXBuildFile; fileRef = F3950CD7212BC88D00F51292 /* SDL_sensor.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
F3950CD9212BC88D00F51292 /* SDL_sensor.h in Headers */ = {isa = PBXBuildFile; fileRef = F3950CD7212BC88D00F51292 /* SDL_sensor.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||||
F3950CDA212BC88D00F51292 /* SDL_sensor.h in Headers */ = {isa = PBXBuildFile; fileRef = F3950CD7212BC88D00F51292 /* SDL_sensor.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
F3950CDA212BC88D00F51292 /* SDL_sensor.h in Headers */ = {isa = PBXBuildFile; fileRef = F3950CD7212BC88D00F51292 /* SDL_sensor.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||||
|
@ -4524,6 +4526,8 @@
|
||||||
BECDF6BE0761BA81005FE872 /* SDL2 */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = SDL2; sourceTree = BUILT_PRODUCTS_DIR; };
|
BECDF6BE0761BA81005FE872 /* SDL2 */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = SDL2; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
DB31407717554B71006C0E22 /* libSDL2.dylib */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.dylib"; includeInIndex = 0; path = libSDL2.dylib; sourceTree = BUILT_PRODUCTS_DIR; };
|
DB31407717554B71006C0E22 /* libSDL2.dylib */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.dylib"; includeInIndex = 0; path = libSDL2.dylib; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
F37DC5BC252FDE620002E6F7 /* SDL_sysurl.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_sysurl.c; sourceTree = "<group>"; };
|
F37DC5BC252FDE620002E6F7 /* SDL_sysurl.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_sysurl.c; sourceTree = "<group>"; };
|
||||||
|
F37DC5F225350EBC0002E6F7 /* CoreHaptics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreHaptics.framework; path = System/Library/Frameworks/CoreHaptics.framework; sourceTree = SDKROOT; };
|
||||||
|
F37DC5F425350ECC0002E6F7 /* CoreHaptics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreHaptics.framework; path = Platforms/AppleTVOS.platform/Developer/SDKs/AppleTVOS14.0.sdk/System/Library/Frameworks/CoreHaptics.framework; sourceTree = DEVELOPER_DIR; };
|
||||||
F3950CD7212BC88D00F51292 /* SDL_sensor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDL_sensor.h; sourceTree = "<group>"; };
|
F3950CD7212BC88D00F51292 /* SDL_sensor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDL_sensor.h; sourceTree = "<group>"; };
|
||||||
F59C710300D5CB5801000001 /* ReadMe.txt */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = text; path = ReadMe.txt; sourceTree = "<group>"; };
|
F59C710300D5CB5801000001 /* ReadMe.txt */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = text; path = ReadMe.txt; sourceTree = "<group>"; };
|
||||||
F59C710600D5CB5801000001 /* SDL.info */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = text; path = SDL.info; sourceTree = "<group>"; };
|
F59C710600D5CB5801000001 /* SDL.info */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = text; path = SDL.info; sourceTree = "<group>"; };
|
||||||
|
@ -4618,6 +4622,7 @@
|
||||||
A75FDB9D23E4CAFA00529352 /* hidapi.framework in Frameworks */,
|
A75FDB9D23E4CAFA00529352 /* hidapi.framework in Frameworks */,
|
||||||
A7D88BC823E24B0300DCD162 /* CoreGraphics.framework in Frameworks */,
|
A7D88BC823E24B0300DCD162 /* CoreGraphics.framework in Frameworks */,
|
||||||
A7D88BC223E24A8800DCD162 /* CoreMotion.framework in Frameworks */,
|
A7D88BC223E24A8800DCD162 /* CoreMotion.framework in Frameworks */,
|
||||||
|
F37DC5F325350EBC0002E6F7 /* CoreHaptics.framework in Frameworks */,
|
||||||
A7D88B4E23E2437C00DCD162 /* CoreVideo.framework in Frameworks */,
|
A7D88B4E23E2437C00DCD162 /* CoreVideo.framework in Frameworks */,
|
||||||
A7D88BBE23E24A6000DCD162 /* GameController.framework in Frameworks */,
|
A7D88BBE23E24A6000DCD162 /* GameController.framework in Frameworks */,
|
||||||
A7D88B5023E2437C00DCD162 /* IOKit.framework in Frameworks */,
|
A7D88B5023E2437C00DCD162 /* IOKit.framework in Frameworks */,
|
||||||
|
@ -4637,6 +4642,7 @@
|
||||||
A7D88D0723E24BED00DCD162 /* CoreAudio.framework in Frameworks */,
|
A7D88D0723E24BED00DCD162 /* CoreAudio.framework in Frameworks */,
|
||||||
A7D88D0823E24BED00DCD162 /* CoreFoundation.framework in Frameworks */,
|
A7D88D0823E24BED00DCD162 /* CoreFoundation.framework in Frameworks */,
|
||||||
A7D88D0923E24BED00DCD162 /* CoreGraphics.framework in Frameworks */,
|
A7D88D0923E24BED00DCD162 /* CoreGraphics.framework in Frameworks */,
|
||||||
|
F37DC5F525350ECC0002E6F7 /* CoreHaptics.framework in Frameworks */,
|
||||||
A7D88D0B23E24BED00DCD162 /* CoreVideo.framework in Frameworks */,
|
A7D88D0B23E24BED00DCD162 /* CoreVideo.framework in Frameworks */,
|
||||||
A7D88D0C23E24BED00DCD162 /* GameController.framework in Frameworks */,
|
A7D88D0C23E24BED00DCD162 /* GameController.framework in Frameworks */,
|
||||||
A7D88D0E23E24BED00DCD162 /* Metal.framework in Frameworks */,
|
A7D88D0E23E24BED00DCD162 /* Metal.framework in Frameworks */,
|
||||||
|
@ -4875,6 +4881,8 @@
|
||||||
564624341FF821B70074AC87 /* Frameworks */ = {
|
564624341FF821B70074AC87 /* Frameworks */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
|
F37DC5F225350EBC0002E6F7 /* CoreHaptics.framework */,
|
||||||
|
F37DC5F425350ECC0002E6F7 /* CoreHaptics.framework */,
|
||||||
A75FDADE23E28D6600529352 /* AudioToolbox.framework */,
|
A75FDADE23E28D6600529352 /* AudioToolbox.framework */,
|
||||||
A75FDADC23E28D5500529352 /* AVFoundation.framework */,
|
A75FDADC23E28D5500529352 /* AVFoundation.framework */,
|
||||||
A75FDADA23E28D4900529352 /* CoreAudio.framework */,
|
A75FDADA23E28D4900529352 /* CoreAudio.framework */,
|
||||||
|
|
|
@ -42,6 +42,7 @@
|
||||||
#if !TARGET_OS_TV
|
#if !TARGET_OS_TV
|
||||||
#import <CoreMotion/CoreMotion.h>
|
#import <CoreMotion/CoreMotion.h>
|
||||||
#endif
|
#endif
|
||||||
|
#import <CoreHaptics/CoreHaptics.h>
|
||||||
|
|
||||||
#ifdef SDL_JOYSTICK_MFI
|
#ifdef SDL_JOYSTICK_MFI
|
||||||
#import <GameController/GameController.h>
|
#import <GameController/GameController.h>
|
||||||
|
@ -771,10 +772,163 @@ IOS_MFIJoystickUpdate(SDL_Joystick * joystick)
|
||||||
#endif /* SDL_JOYSTICK_MFI */
|
#endif /* SDL_JOYSTICK_MFI */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@interface SDL_RumbleMotor : NSObject
|
||||||
|
@end
|
||||||
|
|
||||||
|
@implementation SDL_RumbleMotor {
|
||||||
|
CHHapticEngine *engine API_AVAILABLE(ios(13.0), tvos(14.0));
|
||||||
|
id<CHHapticPatternPlayer> player API_AVAILABLE(ios(13.0), tvos(14.0));
|
||||||
|
}
|
||||||
|
|
||||||
|
-(void)cleanup
|
||||||
|
{
|
||||||
|
if (self->player != nil) {
|
||||||
|
[self->player cancelAndReturnError:nil];
|
||||||
|
self->player = nil;
|
||||||
|
}
|
||||||
|
if (self->engine != nil) {
|
||||||
|
[self->engine stopWithCompletionHandler:nil];
|
||||||
|
self->engine = nil;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
-(int)setIntensity:(float)intensity
|
||||||
|
{
|
||||||
|
if (@available(iOS 14.0, tvOS 14.0, *)) {
|
||||||
|
NSError *error;
|
||||||
|
|
||||||
|
if (self->player != nil) {
|
||||||
|
[self->player stopAtTime:0 error:&error];
|
||||||
|
self->player = nil;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (self->engine == nil) {
|
||||||
|
return SDL_SetError("Haptics engine not available");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (intensity <= 0.01f) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
CHHapticEventParameter *param = [[CHHapticEventParameter alloc] initWithParameterID:CHHapticEventParameterIDHapticIntensity value:intensity];
|
||||||
|
CHHapticEvent *event = [[CHHapticEvent alloc] initWithEventType:CHHapticEventTypeHapticContinuous parameters:[NSArray arrayWithObjects:param, nil] relativeTime:0 duration:GCHapticDurationInfinite];
|
||||||
|
CHHapticPattern *pattern = [[CHHapticPattern alloc] initWithEvents:[NSArray arrayWithObject:event] parameters:[[NSArray alloc] init] error:&error];
|
||||||
|
if (error != nil) {
|
||||||
|
return SDL_SetError("Couldn't create haptic pattern: %s", [error.localizedDescription UTF8String]);
|
||||||
|
}
|
||||||
|
|
||||||
|
self->player = [self->engine createPlayerWithPattern:pattern error:&error];
|
||||||
|
if (error != nil) {
|
||||||
|
return SDL_SetError("Couldn't create haptic player: %s", [error.localizedDescription UTF8String]);
|
||||||
|
}
|
||||||
|
|
||||||
|
[self->player startAtTime:0 error:&error];
|
||||||
|
if (error != nil) {
|
||||||
|
self->player = nil;
|
||||||
|
return SDL_SetError("Couldn't start playback: %s", [error.localizedDescription UTF8String]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
-(id) initWithController:(GCController*)controller locality:(GCHapticsLocality)locality API_AVAILABLE(ios(14.0), tvos(14.0))
|
||||||
|
{
|
||||||
|
NSError *error;
|
||||||
|
|
||||||
|
self->engine = [controller.haptics createEngineWithLocality:locality];
|
||||||
|
if (self->engine == nil) {
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
|
||||||
|
[self->engine startAndReturnError:&error];
|
||||||
|
if (error != nil) {
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
|
||||||
|
__weak typeof(self) weakSelf = self;
|
||||||
|
self->engine.stoppedHandler = ^(CHHapticEngineStoppedReason stoppedReason) {
|
||||||
|
SDL_RumbleMotor *_this = weakSelf;
|
||||||
|
if (_this == nil) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_this->player = nil;
|
||||||
|
_this->engine = nil;
|
||||||
|
};
|
||||||
|
self->engine.resetHandler = ^{
|
||||||
|
SDL_RumbleMotor *_this = weakSelf;
|
||||||
|
if (_this == nil) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_this->player = nil;
|
||||||
|
[_this->engine startAndReturnError:nil];
|
||||||
|
};
|
||||||
|
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
@interface SDL_RumbleContext : NSObject
|
||||||
|
@end
|
||||||
|
|
||||||
|
@implementation SDL_RumbleContext {
|
||||||
|
SDL_RumbleMotor *low_frequency_motor;
|
||||||
|
SDL_RumbleMotor *high_frequency_motor;
|
||||||
|
}
|
||||||
|
|
||||||
|
-(id) initWithLowFrequencyMotor:(SDL_RumbleMotor*)low_frequency_motor andHighFrequencyMotor:(SDL_RumbleMotor*)high_frequency_motor
|
||||||
|
{
|
||||||
|
self->low_frequency_motor = low_frequency_motor;
|
||||||
|
self->high_frequency_motor = high_frequency_motor;
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
-(int) rumbleWithLowFrequency:(Uint16)low_frequency_rumble andHighFrequency:(Uint16)high_frequency_rumble
|
||||||
|
{
|
||||||
|
int result = 0;
|
||||||
|
|
||||||
|
result += [self->low_frequency_motor setIntensity:((float)low_frequency_rumble / 65535.0f)];
|
||||||
|
result += [self->high_frequency_motor setIntensity:((float)high_frequency_rumble / 65535.0f)];
|
||||||
|
return ((result < 0) ? -1 : 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
static SDL_RumbleContext *IOS_JoystickInitRumble(GCController *controller)
|
||||||
|
{
|
||||||
|
@autoreleasepool {
|
||||||
|
if (@available(iOS 14.0, tvOS 14.0, *)) {
|
||||||
|
SDL_RumbleMotor *low_frequency_motor = [[SDL_RumbleMotor alloc] initWithController:controller locality:GCHapticsLocalityLeftHandle];
|
||||||
|
SDL_RumbleMotor *high_frequency_motor = [[SDL_RumbleMotor alloc] initWithController:controller locality:GCHapticsLocalityRightHandle];
|
||||||
|
if (low_frequency_motor && high_frequency_motor) {
|
||||||
|
return [[SDL_RumbleContext alloc] initWithLowFrequencyMotor:low_frequency_motor andHighFrequencyMotor:high_frequency_motor];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
IOS_JoystickRumble(SDL_Joystick * joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble)
|
IOS_JoystickRumble(SDL_Joystick * joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble)
|
||||||
{
|
{
|
||||||
|
SDL_JoystickDeviceItem *device = joystick->hwdata;
|
||||||
|
if (@available(iOS 14.0, tvOS 14.0, *)) {
|
||||||
|
if (!device->rumble && device->controller && device->controller.haptics) {
|
||||||
|
SDL_RumbleContext *rumble = IOS_JoystickInitRumble(device->controller);
|
||||||
|
if (rumble) {
|
||||||
|
device->rumble = (void *)CFBridgingRetain(rumble);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (device->rumble) {
|
||||||
|
SDL_RumbleContext *rumble = (__bridge SDL_RumbleContext *)device->rumble;
|
||||||
|
return [rumble rumbleWithLowFrequency:low_frequency_rumble andHighFrequency:high_frequency_rumble];
|
||||||
|
} else {
|
||||||
return SDL_Unsupported();
|
return SDL_Unsupported();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
@ -805,6 +959,11 @@ IOS_JoystickClose(SDL_Joystick * joystick)
|
||||||
device->joystick = NULL;
|
device->joystick = NULL;
|
||||||
|
|
||||||
@autoreleasepool {
|
@autoreleasepool {
|
||||||
|
if (device->rumble) {
|
||||||
|
CFRelease(device->rumble);
|
||||||
|
device->rumble = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
if (device->accelerometer) {
|
if (device->accelerometer) {
|
||||||
#if !TARGET_OS_TV
|
#if !TARGET_OS_TV
|
||||||
[motionManager stopAccelerometerUpdates];
|
[motionManager stopAccelerometerUpdates];
|
||||||
|
|
|
@ -34,6 +34,7 @@ typedef struct joystick_hwdata
|
||||||
SDL_bool remote;
|
SDL_bool remote;
|
||||||
|
|
||||||
GCController __unsafe_unretained *controller;
|
GCController __unsafe_unretained *controller;
|
||||||
|
void *rumble;
|
||||||
SDL_bool uses_pause_handler;
|
SDL_bool uses_pause_handler;
|
||||||
int num_pause_presses;
|
int num_pause_presses;
|
||||||
Uint32 pause_button_down_time;
|
Uint32 pause_button_down_time;
|
||||||
|
|
Loading…
Reference in New Issue