Merge pull request #60398 from timoschwarzer/ios-haptic-engine-3.x
This commit is contained in:
commit
71c8c675bd
|
@ -401,7 +401,8 @@
|
||||||
<argument index="0" name="duration_ms" type="int" default="500" />
|
<argument index="0" name="duration_ms" type="int" default="500" />
|
||||||
<description>
|
<description>
|
||||||
Vibrate Android and iOS devices.
|
Vibrate Android and iOS devices.
|
||||||
[b]Note:[/b] It needs [code]VIBRATE[/code] permission for Android at export settings. iOS does not support duration.
|
[b]Note:[/b] For Android, it requires enabling the [code]VIBRATE[/code] permission in the export preset.
|
||||||
|
[b]Note:[/b] For iOS, specifying the duration is supported in iOS 13 and later.
|
||||||
</description>
|
</description>
|
||||||
</method>
|
</method>
|
||||||
<method name="warp_mouse_position">
|
<method name="warp_mouse_position">
|
||||||
|
|
|
@ -32,15 +32,26 @@
|
||||||
#define IOS_H
|
#define IOS_H
|
||||||
|
|
||||||
#include "core/object.h"
|
#include "core/object.h"
|
||||||
|
#import <CoreHaptics/CoreHaptics.h>
|
||||||
|
|
||||||
class iOS : public Object {
|
class iOS : public Object {
|
||||||
GDCLASS(iOS, Object);
|
GDCLASS(iOS, Object);
|
||||||
|
|
||||||
static void _bind_methods();
|
static void _bind_methods();
|
||||||
|
|
||||||
|
private:
|
||||||
|
CHHapticEngine *haptic_engine API_AVAILABLE(ios(13)) = NULL;
|
||||||
|
|
||||||
|
CHHapticEngine *get_haptic_engine_instance() API_AVAILABLE(ios(13));
|
||||||
|
void start_haptic_engine();
|
||||||
|
void stop_haptic_engine();
|
||||||
|
|
||||||
public:
|
public:
|
||||||
static void alert(const char *p_alert, const char *p_title);
|
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_model() const;
|
||||||
String get_rate_url(int p_app_id) const;
|
String get_rate_url(int p_app_id) const;
|
||||||
|
|
||||||
|
|
|
@ -33,13 +33,111 @@
|
||||||
#import "app_delegate.h"
|
#import "app_delegate.h"
|
||||||
#import "view_controller.h"
|
#import "view_controller.h"
|
||||||
|
|
||||||
|
#import <CoreHaptics/CoreHaptics.h>
|
||||||
#import <UIKit/UIKit.h>
|
#import <UIKit/UIKit.h>
|
||||||
#include <sys/sysctl.h>
|
#include <sys/sysctl.h>
|
||||||
|
|
||||||
void iOS::_bind_methods() {
|
void iOS::_bind_methods() {
|
||||||
ClassDB::bind_method(D_METHOD("get_rate_url", "app_id"), &iOS::get_rate_url);
|
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<CHHapticDeviceCapability> capabilities = [CHHapticEngine capabilitiesForHardware];
|
||||||
|
return capabilities.supportsHaptics;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
CHHapticEngine *iOS::get_haptic_engine_instance() API_AVAILABLE(ios(13)) {
|
||||||
|
if (haptic_engine == NULL) {
|
||||||
|
NSError *error = NULL;
|
||||||
|
haptic_engine = [[CHHapticEngine alloc] initAndReturnError:&error];
|
||||||
|
|
||||||
|
if (!error) {
|
||||||
|
[haptic_engine setAutoShutdownEnabled:true];
|
||||||
|
} else {
|
||||||
|
haptic_engine = NULL;
|
||||||
|
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) {
|
void iOS::alert(const char *p_alert, const char *p_title) {
|
||||||
NSString *title = [NSString stringWithUTF8String:p_title];
|
NSString *title = [NSString stringWithUTF8String:p_title];
|
||||||
NSString *message = [NSString stringWithUTF8String:p_alert];
|
NSString *message = [NSString stringWithUTF8String:p_alert];
|
||||||
|
|
|
@ -680,8 +680,12 @@ String OSIPhone::get_processor_name() const {
|
||||||
}
|
}
|
||||||
|
|
||||||
void OSIPhone::vibrate_handheld(int p_duration_ms) {
|
void OSIPhone::vibrate_handheld(int p_duration_ms) {
|
||||||
// iOS does not support duration for vibration
|
if (ios->supports_haptic_engine()) {
|
||||||
AudioServicesPlaySystemSound(kSystemSoundID_Vibrate);
|
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) {
|
bool OSIPhone::_check_internal_feature_support(const String &p_feature) {
|
||||||
|
|
Loading…
Reference in New Issue