summaryrefslogtreecommitdiff
path: root/platform
diff options
context:
space:
mode:
Diffstat (limited to 'platform')
-rw-r--r--platform/iphone/SCsub2
-rw-r--r--platform/iphone/app_delegate.mm3
-rw-r--r--platform/iphone/detect.py4
-rw-r--r--platform/iphone/device_metrics.h37
-rw-r--r--platform/iphone/device_metrics.m152
-rw-r--r--platform/iphone/display_layer.mm3
-rw-r--r--platform/iphone/display_server_iphone.mm185
-rw-r--r--platform/iphone/game_center.h2
-rw-r--r--platform/iphone/game_center.mm45
-rw-r--r--platform/iphone/godot_iphone.mm4
-rw-r--r--platform/iphone/godot_view.mm18
-rw-r--r--platform/iphone/godot_view_gesture_recognizer.mm7
-rw-r--r--platform/iphone/icloud.mm14
-rw-r--r--platform/iphone/in_app_store.mm41
-rw-r--r--platform/iphone/ios.mm19
-rw-r--r--platform/iphone/joypad_iphone.mm101
-rw-r--r--platform/iphone/native_video_view.h41
-rw-r--r--platform/iphone/native_video_view.m260
-rw-r--r--platform/iphone/os_iphone.mm6
-rw-r--r--platform/iphone/platform_config.h7
-rw-r--r--platform/iphone/view_controller.h8
-rw-r--r--platform/iphone/view_controller.mm209
-rw-r--r--platform/iphone/vulkan_context_iphone.mm6
-rw-r--r--platform/javascript/SCsub1
-rw-r--r--platform/javascript/audio_driver_javascript.cpp248
-rw-r--r--platform/javascript/audio_driver_javascript.h19
-rw-r--r--platform/javascript/detect.py9
-rw-r--r--platform/javascript/emscripten_helpers.py23
-rw-r--r--platform/javascript/export/export.cpp3
-rw-r--r--platform/javascript/godot_audio.h58
-rw-r--r--platform/javascript/native/library_godot_audio.js173
-rw-r--r--platform/osx/display_server_osx.mm13
-rw-r--r--platform/uwp/detect.py3
-rw-r--r--platform/uwp/export/export.cpp2
-rw-r--r--platform/windows/detect.py19
35 files changed, 984 insertions, 761 deletions
diff --git a/platform/iphone/SCsub b/platform/iphone/SCsub
index 848fd9713a..1dd37dabe5 100644
--- a/platform/iphone/SCsub
+++ b/platform/iphone/SCsub
@@ -19,6 +19,8 @@ iphone_lib = [
"display_layer.mm",
"godot_view_renderer.mm",
"godot_view_gesture_recognizer.mm",
+ "device_metrics.m",
+ "native_video_view.m",
]
env_ios = env.Clone()
diff --git a/platform/iphone/app_delegate.mm b/platform/iphone/app_delegate.mm
index 7edbcc4667..40a63d7ad2 100644
--- a/platform/iphone/app_delegate.mm
+++ b/platform/iphone/app_delegate.mm
@@ -62,7 +62,7 @@ static ViewController *mainViewController = nil;
CGRect windowBounds = [[UIScreen mainScreen] bounds];
// Create a full-screen window
- self.window = [[[UIWindow alloc] initWithFrame:windowBounds] autorelease];
+ self.window = [[UIWindow alloc] initWithFrame:windowBounds];
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
@@ -140,7 +140,6 @@ static ViewController *mainViewController = nil;
- (void)dealloc {
self.window = nil;
- [super dealloc];
}
@end
diff --git a/platform/iphone/detect.py b/platform/iphone/detect.py
index 66579c1ad7..5ebabdd3dc 100644
--- a/platform/iphone/detect.py
+++ b/platform/iphone/detect.py
@@ -129,7 +129,7 @@ def configure(env):
detect_darwin_sdk_path("iphone", env)
env.Append(
CCFLAGS=(
- "-fno-objc-arc -arch armv7 -fmessage-length=0 -fno-strict-aliasing"
+ "-fobjc-arc -arch armv7 -fmessage-length=0 -fno-strict-aliasing"
" -fdiagnostics-print-source-range-info -fdiagnostics-show-category=id -fdiagnostics-parseable-fixits"
" -fpascal-strings -fblocks -isysroot $IPHONESDK -fvisibility=hidden -mthumb"
' "-DIBOutlet=__attribute__((iboutlet))"'
@@ -141,7 +141,7 @@ def configure(env):
detect_darwin_sdk_path("iphone", env)
env.Append(
CCFLAGS=(
- "-fno-objc-arc -arch arm64 -fmessage-length=0 -fno-strict-aliasing"
+ "-fobjc-arc -arch arm64 -fmessage-length=0 -fno-strict-aliasing"
" -fdiagnostics-print-source-range-info -fdiagnostics-show-category=id -fdiagnostics-parseable-fixits"
" -fpascal-strings -fblocks -fvisibility=hidden -MMD -MT dependencies -miphoneos-version-min=11.0"
" -isysroot $IPHONESDK".split()
diff --git a/platform/iphone/device_metrics.h b/platform/iphone/device_metrics.h
new file mode 100644
index 0000000000..6d0ff49077
--- /dev/null
+++ b/platform/iphone/device_metrics.h
@@ -0,0 +1,37 @@
+/*************************************************************************/
+/* device_metrics.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#import <Foundation/Foundation.h>
+
+@interface GodotDeviceMetrics : NSObject
+
+@property(nonatomic, class, readonly, strong) NSDictionary<NSArray *, NSNumber *> *dpiList;
+
+@end
diff --git a/platform/iphone/device_metrics.m b/platform/iphone/device_metrics.m
new file mode 100644
index 0000000000..747872bc49
--- /dev/null
+++ b/platform/iphone/device_metrics.m
@@ -0,0 +1,152 @@
+/*************************************************************************/
+/* device_metrics.m */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#import "device_metrics.h"
+
+@implementation GodotDeviceMetrics
+
++ (NSDictionary *)dpiList {
+ return @{
+ @[
+ @"iPad1,1",
+ @"iPad2,1",
+ @"iPad2,2",
+ @"iPad2,3",
+ @"iPad2,4",
+ ] : @132,
+ @[
+ @"iPhone1,1",
+ @"iPhone1,2",
+ @"iPhone2,1",
+ @"iPad2,5",
+ @"iPad2,6",
+ @"iPad2,7",
+ @"iPod1,1",
+ @"iPod2,1",
+ @"iPod3,1",
+ ] : @163,
+ @[
+ @"iPad3,1",
+ @"iPad3,2",
+ @"iPad3,3",
+ @"iPad3,4",
+ @"iPad3,5",
+ @"iPad3,6",
+ @"iPad4,1",
+ @"iPad4,2",
+ @"iPad4,3",
+ @"iPad5,3",
+ @"iPad5,4",
+ @"iPad6,3",
+ @"iPad6,4",
+ @"iPad6,7",
+ @"iPad6,8",
+ @"iPad6,11",
+ @"iPad6,12",
+ @"iPad7,1",
+ @"iPad7,2",
+ @"iPad7,3",
+ @"iPad7,4",
+ @"iPad7,5",
+ @"iPad7,6",
+ @"iPad7,11",
+ @"iPad7,12",
+ @"iPad8,1",
+ @"iPad8,2",
+ @"iPad8,3",
+ @"iPad8,4",
+ @"iPad8,5",
+ @"iPad8,6",
+ @"iPad8,7",
+ @"iPad8,8",
+ @"iPad8,9",
+ @"iPad8,10",
+ @"iPad8,11",
+ @"iPad8,12",
+ @"iPad11,3",
+ @"iPad11,4",
+ ] : @264,
+ @[
+ @"iPhone3,1",
+ @"iPhone3,2",
+ @"iPhone3,3",
+ @"iPhone4,1",
+ @"iPhone5,1",
+ @"iPhone5,2",
+ @"iPhone5,3",
+ @"iPhone5,4",
+ @"iPhone6,1",
+ @"iPhone6,2",
+ @"iPhone7,2",
+ @"iPhone8,1",
+ @"iPhone8,4",
+ @"iPhone9,1",
+ @"iPhone9,3",
+ @"iPhone10,1",
+ @"iPhone10,4",
+ @"iPhone11,8",
+ @"iPhone12,1",
+ @"iPhone12,8",
+ @"iPad4,4",
+ @"iPad4,5",
+ @"iPad4,6",
+ @"iPad4,7",
+ @"iPad4,8",
+ @"iPad4,9",
+ @"iPad5,1",
+ @"iPad5,2",
+ @"iPad11,1",
+ @"iPad11,2",
+ @"iPod4,1",
+ @"iPod5,1",
+ @"iPod7,1",
+ @"iPod9,1",
+ ] : @326,
+ @[
+ @"iPhone7,1",
+ @"iPhone8,2",
+ @"iPhone9,2",
+ @"iPhone9,4",
+ @"iPhone10,2",
+ @"iPhone10,5",
+ ] : @401,
+ @[
+ @"iPhone10,3",
+ @"iPhone10,6",
+ @"iPhone11,2",
+ @"iPhone11,4",
+ @"iPhone11,6",
+ @"iPhone12,3",
+ @"iPhone12,5",
+ ] : @458,
+ };
+}
+
+@end
diff --git a/platform/iphone/display_layer.mm b/platform/iphone/display_layer.mm
index 5ec94fb471..2716538b89 100644
--- a/platform/iphone/display_layer.mm
+++ b/platform/iphone/display_layer.mm
@@ -124,11 +124,8 @@
}
if (context) {
- [context release];
context = nil;
}
-
- [super dealloc];
}
- (BOOL)createFramebuffer {
diff --git a/platform/iphone/display_server_iphone.mm b/platform/iphone/display_server_iphone.mm
index eea87cecc9..d456f9cbf6 100644
--- a/platform/iphone/display_server_iphone.mm
+++ b/platform/iphone/display_server_iphone.mm
@@ -32,8 +32,10 @@
#import "app_delegate.h"
#include "core/io/file_access_pack.h"
#include "core/project_settings.h"
+#import "device_metrics.h"
#import "godot_view.h"
#include "ios.h"
+#import "native_video_view.h"
#include "os_iphone.h"
#import "view_controller.h"
@@ -41,120 +43,6 @@
#import <sys/utsname.h>
static const float kDisplayServerIPhoneAcceleration = 1;
-static NSDictionary *iOSModelToDPI = @{
- @[
- @"iPad1,1",
- @"iPad2,1",
- @"iPad2,2",
- @"iPad2,3",
- @"iPad2,4",
- ] : @132,
- @[
- @"iPhone1,1",
- @"iPhone1,2",
- @"iPhone2,1",
- @"iPad2,5",
- @"iPad2,6",
- @"iPad2,7",
- @"iPod1,1",
- @"iPod2,1",
- @"iPod3,1",
- ] : @163,
- @[
- @"iPad3,1",
- @"iPad3,2",
- @"iPad3,3",
- @"iPad3,4",
- @"iPad3,5",
- @"iPad3,6",
- @"iPad4,1",
- @"iPad4,2",
- @"iPad4,3",
- @"iPad5,3",
- @"iPad5,4",
- @"iPad6,3",
- @"iPad6,4",
- @"iPad6,7",
- @"iPad6,8",
- @"iPad6,11",
- @"iPad6,12",
- @"iPad7,1",
- @"iPad7,2",
- @"iPad7,3",
- @"iPad7,4",
- @"iPad7,5",
- @"iPad7,6",
- @"iPad7,11",
- @"iPad7,12",
- @"iPad8,1",
- @"iPad8,2",
- @"iPad8,3",
- @"iPad8,4",
- @"iPad8,5",
- @"iPad8,6",
- @"iPad8,7",
- @"iPad8,8",
- @"iPad8,9",
- @"iPad8,10",
- @"iPad8,11",
- @"iPad8,12",
- @"iPad11,3",
- @"iPad11,4",
- ] : @264,
- @[
- @"iPhone3,1",
- @"iPhone3,2",
- @"iPhone3,3",
- @"iPhone4,1",
- @"iPhone5,1",
- @"iPhone5,2",
- @"iPhone5,3",
- @"iPhone5,4",
- @"iPhone6,1",
- @"iPhone6,2",
- @"iPhone7,2",
- @"iPhone8,1",
- @"iPhone8,4",
- @"iPhone9,1",
- @"iPhone9,3",
- @"iPhone10,1",
- @"iPhone10,4",
- @"iPhone11,8",
- @"iPhone12,1",
- @"iPhone12,8",
- @"iPad4,4",
- @"iPad4,5",
- @"iPad4,6",
- @"iPad4,7",
- @"iPad4,8",
- @"iPad4,9",
- @"iPad5,1",
- @"iPad5,2",
- @"iPad11,1",
- @"iPad11,2",
- @"iPod4,1",
- @"iPod5,1",
- @"iPod7,1",
- @"iPod9,1",
- ] : @326,
- @[
- @"iPhone7,1",
- @"iPhone8,2",
- @"iPhone9,2",
- @"iPhone9,4",
- @"iPhone10,2",
- @"iPhone10,5",
- ] : @401,
- @[
- @"iPhone10,3",
- @"iPhone10,6",
- @"iPhone11,2",
- @"iPhone11,4",
- @"iPhone11,6",
- @"iPhone12,3",
- @"iPhone12,5",
- ] : @458,
-};
DisplayServerIPhone *DisplayServerIPhone::get_singleton() {
return (DisplayServerIPhone *)DisplayServer::get_singleton();
@@ -383,8 +271,7 @@ void DisplayServerIPhone::update_gravity(float p_x, float p_y, float p_z) {
Input::get_singleton()->set_gravity(Vector3(p_x, p_y, p_z));
};
-void DisplayServerIPhone::update_accelerometer(float p_x, float p_y,
- float p_z) {
+void DisplayServerIPhone::update_accelerometer(float p_x, float p_y, float p_z) {
// Found out the Z should not be negated! Pass as is!
Vector3 v_accelerometer = Vector3(
p_x / kDisplayServerIPhoneAcceleration,
@@ -392,39 +279,6 @@ void DisplayServerIPhone::update_accelerometer(float p_x, float p_y,
p_z / kDisplayServerIPhoneAcceleration);
Input::get_singleton()->set_accelerometer(v_accelerometer);
-
- /*
- if (p_x != last_accel.x) {
- //printf("updating accel x %f\n", p_x);
- InputEvent ev;
- ev.type = InputEvent::JOYPAD_MOTION;
- ev.device = 0;
- ev.joy_motion.axis = JOY_ANALOG_0;
- ev.joy_motion.axis_value = (p_x / (float)ACCEL_RANGE);
- last_accel.x = p_x;
- queue_event(ev);
- };
- if (p_y != last_accel.y) {
- //printf("updating accel y %f\n", p_y);
- InputEvent ev;
- ev.type = InputEvent::JOYPAD_MOTION;
- ev.device = 0;
- ev.joy_motion.axis = JOY_ANALOG_1;
- ev.joy_motion.axis_value = (p_y / (float)ACCEL_RANGE);
- last_accel.y = p_y;
- queue_event(ev);
- };
- if (p_z != last_accel.z) {
- //printf("updating accel z %f\n", p_z);
- InputEvent ev;
- ev.type = InputEvent::JOYPAD_MOTION;
- ev.device = 0;
- ev.joy_motion.axis = JOY_ANALOG_2;
- ev.joy_motion.axis_value = ( (1.0 - p_z) / (float)ACCEL_RANGE);
- last_accel.z = p_z;
- queue_event(ev);
- };
- */
};
void DisplayServerIPhone::update_magnetometer(float p_x, float p_y, float p_z) {
@@ -516,6 +370,8 @@ int DisplayServerIPhone::screen_get_dpi(int p_screen) const {
NSString *string = [NSString stringWithCString:systemInfo.machine encoding:NSUTF8StringEncoding];
+ NSDictionary *iOSModelToDPI = [GodotDeviceMetrics dpiList];
+
for (NSArray *keyArray in iOSModelToDPI) {
if ([keyArray containsObject:string]) {
NSNumber *value = iOSModelToDPI[keyArray];
@@ -523,7 +379,26 @@ int DisplayServerIPhone::screen_get_dpi(int p_screen) const {
}
}
- return 163;
+ // If device wasn't found in dictionary
+ // make a best guess from device metrics.
+ CGFloat scale = [UIScreen mainScreen].scale;
+
+ UIUserInterfaceIdiom idiom = [UIDevice currentDevice].userInterfaceIdiom;
+
+ switch (idiom) {
+ case UIUserInterfaceIdiomPad:
+ return scale == 2 ? 264 : 132;
+ case UIUserInterfaceIdiomPhone: {
+ if (scale == 3) {
+ CGFloat nativeScale = [UIScreen mainScreen].nativeScale;
+ return nativeScale == 3 ? 458 : 401;
+ }
+
+ return 326;
+ }
+ default:
+ return 72;
+ }
}
float DisplayServerIPhone::screen_get_scale(int p_screen) const {
@@ -716,7 +591,7 @@ Error DisplayServerIPhone::native_video_play(String p_path, float p_volume, Stri
String file_path = ProjectSettings::get_singleton()->globalize_path(p_path);
- NSString *filePath = [[[NSString alloc] initWithUTF8String:file_path.utf8().get_data()] autorelease];
+ NSString *filePath = [[NSString alloc] initWithUTF8String:file_path.utf8().get_data()];
NSString *audioTrack = [NSString stringWithUTF8String:p_audio_track.utf8()];
NSString *subtitleTrack = [NSString stringWithUTF8String:p_subtitle_track.utf8()];
@@ -731,22 +606,22 @@ Error DisplayServerIPhone::native_video_play(String p_path, float p_volume, Stri
}
bool DisplayServerIPhone::native_video_is_playing() const {
- return [AppDelegate.viewController isVideoPlaying];
+ return [AppDelegate.viewController.videoView isVideoPlaying];
}
void DisplayServerIPhone::native_video_pause() {
if (native_video_is_playing()) {
- [AppDelegate.viewController pauseVideo];
+ [AppDelegate.viewController.videoView pauseVideo];
}
}
void DisplayServerIPhone::native_video_unpause() {
- [AppDelegate.viewController unpauseVideo];
+ [AppDelegate.viewController.videoView unpauseVideo];
};
void DisplayServerIPhone::native_video_stop() {
if (native_video_is_playing()) {
- [AppDelegate.viewController stopVideo];
+ [AppDelegate.viewController.videoView stopVideo];
}
}
diff --git a/platform/iphone/game_center.h b/platform/iphone/game_center.h
index 1e9a68fe48..6705674ac6 100644
--- a/platform/iphone/game_center.h
+++ b/platform/iphone/game_center.h
@@ -48,7 +48,7 @@ class GameCenter : public Object {
void return_connect_error(const char *p_error_description);
public:
- void connect();
+ Error authenticate();
bool is_authenticated();
Error post_score(Dictionary p_score);
diff --git a/platform/iphone/game_center.mm b/platform/iphone/game_center.mm
index 4481775c32..0f8c0100c3 100644
--- a/platform/iphone/game_center.mm
+++ b/platform/iphone/game_center.mm
@@ -52,6 +52,7 @@ extern "C" {
GameCenter *GameCenter::instance = NULL;
void GameCenter::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("authenticate"), &GameCenter::authenticate);
ClassDB::bind_method(D_METHOD("is_authenticated"), &GameCenter::is_authenticated);
ClassDB::bind_method(D_METHOD("post_score"), &GameCenter::post_score);
@@ -66,40 +67,28 @@ void GameCenter::_bind_methods() {
ClassDB::bind_method(D_METHOD("pop_pending_event"), &GameCenter::pop_pending_event);
};
-void GameCenter::return_connect_error(const char *p_error_description) {
- authenticated = false;
- Dictionary ret;
- ret["type"] = "authentication";
- ret["result"] = "error";
- ret["error_code"] = 0;
- ret["error_description"] = p_error_description;
- pending_events.push_back(ret);
-}
-
-void GameCenter::connect() {
+Error GameCenter::authenticate() {
//if this class isn't available, game center isn't implemented
if ((NSClassFromString(@"GKLocalPlayer")) == nil) {
- return_connect_error("GameCenter not available");
- return;
+ return ERR_UNAVAILABLE;
}
GKLocalPlayer *player = [GKLocalPlayer localPlayer];
- if (![player respondsToSelector:@selector(authenticateHandler)]) {
- return_connect_error("GameCenter doesn't respond to 'authenticateHandler'");
- return;
- }
+ ERR_FAIL_COND_V(![player respondsToSelector:@selector(authenticateHandler)], ERR_UNAVAILABLE);
ViewController *root_controller = (ViewController *)((AppDelegate *)[[UIApplication sharedApplication] delegate]).window.rootViewController;
- if (!root_controller) {
- return_connect_error("Window doesn't have root ViewController");
- return;
- }
+ ERR_FAIL_COND_V(!root_controller, FAILED);
// This handler is called several times. First when the view needs to be shown, then again
// after the view is cancelled or the user logs in. Or if the user's already logged in, it's
// called just once to confirm they're authenticated. This is why no result needs to be specified
// in the presentViewController phase. In this case, more calls to this function will follow.
+ _weakify(root_controller);
+ _weakify(player);
player.authenticateHandler = (^(UIViewController *controller, NSError *error) {
+ _strongify(root_controller);
+ _strongify(player);
+
if (controller) {
[root_controller presentViewController:controller animated:YES completion:nil];
} else {
@@ -126,6 +115,8 @@ void GameCenter::connect() {
pending_events.push_back(ret);
};
});
+
+ return OK;
};
bool GameCenter::is_authenticated() {
@@ -137,8 +128,8 @@ Error GameCenter::post_score(Dictionary p_score) {
float score = p_score["score"];
String category = p_score["category"];
- NSString *cat_str = [[[NSString alloc] initWithUTF8String:category.utf8().get_data()] autorelease];
- GKScore *reporter = [[[GKScore alloc] initWithLeaderboardIdentifier:cat_str] autorelease];
+ NSString *cat_str = [[NSString alloc] initWithUTF8String:category.utf8().get_data()];
+ GKScore *reporter = [[GKScore alloc] initWithLeaderboardIdentifier:cat_str];
reporter.value = score;
ERR_FAIL_COND_V([GKScore respondsToSelector:@selector(reportScores)], ERR_UNAVAILABLE);
@@ -166,8 +157,8 @@ Error GameCenter::award_achievement(Dictionary p_params) {
String name = p_params["name"];
float progress = p_params["progress"];
- NSString *name_str = [[[NSString alloc] initWithUTF8String:name.utf8().get_data()] autorelease];
- GKAchievement *achievement = [[[GKAchievement alloc] initWithIdentifier:name_str] autorelease];
+ NSString *name_str = [[NSString alloc] initWithUTF8String:name.utf8().get_data()];
+ GKAchievement *achievement = [[GKAchievement alloc] initWithIdentifier:name_str];
ERR_FAIL_COND_V(!achievement, FAILED);
ERR_FAIL_COND_V([GKAchievement respondsToSelector:@selector(reportAchievements)], ERR_UNAVAILABLE);
@@ -311,7 +302,7 @@ Error GameCenter::show_game_center(Dictionary p_params) {
}
}
- GKGameCenterViewController *controller = [[[GKGameCenterViewController alloc] init] autorelease];
+ GKGameCenterViewController *controller = [[GKGameCenterViewController alloc] init];
ERR_FAIL_COND_V(!controller, FAILED);
ViewController *root_controller = (ViewController *)((AppDelegate *)[[UIApplication sharedApplication] delegate]).window.rootViewController;
@@ -323,7 +314,7 @@ Error GameCenter::show_game_center(Dictionary p_params) {
controller.leaderboardIdentifier = nil;
if (p_params.has("leaderboard_name")) {
String name = p_params["leaderboard_name"];
- NSString *name_str = [[[NSString alloc] initWithUTF8String:name.utf8().get_data()] autorelease];
+ NSString *name_str = [[NSString alloc] initWithUTF8String:name.utf8().get_data()];
controller.leaderboardIdentifier = name_str;
}
}
diff --git a/platform/iphone/godot_iphone.mm b/platform/iphone/godot_iphone.mm
index 090b772947..6d95276f37 100644
--- a/platform/iphone/godot_iphone.mm
+++ b/platform/iphone/godot_iphone.mm
@@ -49,10 +49,8 @@ int add_path(int p_argc, char **p_args) {
}
p_args[p_argc++] = (char *)"--path";
- [str retain]; // memory leak lol (maybe make it static here and delete it in ViewController destructor? @todo
p_args[p_argc++] = (char *)[str cStringUsingEncoding:NSUTF8StringEncoding];
p_args[p_argc] = NULL;
- [str release];
return p_argc;
};
@@ -68,9 +66,7 @@ int add_cmdline(int p_argc, char **p_args) {
if (!str) {
continue;
}
- [str retain]; // @todo delete these at some point
p_args[p_argc++] = (char *)[str cStringUsingEncoding:NSUTF8StringEncoding];
- [str release];
};
p_args[p_argc] = NULL;
diff --git a/platform/iphone/godot_view.mm b/platform/iphone/godot_view.mm
index c0a31549c4..3b4344c46d 100644
--- a/platform/iphone/godot_view.mm
+++ b/platform/iphone/godot_view.mm
@@ -145,8 +145,6 @@ static const int max_touches = 8;
if (self.delayGestureRecognizer) {
self.delayGestureRecognizer = nil;
}
-
- [super dealloc];
}
- (void)godot_commonInit {
@@ -156,7 +154,7 @@ static const int max_touches = 8;
// Configure and start accelerometer
if (!self.motionManager) {
- self.motionManager = [[[CMMotionManager alloc] init] autorelease];
+ self.motionManager = [[CMMotionManager alloc] init];
if (self.motionManager.deviceMotionAvailable) {
self.motionManager.deviceMotionUpdateInterval = 1.0 / 70.0;
[self.motionManager startDeviceMotionUpdatesUsingReferenceFrame:CMAttitudeReferenceFrameXMagneticNorthZVertical];
@@ -169,7 +167,6 @@ static const int max_touches = 8;
GodotViewGestureRecognizer *gestureRecognizer = [[GodotViewGestureRecognizer alloc] init];
self.delayGestureRecognizer = gestureRecognizer;
[self addGestureRecognizer:self.delayGestureRecognizer];
- [gestureRecognizer release];
}
- (void)stopRendering {
@@ -204,14 +201,11 @@ static const int max_touches = 8;
if (self.useCADisplayLink) {
self.displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(drawView)];
- // if (@available(iOS 10, *)) {
- self.displayLink.preferredFramesPerSecond = (NSInteger)(1.0 / self.renderingInterval);
- // } else {
- // // Approximate frame rate
- // // assumes device refreshes at 60 fps
- // int frameInterval = (int)floor(self.renderingInterval * 60.0f);
- // [self.displayLink setFrameInterval:frameInterval];
- // }
+ // Approximate frame rate
+ // assumes device refreshes at 60 fps
+ int displayFPS = (NSInteger)(1.0 / self.renderingInterval);
+
+ self.displayLink.preferredFramesPerSecond = displayFPS;
// Setup DisplayLink in main thread
[self.displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];
diff --git a/platform/iphone/godot_view_gesture_recognizer.mm b/platform/iphone/godot_view_gesture_recognizer.mm
index 99ee42ecb3..71367a629c 100644
--- a/platform/iphone/godot_view_gesture_recognizer.mm
+++ b/platform/iphone/godot_view_gesture_recognizer.mm
@@ -83,8 +83,6 @@ const CGFloat kGLGestureMovementDistance = 0.5;
if (self.delayedEvent) {
self.delayedEvent = nil;
}
-
- [super dealloc];
}
- (void)delayTouches:(NSSet *)touches andEvent:(UIEvent *)event {
@@ -116,7 +114,6 @@ const CGFloat kGLGestureMovementDistance = 0.5;
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
NSSet *cleared = [self copyClearedTouches:touches phase:UITouchPhaseBegan];
[self delayTouches:cleared andEvent:event];
- [cleared release];
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
@@ -137,17 +134,14 @@ const CGFloat kGLGestureMovementDistance = 0.5;
if (distance > kGLGestureMovementDistance) {
[self.delayTimer fire];
[self.view touchesMoved:cleared withEvent:event];
- [cleared release];
return;
}
}
- [cleared release];
return;
}
[self.view touchesMoved:cleared withEvent:event];
- [cleared release];
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
@@ -155,7 +149,6 @@ const CGFloat kGLGestureMovementDistance = 0.5;
NSSet *cleared = [self copyClearedTouches:touches phase:UITouchPhaseEnded];
[self.view touchesEnded:cleared withEvent:event];
- [cleared release];
}
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event {
diff --git a/platform/iphone/icloud.mm b/platform/iphone/icloud.mm
index d3086e6cea..3d81349883 100644
--- a/platform/iphone/icloud.mm
+++ b/platform/iphone/icloud.mm
@@ -150,7 +150,7 @@ Variant nsobject_to_variant(NSObject *object) {
NSObject *variant_to_nsobject(Variant v) {
if (v.get_type() == Variant::STRING) {
- return [[[NSString alloc] initWithUTF8String:((String)v).utf8().get_data()] autorelease];
+ return [[NSString alloc] initWithUTF8String:((String)v).utf8().get_data()];
} else if (v.get_type() == Variant::FLOAT) {
return [NSNumber numberWithDouble:(double)v];
} else if (v.get_type() == Variant::INT) {
@@ -158,11 +158,11 @@ NSObject *variant_to_nsobject(Variant v) {
} else if (v.get_type() == Variant::BOOL) {
return [NSNumber numberWithBool:BOOL((bool)v)];
} else if (v.get_type() == Variant::DICTIONARY) {
- NSMutableDictionary *result = [[[NSMutableDictionary alloc] init] autorelease];
+ NSMutableDictionary *result = [[NSMutableDictionary alloc] init];
Dictionary dic = v;
Array keys = dic.keys();
for (int i = 0; i < keys.size(); ++i) {
- NSString *key = [[[NSString alloc] initWithUTF8String:((String)(keys[i])).utf8().get_data()] autorelease];
+ NSString *key = [[NSString alloc] initWithUTF8String:((String)(keys[i])).utf8().get_data()];
NSObject *value = variant_to_nsobject(dic[keys[i]]);
if (key == NULL || value == NULL) {
@@ -173,7 +173,7 @@ NSObject *variant_to_nsobject(Variant v) {
}
return result;
} else if (v.get_type() == Variant::ARRAY) {
- NSMutableArray *result = [[[NSMutableArray alloc] init] autorelease];
+ NSMutableArray *result = [[NSMutableArray alloc] init];
Array arr = v;
for (int i = 0; i < arr.size(); ++i) {
NSObject *value = variant_to_nsobject(arr[i]);
@@ -195,7 +195,7 @@ NSObject *variant_to_nsobject(Variant v) {
}
Error ICloud::remove_key(String p_param) {
- NSString *key = [[[NSString alloc] initWithUTF8String:p_param.utf8().get_data()] autorelease];
+ NSString *key = [[NSString alloc] initWithUTF8String:p_param.utf8().get_data()];
NSUbiquitousKeyValueStore *store = [NSUbiquitousKeyValueStore defaultStore];
@@ -217,7 +217,7 @@ Array ICloud::set_key_values(Dictionary p_params) {
String variant_key = keys[i];
Variant variant_value = p_params[variant_key];
- NSString *key = [[[NSString alloc] initWithUTF8String:variant_key.utf8().get_data()] autorelease];
+ NSString *key = [[NSString alloc] initWithUTF8String:variant_key.utf8().get_data()];
if (key == NULL) {
error_keys.push_back(variant_key);
continue;
@@ -238,7 +238,7 @@ Array ICloud::set_key_values(Dictionary p_params) {
}
Variant ICloud::get_key_value(String p_param) {
- NSString *key = [[[NSString alloc] initWithUTF8String:p_param.utf8().get_data()] autorelease];
+ NSString *key = [[NSString alloc] initWithUTF8String:p_param.utf8().get_data()];
NSUbiquitousKeyValueStore *store = [NSUbiquitousKeyValueStore defaultStore];
if (![[store dictionaryRepresentation] objectForKey:key]) {
diff --git a/platform/iphone/in_app_store.mm b/platform/iphone/in_app_store.mm
index 1477f92200..2b973dfbcf 100644
--- a/platform/iphone/in_app_store.mm
+++ b/platform/iphone/in_app_store.mm
@@ -56,7 +56,6 @@ static NSArray *latestProducts;
[numberFormatter setNumberStyle:NSNumberFormatterCurrencyStyle];
[numberFormatter setLocale:self.priceLocale];
NSString *formattedString = [numberFormatter stringFromNumber:self.price];
- [numberFormatter release];
return formattedString;
}
@@ -124,8 +123,6 @@ void InAppStore::_bind_methods() {
ret["invalid_ids"] = invalid_ids;
InAppStore::get_singleton()->_post_event(ret);
-
- [request release];
};
@end
@@ -136,14 +133,14 @@ Error InAppStore::request_product_info(Dictionary p_params) {
PackedStringArray pids = p_params["product_ids"];
printf("************ request product info! %i\n", pids.size());
- NSMutableArray *array = [[[NSMutableArray alloc] initWithCapacity:pids.size()] autorelease];
+ NSMutableArray *array = [[NSMutableArray alloc] initWithCapacity:pids.size()];
for (int i = 0; i < pids.size(); i++) {
printf("******** adding %s to product list\n", pids[i].utf8().get_data());
- NSString *pid = [[[NSString alloc] initWithUTF8String:pids[i].utf8().get_data()] autorelease];
+ NSString *pid = [[NSString alloc] initWithUTF8String:pids[i].utf8().get_data()];
[array addObject:pid];
};
- NSSet *products = [[[NSSet alloc] initWithArray:array] autorelease];
+ NSSet *products = [[NSSet alloc] initWithArray:array];
SKProductsRequest *request = [[SKProductsRequest alloc] initWithProductIdentifiers:products];
ProductsDelegate *delegate = [[ProductsDelegate alloc] init];
@@ -183,30 +180,14 @@ Error InAppStore::restore_purchases() {
ret["transaction_id"] = transactionId;
NSData *receipt = nil;
- int sdk_version = 6;
-
- if ([[[UIDevice currentDevice] systemVersion] floatValue] >= 7.0) {
- NSURL *receiptFileURL = nil;
- NSBundle *bundle = [NSBundle mainBundle];
- if ([bundle respondsToSelector:@selector(appStoreReceiptURL)]) {
- // Get the transaction receipt file path location in the app bundle.
- receiptFileURL = [bundle appStoreReceiptURL];
-
- // Read in the contents of the transaction file.
- receipt = [NSData dataWithContentsOfURL:receiptFileURL];
- sdk_version = 7;
+ int sdk_version = [[[UIDevice currentDevice] systemVersion] intValue];
- } else {
- // Fall back to deprecated transaction receipt,
- // which is still available in iOS 7.
+ NSBundle *bundle = [NSBundle mainBundle];
+ // Get the transaction receipt file path location in the app bundle.
+ NSURL *receiptFileURL = [bundle appStoreReceiptURL];
- // Use SKPaymentTransaction's transactionReceipt.
- receipt = [NSData dataWithContentsOfURL:[[NSBundle mainBundle] appStoreReceiptURL]];
- }
-
- } else {
- receipt = [NSData dataWithContentsOfURL:[[NSBundle mainBundle] appStoreReceiptURL]];
- }
+ // Read in the contents of the transaction file.
+ receipt = [NSData dataWithContentsOfURL:receiptFileURL];
NSString *receipt_to_send = nil;
if (receipt != nil) {
@@ -265,7 +246,7 @@ Error InAppStore::purchase(Dictionary p_params) {
printf("purchasing!\n");
ERR_FAIL_COND_V(!p_params.has("product_id"), ERR_INVALID_PARAMETER);
- NSString *pid = [[[NSString alloc] initWithUTF8String:String(p_params["product_id"]).utf8().get_data()] autorelease];
+ NSString *pid = [[NSString alloc] initWithUTF8String:String(p_params["product_id"]).utf8().get_data()];
SKProduct *product = nil;
@@ -307,7 +288,7 @@ void InAppStore::_post_event(Variant p_event) {
void InAppStore::_record_purchase(String product_id) {
String skey = "purchased/" + product_id;
- NSString *key = [[[NSString alloc] initWithUTF8String:skey.utf8().get_data()] autorelease];
+ NSString *key = [[NSString alloc] initWithUTF8String:skey.utf8().get_data()];
[[NSUserDefaults standardUserDefaults] setBool:YES forKey:key];
[[NSUserDefaults standardUserDefaults] synchronize];
};
diff --git a/platform/iphone/ios.mm b/platform/iphone/ios.mm
index 6d7699c0c9..d4e099063f 100644
--- a/platform/iphone/ios.mm
+++ b/platform/iphone/ios.mm
@@ -30,6 +30,7 @@
#include "ios.h"
#import "app_delegate.h"
+#import "view_controller.h"
#import <UIKit/UIKit.h>
#include <sys/sysctl.h>
@@ -68,23 +69,9 @@ String iOS::get_model() const {
}
String iOS::get_rate_url(int p_app_id) const {
- String templ = "itms-apps://ax.itunes.apple.com/WebObjects/MZStore.woa/wa/viewContentsUserReviews?type=Purple+Software&id=APP_ID";
- String templ_iOS7 = "itms-apps://itunes.apple.com/app/idAPP_ID";
- String templ_iOS8 = "itms-apps://itunes.apple.com/WebObjects/MZStore.woa/wa/viewContentsUserReviews?id=APP_ID&onlyLatestVersion=true&pageNumber=0&sortOrdering=1&type=Purple+Software";
+ String app_url_path = "itms-apps://itunes.apple.com/app/idAPP_ID";
- //ios7 before
- String ret = templ;
-
- if ([[[UIDevice currentDevice] systemVersion] floatValue] >= 7.0 && [[[UIDevice currentDevice] systemVersion] floatValue] < 7.1) {
- // iOS 7 needs a different templateReviewURL @see https://github.com/arashpayan/appirater/issues/131
- ret = templ_iOS7;
- } else if ([[[UIDevice currentDevice] systemVersion] floatValue] >= 8.0) {
- // iOS 8 needs a different templateReviewURL also @see https://github.com/arashpayan/appirater/issues/182
- ret = templ_iOS8;
- }
-
- // ios7 for everything?
- ret = templ_iOS7.replace("APP_ID", String::num(p_app_id));
+ String ret = app_url_path.replace("APP_ID", String::num(p_app_id));
printf("returning rate url %s\n", ret.utf8().get_data());
return ret;
diff --git a/platform/iphone/joypad_iphone.mm b/platform/iphone/joypad_iphone.mm
index 6088f1c25c..b0a8076b56 100644
--- a/platform/iphone/joypad_iphone.mm
+++ b/platform/iphone/joypad_iphone.mm
@@ -131,8 +131,6 @@ void JoypadIPhone::start_processing() {
- (void)dealloc {
[self finishObserving];
-
- [super dealloc];
}
- (int)getJoyIdForController:(GCController *)controller {
@@ -251,8 +249,13 @@ void JoypadIPhone::start_processing() {
// The extended gamepad profile has all the input you could possibly find on
// a gamepad but will only be active if your gamepad actually has all of
// these...
- controller.extendedGamepad.valueChangedHandler = ^(
- GCExtendedGamepad *gamepad, GCControllerElement *element) {
+ _weakify(self);
+ _weakify(controller);
+
+ controller.extendedGamepad.valueChangedHandler = ^(GCExtendedGamepad *gamepad, GCControllerElement *element) {
+ _strongify(self);
+ _strongify(controller);
+
int joy_id = [self getJoyIdForController:controller];
if (element == gamepad.buttonA) {
@@ -304,71 +307,33 @@ void JoypadIPhone::start_processing() {
Input::get_singleton()->joy_axis(joy_id, JOY_AXIS_TRIGGER_RIGHT, jx);
};
};
+ } else if (controller.microGamepad != nil) {
+ // micro gamepads were added in OS 9 and feature just 2 buttons and a d-pad
+ _weakify(self);
+ _weakify(controller);
+
+ controller.microGamepad.valueChangedHandler = ^(GCMicroGamepad *gamepad, GCControllerElement *element) {
+ _strongify(self);
+ _strongify(controller);
+
+ int joy_id = [self getJoyIdForController:controller];
+
+ if (element == gamepad.buttonA) {
+ Input::get_singleton()->joy_button(joy_id, JOY_BUTTON_A,
+ gamepad.buttonA.isPressed);
+ } else if (element == gamepad.buttonX) {
+ Input::get_singleton()->joy_button(joy_id, JOY_BUTTON_X,
+ gamepad.buttonX.isPressed);
+ } else if (element == gamepad.dpad) {
+ Input::get_singleton()->joy_button(joy_id, JOY_BUTTON_DPAD_UP,
+ gamepad.dpad.up.isPressed);
+ Input::get_singleton()->joy_button(joy_id, JOY_BUTTON_DPAD_DOWN,
+ gamepad.dpad.down.isPressed);
+ Input::get_singleton()->joy_button(joy_id, JOY_BUTTON_DPAD_LEFT, gamepad.dpad.left.isPressed);
+ Input::get_singleton()->joy_button(joy_id, JOY_BUTTON_DPAD_RIGHT, gamepad.dpad.right.isPressed);
+ };
+ };
}
- // else if (controller.gamepad != nil) {
- // // gamepad is the standard profile with 4 buttons, shoulder buttons and a
- // // D-pad
- // controller.gamepad.valueChangedHandler = ^(GCGamepad *gamepad,
- // GCControllerElement *element) {
- // int joy_id = [self getJoyIdForController:controller];
- //
- // if (element == gamepad.buttonA) {
- // Input::get_singleton()->joy_button(joy_id, JOY_BUTTON_A,
- // gamepad.buttonA.isPressed);
- // } else if (element == gamepad.buttonB) {
- // Input::get_singleton()->joy_button(joy_id, JOY_BUTTON_B,
- // gamepad.buttonB.isPressed);
- // } else if (element == gamepad.buttonX) {
- // Input::get_singleton()->joy_button(joy_id, JOY_BUTTON_X,
- // gamepad.buttonX.isPressed);
- // } else if (element == gamepad.buttonY) {
- // Input::get_singleton()->joy_button(joy_id, JOY_BUTTON_Y,
- // gamepad.buttonY.isPressed);
- // } else if (element == gamepad.leftShoulder) {
- // Input::get_singleton()->joy_button(joy_id, JOY_BUTTON_LEFT_SHOULDER,
- // gamepad.leftShoulder.isPressed);
- // } else if (element == gamepad.rightShoulder) {
- // Input::get_singleton()->joy_button(joy_id, JOY_BUTTON_RIGHT_SHOULDER,
- // gamepad.rightShoulder.isPressed);
- // } else if (element == gamepad.dpad) {
- // Input::get_singleton()->joy_button(joy_id, JOY_BUTTON_DPAD_UP,
- // gamepad.dpad.up.isPressed);
- // Input::get_singleton()->joy_button(joy_id, JOY_BUTTON_DPAD_DOWN,
- // gamepad.dpad.down.isPressed);
- // Input::get_singleton()->joy_button(joy_id, JOY_BUTTON_DPAD_LEFT,
- // gamepad.dpad.left.isPressed);
- // Input::get_singleton()->joy_button(joy_id, JOY_BUTTON_DPAD_RIGHT,
- // gamepad.dpad.right.isPressed);
- // };
- // };
- //#ifdef ADD_MICRO_GAMEPAD // disabling this for now, only available on iOS 9+,
- // // while we are setting that as the minimum, seems our
- // // build environment doesn't like it
- // } else if (controller.microGamepad != nil) {
- // // micro gamepads were added in OS 9 and feature just 2 buttons and a d-pad
- // controller.microGamepad.valueChangedHandler =
- // ^(GCMicroGamepad *gamepad, GCControllerElement *element) {
- // int joy_id = [self getJoyIdForController:controller];
- //
- // if (element == gamepad.buttonA) {
- // Input::get_singleton()->joy_button(joy_id, JOY_BUTTON_A,
- // gamepad.buttonA.isPressed);
- // } else if (element == gamepad.buttonX) {
- // Input::get_singleton()->joy_button(joy_id, JOY_BUTTON_X,
- // gamepad.buttonX.isPressed);
- // } else if (element == gamepad.dpad) {
- // Input::get_singleton()->joy_button(joy_id, JOY_BUTTON_DPAD_UP,
- // gamepad.dpad.up.isPressed);
- // Input::get_singleton()->joy_button(joy_id, JOY_BUTTON_DPAD_DOWN,
- // gamepad.dpad.down.isPressed);
- // Input::get_singleton()->joy_button(joy_id, JOY_BUTTON_DPAD_LEFT,
- // gamepad.dpad.left.isPressed);
- // Input::get_singleton()->joy_button(joy_id, JOY_BUTTON_DPAD_RIGHT,
- // gamepad.dpad.right.isPressed);
- // };
- // };
- //#endif
- // };
///@TODO need to add support for controller.motion which gives us access to
/// the orientation of the device (if supported)
diff --git a/platform/iphone/native_video_view.h b/platform/iphone/native_video_view.h
new file mode 100644
index 0000000000..d8687b3538
--- /dev/null
+++ b/platform/iphone/native_video_view.h
@@ -0,0 +1,41 @@
+/*************************************************************************/
+/* native_video_view.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#import <UIKit/UIKit.h>
+
+@interface GodotNativeVideoView : UIView
+
+- (BOOL)playVideoAtPath:(NSString *)filePath volume:(float)videoVolume audio:(NSString *)audioTrack subtitle:(NSString *)subtitleTrack;
+- (BOOL)isVideoPlaying;
+- (void)pauseVideo;
+- (void)unpauseVideo;
+- (void)stopVideo;
+
+@end
diff --git a/platform/iphone/native_video_view.m b/platform/iphone/native_video_view.m
new file mode 100644
index 0000000000..a4e9f209f0
--- /dev/null
+++ b/platform/iphone/native_video_view.m
@@ -0,0 +1,260 @@
+/*************************************************************************/
+/* native_video_view.m */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#import "native_video_view.h"
+#import <AVFoundation/AVFoundation.h>
+
+@interface GodotNativeVideoView ()
+
+@property(strong, nonatomic) AVAsset *avAsset;
+@property(strong, nonatomic) AVPlayerItem *avPlayerItem;
+@property(strong, nonatomic) AVPlayer *avPlayer;
+@property(strong, nonatomic) AVPlayerLayer *avPlayerLayer;
+@property(assign, nonatomic) CMTime videoCurrentTime;
+@property(assign, nonatomic) BOOL isVideoCurrentlyPlaying;
+
+@end
+
+@implementation GodotNativeVideoView
+
+- (instancetype)initWithFrame:(CGRect)frame {
+ self = [super initWithFrame:frame];
+
+ if (self) {
+ [self godot_commonInit];
+ }
+
+ return self;
+}
+
+- (instancetype)initWithCoder:(NSCoder *)coder {
+ self = [super initWithCoder:coder];
+
+ if (self) {
+ [self godot_commonInit];
+ }
+
+ return self;
+}
+
+- (void)godot_commonInit {
+ self.isVideoCurrentlyPlaying = NO;
+ self.videoCurrentTime = kCMTimeZero;
+
+ [self observeVideoAudio];
+}
+
+- (void)observeVideoAudio {
+ printf("******** adding observer for sound routing changes\n");
+ [[NSNotificationCenter defaultCenter]
+ addObserver:self
+ selector:@selector(audioRouteChangeListenerCallback:)
+ name:AVAudioSessionRouteChangeNotification
+ object:nil];
+}
+
+- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
+ if (object == self.avPlayerItem && [keyPath isEqualToString:@"status"]) {
+ [self handleVideoOrPlayerStatus];
+ }
+
+ if (object == self.avPlayer && [keyPath isEqualToString:@"rate"]) {
+ [self handleVideoPlayRate];
+ }
+}
+
+// MARK: Video Audio
+
+- (void)audioRouteChangeListenerCallback:(NSNotification *)notification {
+ printf("*********** route changed!\n");
+ NSDictionary *interuptionDict = notification.userInfo;
+
+ NSInteger routeChangeReason = [[interuptionDict valueForKey:AVAudioSessionRouteChangeReasonKey] integerValue];
+
+ switch (routeChangeReason) {
+ case AVAudioSessionRouteChangeReasonNewDeviceAvailable: {
+ NSLog(@"AVAudioSessionRouteChangeReasonNewDeviceAvailable");
+ NSLog(@"Headphone/Line plugged in");
+ } break;
+ case AVAudioSessionRouteChangeReasonOldDeviceUnavailable: {
+ NSLog(@"AVAudioSessionRouteChangeReasonOldDeviceUnavailable");
+ NSLog(@"Headphone/Line was pulled. Resuming video play....");
+ if ([self isVideoPlaying]) {
+ dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 0.5f * NSEC_PER_SEC), dispatch_get_main_queue(), ^{
+ [self.avPlayer play]; // NOTE: change this line according your current player implementation
+ NSLog(@"resumed play");
+ });
+ }
+ } break;
+ case AVAudioSessionRouteChangeReasonCategoryChange: {
+ // called at start - also when other audio wants to play
+ NSLog(@"AVAudioSessionRouteChangeReasonCategoryChange");
+ } break;
+ }
+}
+
+// MARK: Native Video Player
+
+- (void)handleVideoOrPlayerStatus {
+ if (self.avPlayerItem.status == AVPlayerItemStatusFailed || self.avPlayer.status == AVPlayerStatusFailed) {
+ [self stopVideo];
+ }
+
+ if (self.avPlayer.status == AVPlayerStatusReadyToPlay && self.avPlayerItem.status == AVPlayerItemStatusReadyToPlay && CMTimeCompare(self.videoCurrentTime, kCMTimeZero) == 0) {
+ // NSLog(@"time: %@", self.video_current_time);
+ [self.avPlayer seekToTime:self.videoCurrentTime];
+ self.videoCurrentTime = kCMTimeZero;
+ }
+}
+
+- (void)handleVideoPlayRate {
+ NSLog(@"Player playback rate changed: %.5f", self.avPlayer.rate);
+ if ([self isVideoPlaying] && self.avPlayer.rate == 0.0 && !self.avPlayer.error) {
+ dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 0.5f * NSEC_PER_SEC), dispatch_get_main_queue(), ^{
+ [self.avPlayer play]; // NOTE: change this line according your current player implementation
+ NSLog(@"resumed play");
+ });
+
+ NSLog(@" . . . PAUSED (or just started)");
+ }
+}
+
+- (BOOL)playVideoAtPath:(NSString *)filePath volume:(float)videoVolume audio:(NSString *)audioTrack subtitle:(NSString *)subtitleTrack {
+ self.avAsset = [AVAsset assetWithURL:[NSURL fileURLWithPath:filePath]];
+
+ self.avPlayerItem = [AVPlayerItem playerItemWithAsset:self.avAsset];
+ [self.avPlayerItem addObserver:self forKeyPath:@"status" options:0 context:nil];
+
+ self.avPlayer = [AVPlayer playerWithPlayerItem:self.avPlayerItem];
+ self.avPlayerLayer = [AVPlayerLayer playerLayerWithPlayer:self.avPlayer];
+
+ [self.avPlayer addObserver:self forKeyPath:@"status" options:0 context:nil];
+ [[NSNotificationCenter defaultCenter]
+ addObserver:self
+ selector:@selector(playerItemDidReachEnd:)
+ name:AVPlayerItemDidPlayToEndTimeNotification
+ object:[self.avPlayer currentItem]];
+
+ [self.avPlayer addObserver:self forKeyPath:@"rate" options:NSKeyValueObservingOptionNew context:0];
+
+ [self.avPlayerLayer setFrame:self.bounds];
+ [self.layer addSublayer:self.avPlayerLayer];
+ [self.avPlayer play];
+
+ AVMediaSelectionGroup *audioGroup = [self.avAsset mediaSelectionGroupForMediaCharacteristic:AVMediaCharacteristicAudible];
+
+ NSMutableArray *allAudioParams = [NSMutableArray array];
+ for (id track in audioGroup.options) {
+ NSString *language = [[track locale] localeIdentifier];
+ NSLog(@"subtitle lang: %@", language);
+
+ if ([language isEqualToString:audioTrack]) {
+ AVMutableAudioMixInputParameters *audioInputParams = [AVMutableAudioMixInputParameters audioMixInputParameters];
+ [audioInputParams setVolume:videoVolume atTime:kCMTimeZero];
+ [audioInputParams setTrackID:[track trackID]];
+ [allAudioParams addObject:audioInputParams];
+
+ AVMutableAudioMix *audioMix = [AVMutableAudioMix audioMix];
+ [audioMix setInputParameters:allAudioParams];
+
+ [self.avPlayer.currentItem selectMediaOption:track inMediaSelectionGroup:audioGroup];
+ [self.avPlayer.currentItem setAudioMix:audioMix];
+
+ break;
+ }
+ }
+
+ AVMediaSelectionGroup *subtitlesGroup = [self.avAsset mediaSelectionGroupForMediaCharacteristic:AVMediaCharacteristicLegible];
+ NSArray *useableTracks = [AVMediaSelectionGroup mediaSelectionOptionsFromArray:subtitlesGroup.options withoutMediaCharacteristics:[NSArray arrayWithObject:AVMediaCharacteristicContainsOnlyForcedSubtitles]];
+
+ for (id track in useableTracks) {
+ NSString *language = [[track locale] localeIdentifier];
+ NSLog(@"subtitle lang: %@", language);
+
+ if ([language isEqualToString:subtitleTrack]) {
+ [self.avPlayer.currentItem selectMediaOption:track inMediaSelectionGroup:subtitlesGroup];
+ break;
+ }
+ }
+
+ self.isVideoCurrentlyPlaying = YES;
+
+ return true;
+}
+
+- (BOOL)isVideoPlaying {
+ if (self.avPlayer.error) {
+ printf("Error during playback\n");
+ }
+ return (self.avPlayer.rate > 0 && !self.avPlayer.error);
+}
+
+- (void)pauseVideo {
+ self.videoCurrentTime = self.avPlayer.currentTime;
+ [self.avPlayer pause];
+ self.isVideoCurrentlyPlaying = NO;
+}
+
+- (void)unpauseVideo {
+ [self.avPlayer play];
+ self.isVideoCurrentlyPlaying = YES;
+}
+
+- (void)playerItemDidReachEnd:(NSNotification *)notification {
+ [self stopVideo];
+}
+
+- (void)finishPlayingVideo {
+ [self.avPlayer pause];
+ [self.avPlayerLayer removeFromSuperlayer];
+ self.avPlayerLayer = nil;
+
+ if (self.avPlayerItem) {
+ [self.avPlayerItem removeObserver:self forKeyPath:@"status"];
+ self.avPlayerItem = nil;
+ }
+
+ if (self.avPlayer) {
+ [self.avPlayer removeObserver:self forKeyPath:@"status"];
+ self.avPlayer = nil;
+ }
+
+ self.avAsset = nil;
+
+ self.isVideoCurrentlyPlaying = NO;
+}
+
+- (void)stopVideo {
+ [self finishPlayingVideo];
+
+ [self removeFromSuperview];
+}
+
+@end
diff --git a/platform/iphone/os_iphone.mm b/platform/iphone/os_iphone.mm
index 946fd51923..e007276b4b 100644
--- a/platform/iphone/os_iphone.mm
+++ b/platform/iphone/os_iphone.mm
@@ -128,7 +128,6 @@ void OSIPhone::initialize_modules() {
#ifdef GAME_CENTER_ENABLED
game_center = memnew(GameCenter);
Engine::get_singleton()->add_singleton(Engine::Singleton("GameCenter", game_center));
- game_center->connect();
#endif
#ifdef STOREKIT_ENABLED
@@ -272,7 +271,6 @@ String OSIPhone::get_model_name() const {
Error OSIPhone::shell_open(String p_uri) {
NSString *urlPath = [[NSString alloc] initWithUTF8String:p_uri.utf8().get_data()];
NSURL *url = [NSURL URLWithString:urlPath];
- [urlPath release];
if (![[UIApplication sharedApplication] canOpenURL:url]) {
return ERR_CANT_OPEN;
@@ -280,11 +278,7 @@ Error OSIPhone::shell_open(String p_uri) {
printf("opening url %s\n", p_uri.utf8().get_data());
- // if (@available(iOS 10, *)) {
[[UIApplication sharedApplication] openURL:url options:@{} completionHandler:nil];
- // } else {
- // [[UIApplication sharedApplication] openURL:url];
- // }
return OK;
};
diff --git a/platform/iphone/platform_config.h b/platform/iphone/platform_config.h
index 2bbbe47c0d..ec39ad0ba4 100644
--- a/platform/iphone/platform_config.h
+++ b/platform/iphone/platform_config.h
@@ -33,3 +33,10 @@
#define PLATFORM_REFCOUNT
#define PTHREAD_RENAME_SELF
+
+#define _weakify(var) __weak typeof(var) GDWeak_##var = var;
+#define _strongify(var) \
+ _Pragma("clang diagnostic push") \
+ _Pragma("clang diagnostic ignored \"-Wshadow\"") \
+ __strong typeof(var) var = GDWeak_##var; \
+ _Pragma("clang diagnostic pop")
diff --git a/platform/iphone/view_controller.h b/platform/iphone/view_controller.h
index dffdc01d4a..b0b31ae377 100644
--- a/platform/iphone/view_controller.h
+++ b/platform/iphone/view_controller.h
@@ -32,17 +32,15 @@
#import <UIKit/UIKit.h>
@class GodotView;
+@class GodotNativeVideoView;
@interface ViewController : UIViewController <GKGameCenterControllerDelegate>
-- (GodotView *)godotView;
+@property(nonatomic, readonly, strong) GodotView *godotView;
+@property(nonatomic, readonly, strong) GodotNativeVideoView *videoView;
// MARK: Native Video Player
- (BOOL)playVideoAtPath:(NSString *)filePath volume:(float)videoVolume audio:(NSString *)audioTrack subtitle:(NSString *)subtitleTrack;
-- (BOOL)isVideoPlaying;
-- (void)pauseVideo;
-- (void)unpauseVideo;
-- (void)stopVideo;
@end
diff --git a/platform/iphone/view_controller.mm b/platform/iphone/view_controller.mm
index 31597f7856..fb25041779 100644
--- a/platform/iphone/view_controller.mm
+++ b/platform/iphone/view_controller.mm
@@ -33,6 +33,7 @@
#include "display_server_iphone.h"
#import "godot_view.h"
#import "godot_view_renderer.h"
+#import "native_video_view.h"
#include "os_iphone.h"
#import <GameController/GameController.h>
@@ -40,16 +41,7 @@
@interface ViewController ()
@property(strong, nonatomic) GodotViewRenderer *renderer;
-
-// TODO: separate view to handle video
-// AVPlayer-related properties
-@property(strong, nonatomic) AVAsset *avAsset;
-@property(strong, nonatomic) AVPlayerItem *avPlayerItem;
-@property(strong, nonatomic) AVPlayer *avPlayer;
-@property(strong, nonatomic) AVPlayerLayer *avPlayerLayer;
-@property(assign, nonatomic) CMTime videoCurrentTime;
-@property(assign, nonatomic) BOOL isVideoCurrentlyPlaying;
-@property(assign, nonatomic) BOOL videoHasFoundError;
+@property(strong, nonatomic) GodotNativeVideoView *videoView;
@end
@@ -67,9 +59,6 @@
self.view = view;
view.renderer = self.renderer;
-
- [renderer release];
- [view release];
}
- (instancetype)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
@@ -93,9 +82,7 @@
}
- (void)godot_commonInit {
- self.isVideoCurrentlyPlaying = NO;
- self.videoCurrentTime = kCMTimeZero;
- self.videoHasFoundError = false;
+ // Initialize view controller values.
}
- (void)didReceiveMemoryWarning {
@@ -107,7 +94,6 @@
[super viewDidLoad];
[self observeKeyboard];
- [self observeAudio];
if (@available(iOS 11.0, *)) {
[self setNeedsUpdateOfScreenEdgesDeferringSystemGestures];
@@ -128,33 +114,13 @@
object:nil];
}
-- (void)observeAudio {
- printf("******** adding observer for sound routing changes\n");
- [[NSNotificationCenter defaultCenter]
- addObserver:self
- selector:@selector(audioRouteChangeListenerCallback:)
- name:AVAudioSessionRouteChangeNotification
- object:nil];
-}
-
-- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
- if (object == self.avPlayerItem && [keyPath isEqualToString:@"status"]) {
- [self handleVideoOrPlayerStatus];
- }
-
- if (object == self.avPlayer && [keyPath isEqualToString:@"rate"]) {
- [self handleVideoPlayRate];
- }
-}
-
- (void)dealloc {
- [self stopVideo];
+ [self.videoView stopVideo];
+ self.videoView = nil;
self.renderer = nil;
[[NSNotificationCenter defaultCenter] removeObserver:self];
-
- [super dealloc];
}
// MARK: Orientation
@@ -233,166 +199,19 @@
}
}
-// MARK: Audio
-
-- (void)audioRouteChangeListenerCallback:(NSNotification *)notification {
- printf("*********** route changed!\n");
- NSDictionary *interuptionDict = notification.userInfo;
-
- NSInteger routeChangeReason = [[interuptionDict valueForKey:AVAudioSessionRouteChangeReasonKey] integerValue];
-
- switch (routeChangeReason) {
- case AVAudioSessionRouteChangeReasonNewDeviceAvailable: {
- NSLog(@"AVAudioSessionRouteChangeReasonNewDeviceAvailable");
- NSLog(@"Headphone/Line plugged in");
- } break;
- case AVAudioSessionRouteChangeReasonOldDeviceUnavailable: {
- NSLog(@"AVAudioSessionRouteChangeReasonOldDeviceUnavailable");
- NSLog(@"Headphone/Line was pulled. Resuming video play....");
- if ([self isVideoPlaying]) {
- dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 0.5f * NSEC_PER_SEC), dispatch_get_main_queue(), ^{
- [self.avPlayer play]; // NOTE: change this line according your current player implementation
- NSLog(@"resumed play");
- });
- }
- } break;
- case AVAudioSessionRouteChangeReasonCategoryChange: {
- // called at start - also when other audio wants to play
- NSLog(@"AVAudioSessionRouteChangeReasonCategoryChange");
- } break;
- }
-}
-
// MARK: Native Video Player
-- (void)handleVideoOrPlayerStatus {
- if (self.avPlayerItem.status == AVPlayerItemStatusFailed || self.avPlayer.status == AVPlayerStatusFailed) {
- [self stopVideo];
- self.videoHasFoundError = true;
- }
-
- if (self.avPlayer.status == AVPlayerStatusReadyToPlay && self.avPlayerItem.status == AVPlayerItemStatusReadyToPlay && CMTimeCompare(self.videoCurrentTime, kCMTimeZero) == 0) {
- // NSLog(@"time: %@", self.video_current_time);
- [self.avPlayer seekToTime:self.videoCurrentTime];
- self.videoCurrentTime = kCMTimeZero;
- }
-}
-
-- (void)handleVideoPlayRate {
- NSLog(@"Player playback rate changed: %.5f", self.avPlayer.rate);
- if ([self isVideoPlaying] && self.avPlayer.rate == 0.0 && !self.avPlayer.error) {
- dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 0.5f * NSEC_PER_SEC), dispatch_get_main_queue(), ^{
- [self.avPlayer play]; // NOTE: change this line according your current player implementation
- NSLog(@"resumed play");
- });
-
- NSLog(@" . . . PAUSED (or just started)");
- }
-}
-
- (BOOL)playVideoAtPath:(NSString *)filePath volume:(float)videoVolume audio:(NSString *)audioTrack subtitle:(NSString *)subtitleTrack {
- self.avAsset = [AVAsset assetWithURL:[NSURL fileURLWithPath:filePath]];
-
- self.avPlayerItem = [AVPlayerItem playerItemWithAsset:self.avAsset];
- [self.avPlayerItem addObserver:self forKeyPath:@"status" options:0 context:nil];
-
- self.avPlayer = [AVPlayer playerWithPlayerItem:self.avPlayerItem];
- self.avPlayerLayer = [AVPlayerLayer playerLayerWithPlayer:self.avPlayer];
-
- [self.avPlayer addObserver:self forKeyPath:@"status" options:0 context:nil];
- [[NSNotificationCenter defaultCenter]
- addObserver:self
- selector:@selector(playerItemDidReachEnd:)
- name:AVPlayerItemDidPlayToEndTimeNotification
- object:[self.avPlayer currentItem]];
-
- [self.avPlayer addObserver:self forKeyPath:@"rate" options:NSKeyValueObservingOptionNew context:0];
-
- [self.avPlayerLayer setFrame:self.view.bounds];
- [self.view.layer addSublayer:self.avPlayerLayer];
- [self.avPlayer play];
-
- AVMediaSelectionGroup *audioGroup = [self.avAsset mediaSelectionGroupForMediaCharacteristic:AVMediaCharacteristicAudible];
-
- NSMutableArray *allAudioParams = [NSMutableArray array];
- for (id track in audioGroup.options) {
- NSString *language = [[track locale] localeIdentifier];
- NSLog(@"subtitle lang: %@", language);
-
- if ([language isEqualToString:audioTrack]) {
- AVMutableAudioMixInputParameters *audioInputParams = [AVMutableAudioMixInputParameters audioMixInputParameters];
- [audioInputParams setVolume:videoVolume atTime:kCMTimeZero];
- [audioInputParams setTrackID:[track trackID]];
- [allAudioParams addObject:audioInputParams];
-
- AVMutableAudioMix *audioMix = [AVMutableAudioMix audioMix];
- [audioMix setInputParameters:allAudioParams];
-
- [self.avPlayer.currentItem selectMediaOption:track inMediaSelectionGroup:audioGroup];
- [self.avPlayer.currentItem setAudioMix:audioMix];
-
- break;
- }
- }
-
- AVMediaSelectionGroup *subtitlesGroup = [self.avAsset mediaSelectionGroupForMediaCharacteristic:AVMediaCharacteristicLegible];
- NSArray *useableTracks = [AVMediaSelectionGroup mediaSelectionOptionsFromArray:subtitlesGroup.options withoutMediaCharacteristics:[NSArray arrayWithObject:AVMediaCharacteristicContainsOnlyForcedSubtitles]];
-
- for (id track in useableTracks) {
- NSString *language = [[track locale] localeIdentifier];
- NSLog(@"subtitle lang: %@", language);
-
- if ([language isEqualToString:subtitleTrack]) {
- [self.avPlayer.currentItem selectMediaOption:track inMediaSelectionGroup:subtitlesGroup];
- break;
- }
- }
-
- self.isVideoCurrentlyPlaying = YES;
-
- return true;
-}
-
-- (BOOL)isVideoPlaying {
- if (self.avPlayer.error) {
- printf("Error during playback\n");
- }
- return (self.avPlayer.rate > 0 && !self.avPlayer.error);
-}
-
-- (void)pauseVideo {
- self.videoCurrentTime = self.avPlayer.currentTime;
- [self.avPlayer pause];
- self.isVideoCurrentlyPlaying = NO;
-}
-
-- (void)unpauseVideo {
- [self.avPlayer play];
- self.isVideoCurrentlyPlaying = YES;
-}
-
-- (void)playerItemDidReachEnd:(NSNotification *)notification {
- [self stopVideo];
-}
-
-- (void)stopVideo {
- [self.avPlayer pause];
- [self.avPlayerLayer removeFromSuperlayer];
- self.avPlayerLayer = nil;
-
- if (self.avPlayerItem) {
- [self.avPlayerItem removeObserver:self forKeyPath:@"status"];
- self.avPlayerItem = nil;
- }
-
- if (self.avPlayer) {
- [self.avPlayer removeObserver:self forKeyPath:@"status"];
- self.avPlayer = nil;
+ // If we are showing some video already, reuse existing view for new video.
+ if (self.videoView) {
+ return [self.videoView playVideoAtPath:filePath volume:videoVolume audio:audioTrack subtitle:subtitleTrack];
+ } else {
+ // Create autoresizing view for video playback.
+ GodotNativeVideoView *videoView = [[GodotNativeVideoView alloc] initWithFrame:self.view.bounds];
+ videoView.autoresizingMask = UIViewAutoresizingFlexibleWidth & UIViewAutoresizingFlexibleHeight;
+ [self.view addSubview:videoView];
+ return [self.videoView playVideoAtPath:filePath volume:videoVolume audio:audioTrack subtitle:subtitleTrack];
}
-
- self.avAsset = nil;
-
- self.isVideoCurrentlyPlaying = NO;
}
// MARK: Delegates
diff --git a/platform/iphone/vulkan_context_iphone.mm b/platform/iphone/vulkan_context_iphone.mm
index cb4dbe7f85..d62e826957 100644
--- a/platform/iphone/vulkan_context_iphone.mm
+++ b/platform/iphone/vulkan_context_iphone.mm
@@ -35,14 +35,12 @@ const char *VulkanContextIPhone::_get_platform_surface_extension() const {
return VK_MVK_IOS_SURFACE_EXTENSION_NAME;
}
-Error VulkanContextIPhone::window_create(DisplayServer::WindowID p_window_id,
- CALayer *p_metal_layer, int p_width,
- int p_height) {
+Error VulkanContextIPhone::window_create(DisplayServer::WindowID p_window_id, CALayer *p_metal_layer, int p_width, int p_height) {
VkIOSSurfaceCreateInfoMVK createInfo;
createInfo.sType = VK_STRUCTURE_TYPE_IOS_SURFACE_CREATE_INFO_MVK;
createInfo.pNext = NULL;
createInfo.flags = 0;
- createInfo.pView = p_metal_layer;
+ createInfo.pView = (__bridge const void *)p_metal_layer;
VkSurfaceKHR surface;
VkResult err =
diff --git a/platform/javascript/SCsub b/platform/javascript/SCsub
index 21456efde5..a8861124b9 100644
--- a/platform/javascript/SCsub
+++ b/platform/javascript/SCsub
@@ -19,6 +19,7 @@ build = env.add_program(build_targets, javascript_files)
js_libraries = [
"native/http_request.js",
+ "native/library_godot_audio.js",
]
for lib in js_libraries:
env.Append(LINKFLAGS=["--js-library", env.File(lib).path])
diff --git a/platform/javascript/audio_driver_javascript.cpp b/platform/javascript/audio_driver_javascript.cpp
index 9604914b2c..6ea948004e 100644
--- a/platform/javascript/audio_driver_javascript.cpp
+++ b/platform/javascript/audio_driver_javascript.cpp
@@ -31,34 +31,57 @@
#include "audio_driver_javascript.h"
#include "core/project_settings.h"
+#include "godot_audio.h"
#include <emscripten.h>
AudioDriverJavaScript *AudioDriverJavaScript::singleton = nullptr;
bool AudioDriverJavaScript::is_available() {
- return EM_ASM_INT({
- if (!(window.AudioContext || window.webkitAudioContext)) {
- return 0;
- }
- return 1;
- }) != 0;
+ return godot_audio_is_available() != 0;
}
const char *AudioDriverJavaScript::get_name() const {
return "JavaScript";
}
-extern "C" EMSCRIPTEN_KEEPALIVE void audio_driver_js_mix() {
- AudioDriverJavaScript::singleton->mix_to_js();
+#ifndef NO_THREADS
+void AudioDriverJavaScript::_audio_thread_func(void *p_data) {
+ AudioDriverJavaScript *obj = static_cast<AudioDriverJavaScript *>(p_data);
+ while (!obj->quit) {
+ obj->lock();
+ if (!obj->needs_process) {
+ obj->unlock();
+ OS::get_singleton()->delay_usec(1000); // Give the browser some slack.
+ continue;
+ }
+ obj->_js_driver_process();
+ obj->needs_process = false;
+ obj->unlock();
+ }
+}
+#endif
+
+extern "C" EMSCRIPTEN_KEEPALIVE void audio_driver_process_start() {
+#ifndef NO_THREADS
+ AudioDriverJavaScript::singleton->lock();
+#else
+ AudioDriverJavaScript::singleton->_js_driver_process();
+#endif
+}
+
+extern "C" EMSCRIPTEN_KEEPALIVE void audio_driver_process_end() {
+#ifndef NO_THREADS
+ AudioDriverJavaScript::singleton->needs_process = true;
+ AudioDriverJavaScript::singleton->unlock();
+#endif
}
extern "C" EMSCRIPTEN_KEEPALIVE void audio_driver_process_capture(float sample) {
AudioDriverJavaScript::singleton->process_capture(sample);
}
-void AudioDriverJavaScript::mix_to_js() {
- int channel_count = get_total_channels_by_speaker_mode(get_speaker_mode());
+void AudioDriverJavaScript::_js_driver_process() {
int sample_count = memarr_len(internal_buffer) / channel_count;
int32_t *stream_buffer = reinterpret_cast<int32_t *>(internal_buffer);
audio_server_process(sample_count, stream_buffer);
@@ -73,37 +96,12 @@ void AudioDriverJavaScript::process_capture(float sample) {
}
Error AudioDriverJavaScript::init() {
- int mix_rate = GLOBAL_GET("audio/mix_rate");
+ mix_rate = GLOBAL_GET("audio/mix_rate");
int latency = GLOBAL_GET("audio/output_latency");
- /* clang-format off */
- _driver_id = EM_ASM_INT({
- const MIX_RATE = $0;
- const LATENCY = $1 / 1000;
- return Module.IDHandler.add({
- 'context': new (window.AudioContext || window.webkitAudioContext)({ sampleRate: MIX_RATE, latencyHint: LATENCY}),
- 'input': null,
- 'stream': null,
- 'script': null
- });
- }, mix_rate, latency);
- /* clang-format on */
-
- int channel_count = get_total_channels_by_speaker_mode(get_speaker_mode());
+ channel_count = godot_audio_init(mix_rate, latency);
buffer_length = closest_power_of_2((latency * mix_rate / 1000) * channel_count);
- /* clang-format off */
- buffer_length = EM_ASM_INT({
- var ref = Module.IDHandler.get($0);
- const ctx = ref['context'];
- const BUFFER_LENGTH = $1;
- const CHANNEL_COUNT = $2;
-
- var script = ctx.createScriptProcessor(BUFFER_LENGTH, 2, CHANNEL_COUNT);
- script.connect(ctx.destination);
- ref['script'] = script;
- return script.bufferSize;
- }, _driver_id, buffer_length, channel_count);
- /* clang-format on */
+ buffer_length = godot_audio_create_processor(buffer_length, channel_count);
if (!buffer_length) {
return FAILED;
}
@@ -114,134 +112,60 @@ Error AudioDriverJavaScript::init() {
internal_buffer = memnew_arr(float, buffer_length *channel_count);
}
- return internal_buffer ? OK : ERR_OUT_OF_MEMORY;
+ if (!internal_buffer) {
+ return ERR_OUT_OF_MEMORY;
+ }
+ return OK;
}
void AudioDriverJavaScript::start() {
- /* clang-format off */
- EM_ASM({
- const ref = Module.IDHandler.get($0);
- var INTERNAL_BUFFER_PTR = $1;
-
- var audioDriverMixFunction = cwrap('audio_driver_js_mix');
- var audioDriverProcessCapture = cwrap('audio_driver_process_capture', null, ['number']);
- ref['script'].onaudioprocess = function(audioProcessingEvent) {
- audioDriverMixFunction();
-
- var input = audioProcessingEvent.inputBuffer;
- var output = audioProcessingEvent.outputBuffer;
- var internalBuffer = HEAPF32.subarray(
- INTERNAL_BUFFER_PTR / HEAPF32.BYTES_PER_ELEMENT,
- INTERNAL_BUFFER_PTR / HEAPF32.BYTES_PER_ELEMENT + output.length * output.numberOfChannels);
-
- for (var channel = 0; channel < output.numberOfChannels; channel++) {
- var outputData = output.getChannelData(channel);
- // Loop through samples.
- for (var sample = 0; sample < outputData.length; sample++) {
- outputData[sample] = internalBuffer[sample * output.numberOfChannels + channel];
- }
- }
-
- if (ref['input']) {
- var inputDataL = input.getChannelData(0);
- var inputDataR = input.getChannelData(1);
- for (var i = 0; i < inputDataL.length; i++) {
- audioDriverProcessCapture(inputDataL[i]);
- audioDriverProcessCapture(inputDataR[i]);
- }
- }
- };
- }, _driver_id, internal_buffer);
- /* clang-format on */
+#ifndef NO_THREADS
+ thread = Thread::create(_audio_thread_func, this);
+#endif
+ godot_audio_start(internal_buffer);
}
void AudioDriverJavaScript::resume() {
- /* clang-format off */
- EM_ASM({
- const ref = Module.IDHandler.get($0);
- if (ref && ref['context'] && ref['context'].resume)
- ref['context'].resume();
- }, _driver_id);
- /* clang-format on */
+ godot_audio_resume();
}
float AudioDriverJavaScript::get_latency() {
- /* clang-format off */
- return EM_ASM_DOUBLE({
- const ref = Module.IDHandler.get($0);
- var latency = 0;
- if (ref && ref['context']) {
- const ctx = ref['context'];
- if (ctx.baseLatency) {
- latency += ctx.baseLatency;
- }
- if (ctx.outputLatency) {
- latency += ctx.outputLatency;
- }
- }
- return latency;
- }, _driver_id);
- /* clang-format on */
+ return godot_audio_get_latency();
}
int AudioDriverJavaScript::get_mix_rate() const {
- /* clang-format off */
- return EM_ASM_INT({
- const ref = Module.IDHandler.get($0);
- return ref && ref['context'] ? ref['context'].sampleRate : 0;
- }, _driver_id);
- /* clang-format on */
+ return mix_rate;
}
AudioDriver::SpeakerMode AudioDriverJavaScript::get_speaker_mode() const {
- /* clang-format off */
- return get_speaker_mode_by_total_channels(EM_ASM_INT({
- const ref = Module.IDHandler.get($0);
- return ref && ref['context'] ? ref['context'].destination.channelCount : 0;
- }, _driver_id));
- /* clang-format on */
+ return get_speaker_mode_by_total_channels(channel_count);
}
-// No locking, as threads are not supported.
void AudioDriverJavaScript::lock() {
+#ifndef NO_THREADS
+ mutex.lock();
+#endif
}
void AudioDriverJavaScript::unlock() {
+#ifndef NO_THREADS
+ mutex.unlock();
+#endif
}
void AudioDriverJavaScript::finish_async() {
- // Close the context, add the operation to the async_finish list in module.
- int id = _driver_id;
- _driver_id = 0;
-
- /* clang-format off */
- EM_ASM({
- const id = $0;
- var ref = Module.IDHandler.get(id);
- Module.async_finish.push(new Promise(function(accept, reject) {
- if (!ref) {
- console.log("Ref not found!", id, Module.IDHandler);
- setTimeout(accept, 0);
- } else {
- Module.IDHandler.remove(id);
- const context = ref['context'];
- // Disconnect script and input.
- ref['script'].disconnect();
- if (ref['input'])
- ref['input'].disconnect();
- ref = null;
- context.close().then(function() {
- accept();
- }).catch(function(e) {
- accept();
- });
- }
- }));
- }, id);
- /* clang-format on */
+#ifndef NO_THREADS
+ quit = true; // Ask thread to quit.
+#endif
+ godot_audio_finish_async();
}
void AudioDriverJavaScript::finish() {
+#ifndef NO_THREADS
+ Thread::wait_to_finish(thread);
+ memdelete(thread);
+ thread = NULL;
+#endif
if (internal_buffer) {
memdelete_arr(internal_buffer);
internal_buffer = nullptr;
@@ -249,56 +173,14 @@ void AudioDriverJavaScript::finish() {
}
Error AudioDriverJavaScript::capture_start() {
+ godot_audio_capture_stop();
input_buffer_init(buffer_length);
-
- /* clang-format off */
- EM_ASM({
- function gotMediaInput(stream) {
- var ref = Module.IDHandler.get($0);
- ref['stream'] = stream;
- ref['input'] = ref['context'].createMediaStreamSource(stream);
- ref['input'].connect(ref['script']);
- }
-
- function gotMediaInputError(e) {
- out(e);
- }
-
- if (navigator.mediaDevices.getUserMedia) {
- navigator.mediaDevices.getUserMedia({"audio": true}).then(gotMediaInput, gotMediaInputError);
- } else {
- if (!navigator.getUserMedia)
- navigator.getUserMedia = navigator.webkitGetUserMedia || navigator.mozGetUserMedia;
- navigator.getUserMedia({"audio": true}, gotMediaInput, gotMediaInputError);
- }
- }, _driver_id);
- /* clang-format on */
-
+ godot_audio_capture_start();
return OK;
}
Error AudioDriverJavaScript::capture_stop() {
- /* clang-format off */
- EM_ASM({
- var ref = Module.IDHandler.get($0);
- if (ref['stream']) {
- const tracks = ref['stream'].getTracks();
- for (var i = 0; i < tracks.length; i++) {
- tracks[i].stop();
- }
- ref['stream'] = null;
- }
-
- if (ref['input']) {
- ref['input'].disconnect();
- ref['input'] = null;
- }
-
- }, _driver_id);
- /* clang-format on */
-
input_buffer.clear();
-
return OK;
}
diff --git a/platform/javascript/audio_driver_javascript.h b/platform/javascript/audio_driver_javascript.h
index c1607301d7..56a7da0307 100644
--- a/platform/javascript/audio_driver_javascript.h
+++ b/platform/javascript/audio_driver_javascript.h
@@ -33,15 +33,30 @@
#include "servers/audio_server.h"
+#include "core/os/mutex.h"
+#include "core/os/thread.h"
+
class AudioDriverJavaScript : public AudioDriver {
+private:
float *internal_buffer = nullptr;
- int _driver_id = 0;
int buffer_length = 0;
+ int mix_rate = 0;
+ int channel_count = 0;
public:
+#ifndef NO_THREADS
+ Mutex mutex;
+ Thread *thread = nullptr;
+ bool quit = false;
+ bool needs_process = true;
+
+ static void _audio_thread_func(void *p_data);
+#endif
+
+ void _js_driver_process();
+
static bool is_available();
- void mix_to_js();
void process_capture(float sample);
static AudioDriverJavaScript *singleton;
diff --git a/platform/javascript/detect.py b/platform/javascript/detect.py
index 81287cead8..4b5890545f 100644
--- a/platform/javascript/detect.py
+++ b/platform/javascript/detect.py
@@ -1,6 +1,7 @@
import os
-from emscripten_helpers import parse_config, run_closure_compiler, create_engine_file
+from emscripten_helpers import run_closure_compiler, create_engine_file
+from SCons.Util import WhereIs
def is_active():
@@ -12,7 +13,7 @@ def get_name():
def can_build():
- return "EM_CONFIG" in os.environ or os.path.exists(os.path.expanduser("~/.emscripten"))
+ return WhereIs("emcc") is not None
def get_opts():
@@ -100,9 +101,6 @@ def configure(env):
# Closure compiler extern and support for ecmascript specs (const, let, etc).
env["ENV"]["EMCC_CLOSURE_ARGS"] = "--language_in ECMASCRIPT6"
- em_config = parse_config()
- env.PrependENVPath("PATH", em_config["EMCC_ROOT"])
-
env["CC"] = "emcc"
env["CXX"] = "em++"
env["LINK"] = "emcc"
@@ -139,6 +137,7 @@ def configure(env):
env.Append(LINKFLAGS=["-s", "USE_PTHREADS=1"])
env.Append(LINKFLAGS=["-s", "PTHREAD_POOL_SIZE=4"])
env.Append(LINKFLAGS=["-s", "WASM_MEM_MAX=2048MB"])
+ env.extra_suffix = ".threads" + env.extra_suffix
else:
env.Append(CPPDEFINES=["NO_THREADS"])
diff --git a/platform/javascript/emscripten_helpers.py b/platform/javascript/emscripten_helpers.py
index a55c9d3f48..f6db10fbbd 100644
--- a/platform/javascript/emscripten_helpers.py
+++ b/platform/javascript/emscripten_helpers.py
@@ -1,28 +1,11 @@
import os
-
-def parse_config():
- em_config_file = os.getenv("EM_CONFIG") or os.path.expanduser("~/.emscripten")
- if not os.path.exists(em_config_file):
- raise RuntimeError("Emscripten configuration file '%s' does not exist" % em_config_file)
-
- normalized = {}
- em_config = {}
- with open(em_config_file) as f:
- try:
- # Emscripten configuration file is a Python file with simple assignments.
- exec(f.read(), em_config)
- except StandardError as e:
- raise RuntimeError("Emscripten configuration file '%s' is invalid:\n%s" % (em_config_file, e))
- normalized["EMCC_ROOT"] = em_config.get("EMSCRIPTEN_ROOT")
- normalized["NODE_JS"] = em_config.get("NODE_JS")
- normalized["CLOSURE_BIN"] = os.path.join(normalized["EMCC_ROOT"], "node_modules", ".bin", "google-closure-compiler")
- return normalized
+from SCons.Util import WhereIs
def run_closure_compiler(target, source, env, for_signature):
- cfg = parse_config()
- cmd = [cfg["NODE_JS"], cfg["CLOSURE_BIN"]]
+ closure_bin = os.path.join(os.path.dirname(WhereIs("emcc")), "node_modules", ".bin", "google-closure-compiler")
+ cmd = [WhereIs("node"), closure_bin]
cmd.extend(["--compilation_level", "ADVANCED_OPTIMIZATIONS"])
for f in env["JSEXTERNS"]:
cmd.extend(["--externs", f.get_abspath()])
diff --git a/platform/javascript/export/export.cpp b/platform/javascript/export/export.cpp
index 230575abce..a83ff44d20 100644
--- a/platform/javascript/export/export.cpp
+++ b/platform/javascript/export/export.cpp
@@ -124,6 +124,9 @@ public:
String s = "HTTP/1.1 200 OK\r\n";
s += "Connection: Close\r\n";
s += "Content-Type: " + ctype + "\r\n";
+ s += "Access-Control-Allow-Origin: *\r\n";
+ s += "Cross-Origin-Opener-Policy: same-origin\r\n";
+ s += "Cross-Origin-Embedder-Policy: require-corp\r\n";
s += "\r\n";
CharString cs = s.utf8();
Error err = connection->put_data((const uint8_t *)cs.get_data(), cs.size() - 1);
diff --git a/platform/javascript/godot_audio.h b/platform/javascript/godot_audio.h
new file mode 100644
index 0000000000..f7f26e5262
--- /dev/null
+++ b/platform/javascript/godot_audio.h
@@ -0,0 +1,58 @@
+/*************************************************************************/
+/* godot_audio.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifndef GODOT_AUDIO_H
+#define GODOT_AUDIO_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "stddef.h"
+
+extern int godot_audio_is_available();
+
+extern int godot_audio_init(int p_mix_rate, int p_latency);
+extern int godot_audio_create_processor(int p_buffer_length, int p_channel_count);
+
+extern void godot_audio_start(float *r_buffer_ptr);
+extern void godot_audio_resume();
+extern void godot_audio_finish_async();
+
+extern float godot_audio_get_latency();
+
+extern void godot_audio_capture_start();
+extern void godot_audio_capture_stop();
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* GODOT_AUDIO_H */
diff --git a/platform/javascript/native/library_godot_audio.js b/platform/javascript/native/library_godot_audio.js
new file mode 100644
index 0000000000..d300280ccd
--- /dev/null
+++ b/platform/javascript/native/library_godot_audio.js
@@ -0,0 +1,173 @@
+/*************************************************************************/
+/* library_godot_audio.js */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+var GodotAudio = {
+
+ $GodotAudio: {
+
+ ctx: null,
+ input: null,
+ script: null,
+ },
+
+ godot_audio_is_available__proxy: 'sync',
+ godot_audio_is_available: function () {
+ if (!(window.AudioContext || window.webkitAudioContext)) {
+ return 0;
+ }
+ return 1;
+ },
+
+ godot_audio_init: function(mix_rate, latency) {
+ GodotAudio.ctx = new (window.AudioContext || window.webkitAudioContext)({
+ sampleRate: mix_rate,
+ latencyHint: latency
+ });
+ return GodotAudio.ctx.destination.channelCount;
+ },
+
+ godot_audio_create_processor: function(buffer_length, channel_count) {
+ GodotAudio.script = GodotAudio.ctx.createScriptProcessor(buffer_length, 2, channel_count);
+ GodotAudio.script.connect(GodotAudio.ctx.destination);
+ return GodotAudio.script.bufferSize;
+ },
+
+ godot_audio_start: function(buffer_ptr) {
+ var audioDriverProcessStart = cwrap('audio_driver_process_start');
+ var audioDriverProcessEnd = cwrap('audio_driver_process_end');
+ var audioDriverProcessCapture = cwrap('audio_driver_process_capture', null, ['number']);
+ GodotAudio.script.onaudioprocess = function(audioProcessingEvent) {
+ audioDriverProcessStart();
+
+ var input = audioProcessingEvent.inputBuffer;
+ var output = audioProcessingEvent.outputBuffer;
+ var internalBuffer = HEAPF32.subarray(
+ buffer_ptr / HEAPF32.BYTES_PER_ELEMENT,
+ buffer_ptr / HEAPF32.BYTES_PER_ELEMENT + output.length * output.numberOfChannels);
+ for (var channel = 0; channel < output.numberOfChannels; channel++) {
+ var outputData = output.getChannelData(channel);
+ // Loop through samples.
+ for (var sample = 0; sample < outputData.length; sample++) {
+ outputData[sample] = internalBuffer[sample * output.numberOfChannels + channel];
+ }
+ }
+
+ if (GodotAudio.input) {
+ var inputDataL = input.getChannelData(0);
+ var inputDataR = input.getChannelData(1);
+ for (var i = 0; i < inputDataL.length; i++) {
+ audioDriverProcessCapture(inputDataL[i]);
+ audioDriverProcessCapture(inputDataR[i]);
+ }
+ }
+ audioDriverProcessEnd();
+ };
+ },
+
+ godot_audio_resume: function() {
+ if (GodotAudio.ctx && GodotAudio.ctx.state != 'running') {
+ GodotAudio.ctx.resume();
+ }
+ },
+
+ godot_audio_finish_async: function() {
+ Module.async_finish.push(new Promise(function(accept, reject) {
+ if (!GodotAudio.ctx) {
+ setTimeout(accept, 0);
+ } else {
+ if (GodotAudio.script) {
+ GodotAudio.script.disconnect();
+ GodotAudio.script = null;
+ }
+ if (GodotAudio.input) {
+ GodotAudio.input.disconnect();
+ GodotAudio.input = null;
+ }
+ GodotAudio.ctx.close().then(function() {
+ accept();
+ }).catch(function(e) {
+ accept();
+ });
+ GodotAudio.ctx = null;
+ }
+ }));
+ },
+
+ godot_audio_get_latency__proxy: 'sync',
+ godot_audio_get_latency: function() {
+ var latency = 0;
+ if (GodotAudio.ctx) {
+ if (GodotAudio.ctx.baseLatency) {
+ latency += GodotAudio.ctx.baseLatency;
+ }
+ if (GodotAudio.ctx.outputLatency) {
+ latency += GodotAudio.ctx.outputLatency;
+ }
+ }
+ return latency;
+ },
+
+ godot_audio_capture_start__proxy: 'sync',
+ godot_audio_capture_start: function() {
+ if (GodotAudio.input) {
+ return; // Already started.
+ }
+ function gotMediaInput(stream) {
+ GodotAudio.input = GodotAudio.ctx.createMediaStreamSource(stream);
+ GodotAudio.input.connect(GodotAudio.script);
+ }
+
+ function gotMediaInputError(e) {
+ out(e);
+ }
+
+ if (navigator.mediaDevices.getUserMedia) {
+ navigator.mediaDevices.getUserMedia({"audio": true}).then(gotMediaInput, gotMediaInputError);
+ } else {
+ if (!navigator.getUserMedia)
+ navigator.getUserMedia = navigator.webkitGetUserMedia || navigator.mozGetUserMedia;
+ navigator.getUserMedia({"audio": true}, gotMediaInput, gotMediaInputError);
+ }
+ },
+
+ godot_audio_capture_stop__proxy: 'sync',
+ godot_audio_capture_stop: function() {
+ if (GodotAudio.input) {
+ const tracks = GodotAudio.input.mediaStream.getTracks();
+ for (var i = 0; i < tracks.length; i++) {
+ tracks[i].stop();
+ }
+ GodotAudio.input.disconnect();
+ GodotAudio.input = null;
+ }
+ },
+};
+
+autoAddDeps(GodotAudio, "$GodotAudio");
+mergeInto(LibraryManager.library, GodotAudio);
diff --git a/platform/osx/display_server_osx.mm b/platform/osx/display_server_osx.mm
index 49f0e7bfa3..cd5b71890c 100644
--- a/platform/osx/display_server_osx.mm
+++ b/platform/osx/display_server_osx.mm
@@ -2246,11 +2246,18 @@ int DisplayServerOSX::screen_get_dpi(int p_screen) const {
NSArray *screenArray = [NSScreen screens];
if ((NSUInteger)p_screen < [screenArray count]) {
NSDictionary *description = [[screenArray objectAtIndex:p_screen] deviceDescription];
- NSSize displayDPI = [[description objectForKey:NSDeviceResolution] sizeValue];
- return (displayDPI.width + displayDPI.height) / 2;
+
+ const NSSize displayPixelSize = [[description objectForKey:NSDeviceSize] sizeValue];
+ const CGSize displayPhysicalSize = CGDisplayScreenSize([[description objectForKey:@"NSScreenNumber"] unsignedIntValue]);
+ float scale = [[screenArray objectAtIndex:p_screen] backingScaleFactor];
+
+ float den2 = (displayPhysicalSize.width / 25.4f) * (displayPhysicalSize.width / 25.4f) + (displayPhysicalSize.height / 25.4f) * (displayPhysicalSize.height / 25.4f);
+ if (den2 > 0.0f) {
+ return ceil(sqrt(displayPixelSize.width * displayPixelSize.width + displayPixelSize.height * displayPixelSize.height) / sqrt(den2) * scale);
+ }
}
- return 96;
+ return 72;
}
float DisplayServerOSX::screen_get_scale(int p_screen) const {
diff --git a/platform/uwp/detect.py b/platform/uwp/detect.py
index a7ca26c16c..6e57dbbf64 100644
--- a/platform/uwp/detect.py
+++ b/platform/uwp/detect.py
@@ -80,6 +80,9 @@ def configure(env):
env["ENV"] = os.environ
vc_base_path = os.environ["VCTOOLSINSTALLDIR"] if "VCTOOLSINSTALLDIR" in os.environ else os.environ["VCINSTALLDIR"]
+ # Force to use Unicode encoding
+ env.Append(MSVC_FLAGS=["/utf-8"])
+
# ANGLE
angle_root = os.getenv("ANGLE_SRC_PATH")
env.Prepend(CPPPATH=[angle_root + "/include"])
diff --git a/platform/uwp/export/export.cpp b/platform/uwp/export/export.cpp
index 5679ec3eac..219174b509 100644
--- a/platform/uwp/export/export.cpp
+++ b/platform/uwp/export/export.cpp
@@ -249,7 +249,7 @@ void AppxPackager::make_content_types(const String &p_path) {
Map<String, String> types;
for (int i = 0; i < file_metadata.size(); i++) {
- String ext = file_metadata[i].name.get_extension();
+ String ext = file_metadata[i].name.get_extension().to_lower();
if (types.has(ext)) {
continue;
diff --git a/platform/windows/detect.py b/platform/windows/detect.py
index 6b503c1561..d5474ace9c 100644
--- a/platform/windows/detect.py
+++ b/platform/windows/detect.py
@@ -65,7 +65,7 @@ def get_opts():
# Vista support dropped after EOL due to GH-10243
("target_win_version", "Targeted Windows version, >= 0x0601 (Windows 7)", "0x0601"),
EnumVariable("debug_symbols", "Add debugging symbols to release builds", "yes", ("yes", "no", "full")),
- EnumVariable("windows_subsystem", "Windows subsystem", "gui", ("console", "gui")),
+ EnumVariable("windows_subsystem", "Windows subsystem", "default", ("default", "console", "gui")),
BoolVariable("separate_debug_symbols", "Create a separate file containing debugging symbols", False),
("msvc_version", "MSVC version to use. Ignored if VCINSTALLDIR is set in shell env.", None),
BoolVariable("use_mingw", "Use the Mingw compiler, even if MSVC is installed. Only used on Windows.", False),
@@ -178,8 +178,15 @@ def configure_msvc(env, manual_msvc_config):
"""Configure env to work with MSVC"""
# Build type
+
if env["tests"]:
env["windows_subsystem"] = "console"
+ elif env["windows_subsystem"] == "default":
+ # Default means we use console for debug, gui for release.
+ if "debug" in env["target"]:
+ env["windows_subsystem"] = "console"
+ else:
+ env["windows_subsystem"] = "gui"
if env["target"] == "release":
if env["optimize"] == "speed": # optimize for speed (default)
@@ -215,8 +222,8 @@ def configure_msvc(env, manual_msvc_config):
## Compile/link flags
env.AppendUnique(CCFLAGS=["/MT", "/Gd", "/GR", "/nologo"])
- if int(env["MSVC_VERSION"].split(".")[0]) >= 14: # vs2015 and later
- env.AppendUnique(CCFLAGS=["/utf-8"])
+ # Force to use Unicode encoding
+ env.Append(MSVC_FLAGS=["/utf-8"])
env.AppendUnique(CXXFLAGS=["/TP"]) # assume all sources are C++
if manual_msvc_config: # should be automatic if SCons found it
if os.getenv("WindowsSdkDir") is not None:
@@ -311,6 +318,12 @@ def configure_mingw(env):
if env["tests"]:
env["windows_subsystem"] = "console"
+ elif env["windows_subsystem"] == "default":
+ # Default means we use console for debug, gui for release.
+ if "debug" in env["target"]:
+ env["windows_subsystem"] = "console"
+ else:
+ env["windows_subsystem"] = "gui"
if env["target"] == "release":
env.Append(CCFLAGS=["-msse2"])