git.y1.nz

SameBoy

Accurate GB/GBC emulator
download: https://git.y1.nz/archives/sameboy.tar.gz
README | Files | Log | Refs | LICENSE

iOS/GBHapticManager.m

      1 #import "GBHapticManager.h"
      2 #import "GBHapticManagerLegacy.h"
      3 #import <CoreHaptics/CoreHaptics.h>
      4 
      5 @implementation GBHapticManager
      6 {
      7 #pragma clang diagnostic push
      8 #pragma clang diagnostic ignored "-Wpartial-availability"
      9     CHHapticEngine *_engine;
     10     CHHapticEngine *_externalEngine;
     11     id<CHHapticPatternPlayer> _rumblePlayer;
     12 #pragma clang diagnostic pop
     13     __weak GCController *_controller;
     14     double _rumble;
     15     dispatch_queue_t _queue;
     16 }
     17 
     18 + (instancetype)sharedManager
     19 {
     20     static dispatch_once_t onceToken;
     21     static GBHapticManager *manager;
     22     dispatch_once(&onceToken, ^{
     23         manager = [[self alloc] init];
     24     });
     25     return manager;
     26 }
     27 
     28 - (instancetype)init
     29 {
     30     self = [super init];
     31     if (!self) return nil;
     32     if (self.class != [GBHapticManager class]) return self;
     33     
     34     if (@available(iOS 13.0, *)) {
     35             _engine = [[CHHapticEngine alloc] initAndReturnError:nil];
     36             _engine.playsHapticsOnly = true;
     37             _engine.autoShutdownEnabled = true;
     38     }
     39     if (!_engine) return [[GBHapticManagerLegacy alloc] init];
     40     _queue = dispatch_queue_create("SameBoy Haptic Queue", NULL);
     41     return self;
     42 }
     43 
     44 #pragma clang diagnostic ignored "-Wpartial-availability"
     45 
     46 - (CHHapticEvent *) eventWithType:(CHHapticEventType)type
     47                         sharpness:(double)sharpness
     48                         intensity:(double)intensity
     49                          duration:(NSTimeInterval)duration
     50 {
     51     return [[CHHapticEvent alloc] initWithEventType:type
     52                                          parameters:@[[[CHHapticEventParameter alloc] initWithParameterID:CHHapticEventParameterIDHapticSharpness
     53                                                                                                     value:sharpness],
     54                                                       [[CHHapticEventParameter alloc] initWithParameterID:CHHapticEventParameterIDHapticIntensity
     55                                                                                                     value:intensity]]
     56                                                        relativeTime:CHHapticTimeImmediate
     57                                                            duration:duration];
     58 }
     59 
     60 - (void)doTapHaptic
     61 {
     62     double intensity = [[NSUserDefaults standardUserDefaults] doubleForKey:@"GBHapticsStrength"];
     63     if (_rumble > intensity) return;
     64 
     65     dispatch_async(_queue, ^{
     66         CHHapticPattern *pattern = [[CHHapticPattern alloc] initWithEvents:@[[self eventWithType:CHHapticEventTypeHapticTransient
     67                                                                                        sharpness:0.25
     68                                                                                        intensity:intensity
     69                                                                                         duration:1.0]]
     70                                                                 parameters:nil
     71                                                                      error:nil];
     72         @try {
     73             id<CHHapticPatternPlayer> player = [_engine createPlayerWithPattern:pattern error:nil];
     74             [player startAtTime:0 error:nil];
     75         }
     76         @catch (NSException *exception) {}
     77     });
     78 }
     79 
     80 - (void)setRumbleStrength:(double)rumble
     81 {
     82     if (!_controller) { // Controller disconnected
     83         _externalEngine = nil;
     84     }
     85     if (!_externalEngine && _controller && !_controller.isAttachedToDevice) {
     86         /* We have a controller with no rumble support which is not attached to the device,
     87            don't rumble since the user is holding neither the device nor a haptic-enabled
     88            controller. */
     89         rumble = 0;
     90     }
     91     if (rumble == 0) {
     92         @try {
     93             /* Why must every method from this framework randomly throw exceptions whenever
     94                anything remotely unusual happens? CoreHaptic sucks.*/
     95             [_rumblePlayer stopAtTime:0 error:nil];
     96         }
     97         @catch (NSException *exception) {}
     98         _rumblePlayer = nil;
     99         _rumble = 0;
    100         return;
    101     }
    102     
    103     // No change
    104     if (rumble == _rumble) return;
    105     _rumble = rumble;
    106     
    107     dispatch_async(_queue, ^{
    108         CHHapticPattern *pattern = [[CHHapticPattern alloc] initWithEvents:@[[self eventWithType:CHHapticEventTypeHapticContinuous
    109                                                                                        sharpness:0.75
    110                                                                                        intensity:rumble
    111                                                                                         duration:1.0]]
    112                                                                 parameters:nil
    113                                                                      error:nil];
    114         @try {
    115             id<CHHapticPatternPlayer> newPlayer = [_externalEngine ?: _engine createPlayerWithPattern:pattern error:nil];
    116             [newPlayer startAtTime:0 error:nil];
    117             [_rumblePlayer stopAtTime:0 error:nil];
    118             _rumblePlayer = newPlayer;
    119         }
    120         @catch (NSException *exception) {
    121             if (_externalEngine) {
    122                 // Something might have happened with our controller? Delete and try again
    123                 _externalEngine = nil;
    124                 [self setRumbleStrength: rumble];
    125             }
    126         }
    127     });
    128 }
    129 
    130 - (void)setController:(GCController *)controller
    131 {
    132     if (_controller != controller) {
    133         if (@available(iOS 14.0, *)) {
    134             _externalEngine = [controller.haptics createEngineWithLocality:GCHapticsLocalityDefault];
    135             _externalEngine.playsHapticsOnly = true;
    136             _externalEngine.autoShutdownEnabled = true;
    137 
    138         }
    139         _controller = controller;
    140     }
    141 }
    142 
    143 - (GCController *)controller
    144 {
    145     return _controller;
    146 }
    147 
    148 @end

This webpage is intended to be an accessible preview of this repository. To get a fuller picture, clone it and use the git CLI.