Skip to content

Commit

Permalink
Rumble iOS device if Player1 controller does not support it
Browse files Browse the repository at this point in the history
  • Loading branch information
XITRIX committed Oct 27, 2024
1 parent ac5b1a9 commit b370e2f
Show file tree
Hide file tree
Showing 2 changed files with 200 additions and 0 deletions.
184 changes: 184 additions & 0 deletions library/lib/platforms/desktop/ios_darwin.mm
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,175 @@
#import <borealis/platforms/desktop/desktop_platform.hpp>
#import <UIKit/UIKit.h>

#import <CoreHaptics/CoreHaptics.h>
#import <GameController/GameController.h>

@interface HapticContext : NSObject

-(void)setMotorAmplitude:(unsigned short)amplitude;
-(void)cleanup;

+(HapticContext*) createContextForHighFreqMotor;
+(HapticContext*) createContextForLowFreqMotor;
+(HapticContext*) createContextForLeftTrigger;
+(HapticContext*) createContextForRightTrigger;

@end

@implementation HapticContext {
GCControllerPlayerIndex _playerIndex;
CHHapticEngine* _hapticEngine API_AVAILABLE(ios(13.0), tvos(14.0));
id<CHHapticPatternPlayer> _hapticPlayer API_AVAILABLE(ios(13.0), tvos(14.0));
BOOL _playing;
}

-(void)cleanup API_AVAILABLE(ios(14.0), tvos(14.0)) {
if (_hapticPlayer != nil) {
[_hapticPlayer cancelAndReturnError:nil];
_hapticPlayer = nil;
}
if (_hapticEngine != nil) {
[_hapticEngine stopWithCompletionHandler:nil];
_hapticEngine = nil;
}
}

-(void)setMotorAmplitude:(unsigned short)amplitude API_AVAILABLE(ios(14.0), tvos(14.0)) {
NSError* error;

// Check if the haptic engine died
if (_hapticEngine == nil) {
return;
}

// Stop the effect entirely if the amplitude is 0
if (amplitude == 0) {
if (_playing) {
[_hapticPlayer stopAtTime:0 error:&error];
_playing = NO;
}

return;
}

if (_hapticPlayer == nil) {
// We must initialize the intensity to 1.0f because the dynamic parameters are multiplied by this value before being applied
CHHapticEventParameter* intensityParameter = [[CHHapticEventParameter alloc] initWithParameterID:CHHapticEventParameterIDHapticIntensity value:1.0f];
CHHapticEvent* hapticEvent = [[CHHapticEvent alloc] initWithEventType:CHHapticEventTypeHapticContinuous parameters:[NSArray arrayWithObject:intensityParameter] relativeTime:0 duration:GCHapticDurationInfinite];
CHHapticPattern* hapticPattern = [[CHHapticPattern alloc] initWithEvents:[NSArray arrayWithObject:hapticEvent] parameters:[[NSArray alloc] init] error:&error];
if (error != nil) {
NSLog(@"Controller %d: Haptic pattern creation failed: %@", _playerIndex, error);
return;
}

_hapticPlayer = [_hapticEngine createPlayerWithPattern:hapticPattern error:&error];
if (error != nil) {
NSLog(@"Controller %d: Haptic player creation failed: %@", _playerIndex, error);
return;
}
}

CHHapticDynamicParameter* intensityParameter = [[CHHapticDynamicParameter alloc] initWithParameterID:CHHapticDynamicParameterIDHapticIntensityControl value:amplitude / 65535.0f relativeTime:0];
[_hapticPlayer sendParameters:[NSArray arrayWithObject:intensityParameter] atTime:CHHapticTimeImmediate error:&error];
if (error != nil) {
NSLog(@"Controller %d: Haptic player parameter update failed: %@", _playerIndex, error);
return;
}

if (!_playing) {
[_hapticPlayer startAtTime:0 error:&error];
if (error != nil) {
_hapticPlayer = nil;
NSLog(@"Controller %d: Haptic playback start failed: %@", _playerIndex, error);
return;
}

_playing = YES;
}
}

-(id) initWithLocality:(GCHapticsLocality)locality API_AVAILABLE(ios(14.0), tvos(14.0)) {
NSLog(@"Controller %d does not support haptic locality: %@", 0, locality);
if (@available(iOS 13.0, *)) {
NSError *error = nil;
_hapticEngine = [[CHHapticEngine alloc] initAndReturnError:&error];
} else{
return nil;
}
_playerIndex = GCControllerPlayerIndex1;

NSError* error;
[_hapticEngine startAndReturnError:&error];
if (error != nil) {
NSLog(@"Controller %d: Haptic engine failed to start: %@", _playerIndex, error);
return nil;
}

__weak typeof(self) weakSelf = self;
_hapticEngine.stoppedHandler = ^(CHHapticEngineStoppedReason stoppedReason) {
HapticContext* me = weakSelf;
if (me == nil) {
return;
}

NSLog(@"Controller %d: Haptic engine stopped: %p", me->_playerIndex, stoppedReason);
me->_hapticPlayer = nil;
me->_hapticEngine = nil;
me->_playing = NO;
};
_hapticEngine.resetHandler = ^{
HapticContext* me = weakSelf;
if (me == nil) {
return;
}

NSLog(@"Controller %d: Haptic engine reset", me->_playerIndex);
me->_hapticPlayer = nil;
me->_playing = NO;
[me->_hapticEngine startAndReturnError:nil];
};

return self;
}

+(HapticContext*) createContextForHighFreqMotor {
if (@available(iOS 14.0, tvOS 14.0, *)) {
return [[HapticContext alloc] initWithLocality:GCHapticsLocalityRightHandle];
}
else {
return nil;
}
}

+(HapticContext*) createContextForLowFreqMotor {
if (@available(iOS 14.0, tvOS 14.0, *)) {
return [[HapticContext alloc] initWithLocality:GCHapticsLocalityLeftHandle];
}
else {
return nil;
}
}

+(HapticContext*) createContextForLeftTrigger {
if (@available(iOS 14.0, tvOS 14.0, *)) {
return [[HapticContext alloc] initWithLocality:GCHapticsLocalityLeftTrigger];
}
else {
return nil;
}
}

+(HapticContext*) createContextForRightTrigger {
if (@available(iOS 14.0, tvOS 14.0, *)) {
return [[HapticContext alloc] initWithLocality:GCHapticsLocalityRightTrigger];
}
else {
return nil;
}
}

@end

namespace brls {

ThemeVariant ios_theme() {
Expand Down Expand Up @@ -33,4 +202,19 @@ float ios_battery() {
return 0;
#endif
}

HapticContext* contextForHighFreqMotor = NULL;
HapticContext* contextForLowFreqMotor = NULL;

void device_rumble(unsigned short lowFreqMotor, unsigned short highFreqMotor) {
if (contextForHighFreqMotor == NULL)
contextForHighFreqMotor = [HapticContext createContextForLowFreqMotor];

if (contextForLowFreqMotor == NULL)
contextForLowFreqMotor = [HapticContext createContextForHighFreqMotor];

[contextForLowFreqMotor setMotorAmplitude:lowFreqMotor];
[contextForHighFreqMotor setMotorAmplitude:highFreqMotor];
}

};
16 changes: 16 additions & 0 deletions library/lib/platforms/sdl/sdl_input.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,12 @@
namespace brls
{

#if defined(PLATFORM_IOS)
extern void device_rumble(unsigned short lowFreqMotor, unsigned short highFreqMotor);
#else
void device_rumble(unsigned short lowFreqMotor, unsigned short highFreqMotor) {}
#endif

#define SDL_GAMEPAD_BUTTON_NONE SIZE_MAX
#define SDL_GAMEPAD_BUTTON_MAX 15
#define SDL_GAMEPAD_AXIS_MAX 6
Expand Down Expand Up @@ -471,6 +477,11 @@ void SDLInputManager::sendRumble(unsigned short controller, unsigned short lowFr
return;
SDL_GameController* c = controllers[controller];

if (!SDL_GameControllerHasRumble(c)) {
device_rumble(lowFreqMotor, highFreqMotor);
return;
}

SDL_GameControllerRumble(c, lowFreqMotor, highFreqMotor, 30000);
}

Expand All @@ -480,6 +491,11 @@ void SDLInputManager::sendRumble(unsigned short controller, unsigned short lowFr
return;
SDL_GameController* c = controllers[controller];

if (!SDL_GameControllerHasRumble(c)) {
device_rumble(lowFreqMotor, highFreqMotor);
return;
}

SDL_GameControllerRumble(c, lowFreqMotor, highFreqMotor, 30000);
SDL_GameControllerRumbleTriggers(c, leftTriggerFreqMotor, rightTriggerFreqMotor, 30000);
}
Expand Down

0 comments on commit b370e2f

Please sign in to comment.