Commit d9aea0c3a087ec24da2d2b2a1e2ea18000861d0d

Sam Lantinga 2020-10-15T10:13:42

Update the existing haptic player when we rumble on iOS

diff --git a/src/joystick/iphoneos/SDL_sysjoystick.m b/src/joystick/iphoneos/SDL_sysjoystick.m
index 920f5dd..0141802 100644
--- a/src/joystick/iphoneos/SDL_sysjoystick.m
+++ b/src/joystick/iphoneos/SDL_sysjoystick.m
@@ -827,6 +827,7 @@ IOS_MFIJoystickUpdate(SDL_Joystick * joystick)
 @implementation SDL_RumbleMotor {
     CHHapticEngine *engine API_AVAILABLE(ios(13.0), tvos(14.0));
     id<CHHapticPatternPlayer> player API_AVAILABLE(ios(13.0), tvos(14.0));
+	bool active;
 }
 
 -(void)cleanup
@@ -843,78 +844,92 @@ IOS_MFIJoystickUpdate(SDL_Joystick * joystick)
 
 -(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;
+    @autoreleasepool {
+		if (@available(iOS 14.0, tvOS 14.0, *)) {
+			NSError *error;
+			
+			if (self->engine == nil) {
+				return SDL_SetError("Haptics engine was stopped");
+			}
+
+			if (intensity == 0.0f) {
+				if (self->player && self->active) {
+					[self->player stopAtTime:0 error:&error];
+				}
+				self->active = false;
+				return 0;
+			}
+
+			if (self->player == nil) {
+				CHHapticEventParameter *param = [[CHHapticEventParameter alloc] initWithParameterID:CHHapticEventParameterIDHapticIntensity value:1.0f];
+				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->active = false;
+			}
+
+			CHHapticDynamicParameter *param = [[CHHapticDynamicParameter alloc] initWithParameterID:CHHapticDynamicParameterIDHapticIntensityControl value:intensity relativeTime:0];
+			[self->player sendParameters:[NSArray arrayWithObject:param] atTime:0 error:&error];
+			if (error != nil) {
+				return SDL_SetError("Couldn't update haptic player: %s", [error.localizedDescription UTF8String]);
+			}
+
+			if (!self->active) {
+				[self->player startAtTime:0 error:&error];
+				self->active = true;
+			}
+		}
+
+		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;
+    @autoreleasepool {
+		NSError *error;
+
+		self->engine = [controller.haptics createEngineWithLocality:locality];
+		if (self->engine == nil) {
+			SDL_SetError("Couldn't create haptics engine");
+			return nil;
+		}
+
+		[self->engine startAndReturnError:&error];
+		if (error != nil) {
+			SDL_SetError("Couldn't start haptics engine");
+			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
@@ -943,6 +958,12 @@ IOS_MFIJoystickUpdate(SDL_Joystick * joystick)
     return ((result < 0) ? -1 : 0);
 }
 
+-(void)cleanup
+{
+	[self->low_frequency_motor cleanup];
+	[self->high_frequency_motor cleanup];
+}
+
 @end
 
 static SDL_RumbleContext *IOS_JoystickInitRumble(GCController *controller)
@@ -1015,10 +1036,15 @@ IOS_JoystickClose(SDL_Joystick * joystick)
     device->joystick = NULL;
 
     @autoreleasepool {
+#ifdef ENABLE_MFI_RUMBLE
         if (device->rumble) {
+			SDL_RumbleContext *rumble = (__bridge SDL_RumbleContext *)device->rumble;
+
+			[rumble cleanup];
             CFRelease(device->rumble);
             device->rumble = NULL;
         }
+#endif /* ENABLE_MFI_RUMBLE */
 
         if (device->accelerometer) {
 #if !TARGET_OS_TV