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.