diff --git a/platform/iphone/ios.h b/platform/iphone/ios.h index 03e663b05cc..0607d7b3950 100644 --- a/platform/iphone/ios.h +++ b/platform/iphone/ios.h @@ -32,15 +32,26 @@ #define IOS_H #include "core/object/class_db.h" +#import class iOS : public Object { GDCLASS(iOS, Object); static void _bind_methods(); +private: + CHHapticEngine *haptic_engine API_AVAILABLE(ios(13)) = nullptr; + + CHHapticEngine *get_haptic_engine_instance() API_AVAILABLE(ios(13)); + void start_haptic_engine(); + void stop_haptic_engine(); + public: static void alert(const char *p_alert, const char *p_title); + bool supports_haptic_engine(); + void vibrate_haptic_engine(float p_duration_seconds); + String get_model() const; String get_rate_url(int p_app_id) const; diff --git a/platform/iphone/ios.mm b/platform/iphone/ios.mm index ad1ea70c105..cca28cc055f 100644 --- a/platform/iphone/ios.mm +++ b/platform/iphone/ios.mm @@ -32,11 +32,110 @@ #import "app_delegate.h" #import "view_controller.h" + +#import #import #include void iOS::_bind_methods() { ClassDB::bind_method(D_METHOD("get_rate_url", "app_id"), &iOS::get_rate_url); + ClassDB::bind_method(D_METHOD("supports_haptic_engine"), &iOS::supports_haptic_engine); + ClassDB::bind_method(D_METHOD("start_haptic_engine"), &iOS::start_haptic_engine); + ClassDB::bind_method(D_METHOD("stop_haptic_engine"), &iOS::stop_haptic_engine); +}; + +bool iOS::supports_haptic_engine() { + if (@available(iOS 13, *)) { + id capabilities = [CHHapticEngine capabilitiesForHardware]; + return capabilities.supportsHaptics; + } + + return false; +} + +CHHapticEngine *iOS::get_haptic_engine_instance() API_AVAILABLE(ios(13)) { + if (haptic_engine == nullptr) { + NSError *error = nullptr; + haptic_engine = [[CHHapticEngine alloc] initAndReturnError:&error]; + + if (!error) { + [haptic_engine setAutoShutdownEnabled:true]; + } else { + haptic_engine = nullptr; + NSLog(@"Could not initialize haptic engine: %@", error); + } + } + + return haptic_engine; +} + +void iOS::vibrate_haptic_engine(float p_duration_seconds) API_AVAILABLE(ios(13)) { + if (@available(iOS 13, *)) { // We need the @available check every time to make the compiler happy... + if (supports_haptic_engine()) { + CHHapticEngine *haptic_engine = get_haptic_engine_instance(); + if (haptic_engine) { + NSDictionary *hapticDict = @{ + CHHapticPatternKeyPattern : @[ + @{CHHapticPatternKeyEvent : @{ + CHHapticPatternKeyEventType : CHHapticEventTypeHapticTransient, + CHHapticPatternKeyTime : @(CHHapticTimeImmediate), + CHHapticPatternKeyEventDuration : @(p_duration_seconds) + }, + }, + ], + }; + + NSError *error; + CHHapticPattern *pattern = [[CHHapticPattern alloc] initWithDictionary:hapticDict error:&error]; + + [[haptic_engine createPlayerWithPattern:pattern error:&error] startAtTime:0 error:&error]; + + NSLog(@"Could not vibrate using haptic engine: %@", error); + } + + return; + } + } + + NSLog(@"Haptic engine is not supported in this version of iOS"); +} + +void iOS::start_haptic_engine() { + if (@available(iOS 13, *)) { + if (supports_haptic_engine()) { + CHHapticEngine *haptic_engine = get_haptic_engine_instance(); + if (haptic_engine) { + [haptic_engine startWithCompletionHandler:^(NSError *returnedError) { + if (returnedError) { + NSLog(@"Could not start haptic engine: %@", returnedError); + } + }]; + } + + return; + } + } + + NSLog(@"Haptic engine is not supported in this version of iOS"); +} + +void iOS::stop_haptic_engine() { + if (@available(iOS 13, *)) { + if (supports_haptic_engine()) { + CHHapticEngine *haptic_engine = get_haptic_engine_instance(); + if (haptic_engine) { + [haptic_engine stopWithCompletionHandler:^(NSError *returnedError) { + if (returnedError) { + NSLog(@"Could not stop haptic engine: %@", returnedError); + } + }]; + } + + return; + } + } + + NSLog(@"Haptic engine is not supported in this version of iOS"); } void iOS::alert(const char *p_alert, const char *p_title) { diff --git a/platform/iphone/os_iphone.mm b/platform/iphone/os_iphone.mm index ea1bc0ef64e..f7974c4b3df 100644 --- a/platform/iphone/os_iphone.mm +++ b/platform/iphone/os_iphone.mm @@ -298,8 +298,12 @@ String OSIPhone::get_processor_name() const { } void OSIPhone::vibrate_handheld(int p_duration_ms) { - // iOS does not support duration for vibration - AudioServicesPlaySystemSound(kSystemSoundID_Vibrate); + if (ios->supports_haptic_engine()) { + ios->vibrate_haptic_engine((float)p_duration_ms / 1000.f); + } else { + // iOS <13 does not support duration for vibration + AudioServicesPlaySystemSound(kSystemSoundID_Vibrate); + } } bool OSIPhone::_check_internal_feature_support(const String &p_feature) {