summaryrefslogtreecommitdiff
path: root/platform
diff options
context:
space:
mode:
Diffstat (limited to 'platform')
-rw-r--r--platform/iphone/SCsub4
-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.h2
-rw-r--r--platform/iphone/godot_view_gesture_recognizer.mm (renamed from platform/iphone/godot_view_gesture_recognizer.m)22
-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/linuxbsd/display_server_x11.cpp354
-rw-r--r--platform/linuxbsd/display_server_x11.h17
-rw-r--r--platform/osx/display_server_osx.mm13
27 files changed, 899 insertions, 678 deletions
diff --git a/platform/iphone/SCsub b/platform/iphone/SCsub
index 49c77468ed..1dd37dabe5 100644
--- a/platform/iphone/SCsub
+++ b/platform/iphone/SCsub
@@ -18,7 +18,9 @@ iphone_lib = [
"godot_view.mm",
"display_layer.mm",
"godot_view_renderer.mm",
- "godot_view_gesture_recognizer.m",
+ "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.h b/platform/iphone/godot_view_gesture_recognizer.h
index 8d84914712..1431a9fb89 100644
--- a/platform/iphone/godot_view_gesture_recognizer.h
+++ b/platform/iphone/godot_view_gesture_recognizer.h
@@ -39,6 +39,8 @@
@interface GodotViewGestureRecognizer : UIGestureRecognizer
+@property(nonatomic, readonly, assign) NSTimeInterval delayTimeInterval;
+
- (instancetype)init;
@end
diff --git a/platform/iphone/godot_view_gesture_recognizer.m b/platform/iphone/godot_view_gesture_recognizer.mm
index 377ccd52a5..71367a629c 100644
--- a/platform/iphone/godot_view_gesture_recognizer.m
+++ b/platform/iphone/godot_view_gesture_recognizer.mm
@@ -1,5 +1,5 @@
/*************************************************************************/
-/* godot_view_gesture_recognizer.m */
+/* godot_view_gesture_recognizer.mm */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
@@ -30,8 +30,7 @@
#import "godot_view_gesture_recognizer.h"
-// Using same delay interval that is used for `UIScrollView`
-const NSTimeInterval kGLGestureDelayInterval = 0.150;
+#include "core/project_settings.h"
// Minimum distance for touches to move to fire
// a delay timer before scheduled time.
@@ -41,6 +40,12 @@ const CGFloat kGLGestureMovementDistance = 0.5;
@interface GodotViewGestureRecognizer ()
+@property(nonatomic, readwrite, assign) NSTimeInterval delayTimeInterval;
+
+@end
+
+@interface GodotViewGestureRecognizer ()
+
// Timer used to delay begin touch message.
// Should work as simple emulation of UIDelayedAction
@property(strong, nonatomic) NSTimer *delayTimer;
@@ -60,6 +65,8 @@ const CGFloat kGLGestureMovementDistance = 0.5;
self.delaysTouchesBegan = YES;
self.delaysTouchesEnded = YES;
+ self.delayTimeInterval = GLOBAL_GET("input_devices/pointing/ios/touch_delay");
+
return self;
}
@@ -76,8 +83,6 @@ const CGFloat kGLGestureMovementDistance = 0.5;
if (self.delayedEvent) {
self.delayedEvent = nil;
}
-
- [super dealloc];
}
- (void)delayTouches:(NSSet *)touches andEvent:(UIEvent *)event {
@@ -87,7 +92,7 @@ const CGFloat kGLGestureMovementDistance = 0.5;
self.delayedEvent = event;
self.delayTimer = [NSTimer
- scheduledTimerWithTimeInterval:kGLGestureDelayInterval
+ scheduledTimerWithTimeInterval:self.delayTimeInterval
target:self
selector:@selector(fireDelayedTouches:)
userInfo:nil
@@ -109,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 {
@@ -130,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 {
@@ -148,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/linuxbsd/display_server_x11.cpp b/platform/linuxbsd/display_server_x11.cpp
index e3cf992302..8b0d08d1cb 100644
--- a/platform/linuxbsd/display_server_x11.cpp
+++ b/platform/linuxbsd/display_server_x11.cpp
@@ -322,23 +322,19 @@ bool DisplayServerX11::_refresh_device_info() {
}
void DisplayServerX11::_flush_mouse_motion() {
- while (true) {
- if (XPending(x11_display) > 0) {
- XEvent event;
- XPeekEvent(x11_display, &event);
-
- if (XGetEventData(x11_display, &event.xcookie) && event.xcookie.type == GenericEvent && event.xcookie.extension == xi.opcode) {
- XIDeviceEvent *event_data = (XIDeviceEvent *)event.xcookie.data;
-
- if (event_data->evtype == XI_RawMotion) {
- XNextEvent(x11_display, &event);
- } else {
- break;
- }
- } else {
- break;
+ // Block events polling while flushing motion events.
+ MutexLock mutex_lock(events_mutex);
+
+ for (uint32_t event_index = 0; event_index < polled_events.size(); ++event_index) {
+ XEvent &event = polled_events[event_index];
+ if (XGetEventData(x11_display, &event.xcookie) && event.xcookie.type == GenericEvent && event.xcookie.extension == xi.opcode) {
+ XIDeviceEvent *event_data = (XIDeviceEvent *)event.xcookie.data;
+ if (event_data->evtype == XI_RawMotion) {
+ XFreeEventData(x11_display, &event.xcookie);
+ polled_events.remove(event_index--);
+ continue;
}
- } else {
+ XFreeEventData(x11_display, &event.xcookie);
break;
}
}
@@ -451,12 +447,25 @@ int DisplayServerX11::mouse_get_button_state() const {
void DisplayServerX11::clipboard_set(const String &p_text) {
_THREAD_SAFE_METHOD_
- internal_clipboard = p_text;
+ {
+ // The clipboard content can be accessed while polling for events.
+ MutexLock mutex_lock(events_mutex);
+ internal_clipboard = p_text;
+ }
+
XSetSelectionOwner(x11_display, XA_PRIMARY, windows[MAIN_WINDOW_ID].x11_window, CurrentTime);
XSetSelectionOwner(x11_display, XInternAtom(x11_display, "CLIPBOARD", 0), windows[MAIN_WINDOW_ID].x11_window, CurrentTime);
}
-static String _clipboard_get_impl(Atom p_source, Window x11_window, ::Display *x11_display, String p_internal_clipboard, Atom target) {
+Bool DisplayServerX11::_predicate_clipboard_selection(Display *display, XEvent *event, XPointer arg) {
+ if (event->type == SelectionNotify && event->xselection.requestor == *(Window *)arg) {
+ return True;
+ } else {
+ return False;
+ }
+}
+
+String DisplayServerX11::_clipboard_get_impl(Atom p_source, Window x11_window, Atom target) const {
String ret;
Atom type;
@@ -464,23 +473,27 @@ static String _clipboard_get_impl(Atom p_source, Window x11_window, ::Display *x
int format, result;
unsigned long len, bytes_left, dummy;
unsigned char *data;
- Window Sown = XGetSelectionOwner(x11_display, p_source);
+ Window selection_owner = XGetSelectionOwner(x11_display, p_source);
- if (Sown == x11_window) {
- return p_internal_clipboard;
- };
+ if (selection_owner == x11_window) {
+ return internal_clipboard;
+ }
- if (Sown != None) {
- XConvertSelection(x11_display, p_source, target, selection,
- x11_window, CurrentTime);
- XFlush(x11_display);
- while (true) {
+ if (selection_owner != None) {
+ {
+ // Block events polling while processing selection events.
+ MutexLock mutex_lock(events_mutex);
+
+ XConvertSelection(x11_display, p_source, target, selection,
+ x11_window, CurrentTime);
+
+ XFlush(x11_display);
+
+ // Blocking wait for predicate to be True
+ // and remove the event from the queue.
XEvent event;
- XNextEvent(x11_display, &event);
- if (event.type == SelectionNotify && event.xselection.requestor == x11_window) {
- break;
- };
- };
+ XIfEvent(x11_display, &event, _predicate_clipboard_selection, (XPointer)&x11_window);
+ }
//
// Do not get any data, see how much data is there
@@ -514,14 +527,14 @@ static String _clipboard_get_impl(Atom p_source, Window x11_window, ::Display *x
return ret;
}
-static String _clipboard_get(Atom p_source, Window x11_window, ::Display *x11_display, String p_internal_clipboard) {
+String DisplayServerX11::_clipboard_get(Atom p_source, Window x11_window) const {
String ret;
Atom utf8_atom = XInternAtom(x11_display, "UTF8_STRING", True);
if (utf8_atom != None) {
- ret = _clipboard_get_impl(p_source, x11_window, x11_display, p_internal_clipboard, utf8_atom);
+ ret = _clipboard_get_impl(p_source, x11_window, utf8_atom);
}
- if (ret == "") {
- ret = _clipboard_get_impl(p_source, x11_window, x11_display, p_internal_clipboard, XA_STRING);
+ if (ret.empty()) {
+ ret = _clipboard_get_impl(p_source, x11_window, XA_STRING);
}
return ret;
}
@@ -530,11 +543,11 @@ String DisplayServerX11::clipboard_get() const {
_THREAD_SAFE_METHOD_
String ret;
- ret = _clipboard_get(XInternAtom(x11_display, "CLIPBOARD", 0), windows[MAIN_WINDOW_ID].x11_window, x11_display, internal_clipboard);
+ ret = _clipboard_get(XInternAtom(x11_display, "CLIPBOARD", 0), windows[MAIN_WINDOW_ID].x11_window);
- if (ret == "") {
- ret = _clipboard_get(XA_PRIMARY, windows[MAIN_WINDOW_ID].x11_window, x11_display, internal_clipboard);
- };
+ if (ret.empty()) {
+ ret = _clipboard_get(XA_PRIMARY, windows[MAIN_WINDOW_ID].x11_window);
+ }
return ret;
}
@@ -1649,10 +1662,16 @@ void DisplayServerX11::window_set_ime_active(const bool p_active, WindowID p_win
return;
}
+ // Block events polling while changing input focus
+ // because it triggers some event polling internally.
if (p_active) {
- XSetICFocus(wd.xic);
+ {
+ MutexLock mutex_lock(events_mutex);
+ XSetICFocus(wd.xic);
+ }
window_set_ime_position(wd.im_position, p_window);
} else {
+ MutexLock mutex_lock(events_mutex);
XUnsetICFocus(wd.xic);
}
}
@@ -1673,7 +1692,14 @@ void DisplayServerX11::window_set_ime_position(const Point2i &p_pos, WindowID p_
spot.x = short(p_pos.x);
spot.y = short(p_pos.y);
XVaNestedList preedit_attr = XVaCreateNestedList(0, XNSpotLocation, &spot, nullptr);
- XSetICValues(wd.xic, XNPreeditAttributes, preedit_attr, nullptr);
+
+ {
+ // Block events polling during this call
+ // because it triggers some event polling internally.
+ MutexLock mutex_lock(events_mutex);
+ XSetICValues(wd.xic, XNPreeditAttributes, preedit_attr, nullptr);
+ }
+
XFree(preedit_attr);
}
@@ -1993,7 +2019,7 @@ unsigned int DisplayServerX11::_get_mouse_button_state(unsigned int p_x11_button
return last_button_state;
}
-void DisplayServerX11::_handle_key_event(WindowID p_window, XKeyEvent *p_event, bool p_echo) {
+void DisplayServerX11::_handle_key_event(WindowID p_window, XKeyEvent *p_event, LocalVector<XEvent> &p_events, uint32_t &p_event_index, bool p_echo) {
WindowData wd = windows[p_window];
// X11 functions don't know what const is
XKeyEvent *xkeyevent = p_event;
@@ -2130,7 +2156,7 @@ void DisplayServerX11::_handle_key_event(WindowID p_window, XKeyEvent *p_event,
/* Phase 4, determine if event must be filtered */
// This seems to be a side-effect of using XIM.
- // XEventFilter looks like a core X11 function,
+ // XFilterEvent looks like a core X11 function,
// but it's actually just used to see if we must
// ignore a deadkey, or events XIM determines
// must not reach the actual gui.
@@ -2164,17 +2190,16 @@ void DisplayServerX11::_handle_key_event(WindowID p_window, XKeyEvent *p_event,
// Echo characters in X11 are a keyrelease and a keypress
// one after the other with the (almot) same timestamp.
- // To detect them, i use XPeekEvent and check that their
- // difference in time is below a threshold.
+ // To detect them, i compare to the next event in list and
+ // check that their difference in time is below a threshold.
if (xkeyevent->type != KeyPress) {
p_echo = false;
// make sure there are events pending,
// so this call won't block.
- if (XPending(x11_display) > 0) {
- XEvent peek_event;
- XPeekEvent(x11_display, &peek_event);
+ if (p_event_index + 1 < p_events.size()) {
+ XEvent &peek_event = p_events[p_event_index + 1];
// I'm using a threshold of 5 msecs,
// since sometimes there seems to be a little
@@ -2189,9 +2214,9 @@ void DisplayServerX11::_handle_key_event(WindowID p_window, XKeyEvent *p_event,
KeySym rk;
XLookupString((XKeyEvent *)&peek_event, str, 256, &rk, nullptr);
if (rk == keysym_keycode) {
- XEvent event;
- XNextEvent(x11_display, &event); //erase next event
- _handle_key_event(p_window, (XKeyEvent *)&event, true);
+ // Consume to next event.
+ ++p_event_index;
+ _handle_key_event(p_window, (XKeyEvent *)&peek_event, p_events, p_event_index, true);
return; //ignore current, echo next
}
}
@@ -2246,6 +2271,66 @@ void DisplayServerX11::_handle_key_event(WindowID p_window, XKeyEvent *p_event,
Input::get_singleton()->accumulate_input_event(k);
}
+void DisplayServerX11::_handle_selection_request_event(XSelectionRequestEvent *p_event) {
+ XEvent respond;
+ if (p_event->target == XInternAtom(x11_display, "UTF8_STRING", 0) ||
+ p_event->target == XInternAtom(x11_display, "COMPOUND_TEXT", 0) ||
+ p_event->target == XInternAtom(x11_display, "TEXT", 0) ||
+ p_event->target == XA_STRING ||
+ p_event->target == XInternAtom(x11_display, "text/plain;charset=utf-8", 0) ||
+ p_event->target == XInternAtom(x11_display, "text/plain", 0)) {
+ // Directly using internal clipboard because we know our window
+ // is the owner during a selection request.
+ CharString clip = internal_clipboard.utf8();
+ XChangeProperty(x11_display,
+ p_event->requestor,
+ p_event->property,
+ p_event->target,
+ 8,
+ PropModeReplace,
+ (unsigned char *)clip.get_data(),
+ clip.length());
+ respond.xselection.property = p_event->property;
+ } else if (p_event->target == XInternAtom(x11_display, "TARGETS", 0)) {
+ Atom data[7];
+ data[0] = XInternAtom(x11_display, "TARGETS", 0);
+ data[1] = XInternAtom(x11_display, "UTF8_STRING", 0);
+ data[2] = XInternAtom(x11_display, "COMPOUND_TEXT", 0);
+ data[3] = XInternAtom(x11_display, "TEXT", 0);
+ data[4] = XA_STRING;
+ data[5] = XInternAtom(x11_display, "text/plain;charset=utf-8", 0);
+ data[6] = XInternAtom(x11_display, "text/plain", 0);
+
+ XChangeProperty(x11_display,
+ p_event->requestor,
+ p_event->property,
+ XA_ATOM,
+ 32,
+ PropModeReplace,
+ (unsigned char *)&data,
+ sizeof(data) / sizeof(data[0]));
+ respond.xselection.property = p_event->property;
+
+ } else {
+ char *targetname = XGetAtomName(x11_display, p_event->target);
+ printf("No Target '%s'\n", targetname);
+ if (targetname) {
+ XFree(targetname);
+ }
+ respond.xselection.property = None;
+ }
+
+ respond.xselection.type = SelectionNotify;
+ respond.xselection.display = p_event->display;
+ respond.xselection.requestor = p_event->requestor;
+ respond.xselection.selection = p_event->selection;
+ respond.xselection.target = p_event->target;
+ respond.xselection.time = p_event->time;
+
+ XSendEvent(x11_display, p_event->requestor, True, NoEventMask, &respond);
+ XFlush(x11_display);
+}
+
void DisplayServerX11::_xim_destroy_callback(::XIM im, ::XPointer client_data,
::XPointer call_data) {
WARN_PRINT("Input method stopped");
@@ -2358,6 +2443,65 @@ void DisplayServerX11::_send_window_event(const WindowData &wd, WindowEvent p_ev
}
}
+void DisplayServerX11::_poll_events_thread(void *ud) {
+ DisplayServerX11 *display_server = (DisplayServerX11 *)ud;
+ display_server->_poll_events();
+}
+
+Bool DisplayServerX11::_predicate_all_events(Display *display, XEvent *event, XPointer arg) {
+ // Just accept all events.
+ return True;
+}
+
+void DisplayServerX11::_poll_events() {
+ int x11_fd = ConnectionNumber(x11_display);
+ fd_set in_fds;
+
+ while (!events_thread_done) {
+ XFlush(x11_display);
+
+ FD_ZERO(&in_fds);
+ FD_SET(x11_fd, &in_fds);
+
+ struct timeval tv;
+ tv.tv_usec = 0;
+ tv.tv_sec = 1;
+
+ // Wait for next event or timeout.
+ int num_ready_fds = select(x11_fd + 1, &in_fds, NULL, NULL, &tv);
+ if (num_ready_fds < 0) {
+ ERR_PRINT("_poll_events: select error: " + itos(errno));
+ }
+
+ // Process events from the queue.
+ {
+ MutexLock mutex_lock(events_mutex);
+
+ // Non-blocking wait for next event
+ // and remove it from the queue.
+ XEvent ev;
+ while (XCheckIfEvent(x11_display, &ev, _predicate_all_events, nullptr)) {
+ // Check if the input manager wants to process the event.
+ if (XFilterEvent(&ev, None)) {
+ // Event has been filtered by the Input Manager,
+ // it has to be ignored and a new one will be received.
+ continue;
+ }
+
+ // Handle selection request events directly in the event thread, because
+ // communication through the x server takes several events sent back and forth
+ // and we don't want to block other programs while processing only one each frame.
+ if (ev.type == SelectionRequest) {
+ _handle_selection_request_event(&(ev.xselectionrequest));
+ continue;
+ }
+
+ polled_events.push_back(ev);
+ }
+ }
+ }
+}
+
void DisplayServerX11::process_events() {
_THREAD_SAFE_METHOD_
@@ -2401,13 +2545,16 @@ void DisplayServerX11::process_events() {
xi.tilt = Vector2();
xi.pressure_supported = false;
- while (XPending(x11_display) > 0) {
- XEvent event;
- XNextEvent(x11_display, &event);
+ LocalVector<XEvent> events;
+ {
+ // Block events polling while flushing events.
+ MutexLock mutex_lock(events_mutex);
+ events = polled_events;
+ polled_events.clear();
+ }
- if (XFilterEvent(&event, None)) {
- continue;
- }
+ for (uint32_t event_index = 0; event_index < events.size(); ++event_index) {
+ XEvent &event = events[event_index];
WindowID window_id = MAIN_WINDOW_ID;
@@ -2666,6 +2813,9 @@ void DisplayServerX11::process_events() {
}*/
#endif
if (wd.xic) {
+ // Block events polling while changing input focus
+ // because it triggers some event polling internally.
+ MutexLock mutex_lock(events_mutex);
XSetICFocus(wd.xic);
}
@@ -2715,7 +2865,10 @@ void DisplayServerX11::process_events() {
xi.state.clear();
#endif
if (wd.xic) {
- XSetICFocus(wd.xic);
+ // Block events polling while changing input focus
+ // because it triggers some event polling internally.
+ MutexLock mutex_lock(events_mutex);
+ XUnsetICFocus(wd.xic);
}
} break;
@@ -2834,11 +2987,11 @@ void DisplayServerX11::process_events() {
break;
}
- if (XPending(x11_display) > 0) {
- XEvent tevent;
- XPeekEvent(x11_display, &tevent);
- if (tevent.type == MotionNotify) {
- XNextEvent(x11_display, &event);
+ if (event_index + 1 < events.size()) {
+ const XEvent &next_event = events[event_index + 1];
+ if (next_event.type == MotionNotify) {
+ ++event_index;
+ event = next_event;
} else {
break;
}
@@ -2969,67 +3122,7 @@ void DisplayServerX11::process_events() {
// key event is a little complex, so
// it will be handled in its own function.
- _handle_key_event(window_id, (XKeyEvent *)&event);
- } break;
- case SelectionRequest: {
- XSelectionRequestEvent *req;
- XEvent e, respond;
- e = event;
-
- req = &(e.xselectionrequest);
- if (req->target == XInternAtom(x11_display, "UTF8_STRING", 0) ||
- req->target == XInternAtom(x11_display, "COMPOUND_TEXT", 0) ||
- req->target == XInternAtom(x11_display, "TEXT", 0) ||
- req->target == XA_STRING ||
- req->target == XInternAtom(x11_display, "text/plain;charset=utf-8", 0) ||
- req->target == XInternAtom(x11_display, "text/plain", 0)) {
- CharString clip = clipboard_get().utf8();
- XChangeProperty(x11_display,
- req->requestor,
- req->property,
- req->target,
- 8,
- PropModeReplace,
- (unsigned char *)clip.get_data(),
- clip.length());
- respond.xselection.property = req->property;
- } else if (req->target == XInternAtom(x11_display, "TARGETS", 0)) {
- Atom data[7];
- data[0] = XInternAtom(x11_display, "TARGETS", 0);
- data[1] = XInternAtom(x11_display, "UTF8_STRING", 0);
- data[2] = XInternAtom(x11_display, "COMPOUND_TEXT", 0);
- data[3] = XInternAtom(x11_display, "TEXT", 0);
- data[4] = XA_STRING;
- data[5] = XInternAtom(x11_display, "text/plain;charset=utf-8", 0);
- data[6] = XInternAtom(x11_display, "text/plain", 0);
-
- XChangeProperty(x11_display,
- req->requestor,
- req->property,
- XA_ATOM,
- 32,
- PropModeReplace,
- (unsigned char *)&data,
- sizeof(data) / sizeof(data[0]));
- respond.xselection.property = req->property;
-
- } else {
- char *targetname = XGetAtomName(x11_display, req->target);
- printf("No Target '%s'\n", targetname);
- if (targetname) {
- XFree(targetname);
- }
- respond.xselection.property = None;
- }
-
- respond.xselection.type = SelectionNotify;
- respond.xselection.display = req->display;
- respond.xselection.requestor = req->requestor;
- respond.xselection.selection = req->selection;
- respond.xselection.target = req->target;
- respond.xselection.time = req->time;
- XSendEvent(x11_display, req->requestor, True, NoEventMask, &respond);
- XFlush(x11_display);
+ _handle_key_event(window_id, (XKeyEvent *)&event, events, event_index);
} break;
case SelectionNotify:
@@ -3424,6 +3517,10 @@ DisplayServerX11::WindowID DisplayServerX11::_create_window(WindowMode p_mode, u
XChangeProperty(x11_display, wd.x11_window, xdnd_aware, XA_ATOM, 32, PropModeReplace, (unsigned char *)&xdnd_version, 1);
if (xim && xim_style) {
+ // Block events polling while changing input focus
+ // because it triggers some event polling internally.
+ MutexLock mutex_lock(events_mutex);
+
wd.xic = XCreateIC(xim, XNInputStyle, xim_style, XNClientWindow, wd.x11_window, XNFocusWindow, wd.x11_window, (char *)nullptr);
if (XGetICValues(wd.xic, XNFilterEvents, &im_event_mask, nullptr) != nullptr) {
WARN_PRINT("XGetICValues couldn't obtain XNFilterEvents value");
@@ -3926,12 +4023,19 @@ DisplayServerX11::DisplayServerX11(const String &p_rendering_driver, WindowMode
}
}
+ events_thread = Thread::create(_poll_events_thread, this);
+
_update_real_mouse_position(windows[MAIN_WINDOW_ID]);
r_error = OK;
}
DisplayServerX11::~DisplayServerX11() {
+ events_thread_done = true;
+ Thread::wait_to_finish(events_thread);
+ memdelete(events_thread);
+ events_thread = nullptr;
+
//destroy all windows
for (Map<WindowID, WindowData>::Element *E = windows.front(); E; E = E->next()) {
#ifdef VULKAN_ENABLED
diff --git a/platform/linuxbsd/display_server_x11.h b/platform/linuxbsd/display_server_x11.h
index e894cf515c..740bf81fd9 100644
--- a/platform/linuxbsd/display_server_x11.h
+++ b/platform/linuxbsd/display_server_x11.h
@@ -36,6 +36,7 @@
#include "servers/display_server.h"
#include "core/input/input.h"
+#include "core/local_vector.h"
#include "drivers/alsa/audio_driver_alsa.h"
#include "drivers/alsamidi/midi_driver_alsamidi.h"
#include "drivers/pulseaudio/audio_driver_pulseaudio.h"
@@ -202,7 +203,11 @@ class DisplayServerX11 : public DisplayServer {
MouseMode mouse_mode;
Point2i center;
- void _handle_key_event(WindowID p_window, XKeyEvent *p_event, bool p_echo = false);
+ void _handle_key_event(WindowID p_window, XKeyEvent *p_event, LocalVector<XEvent> &p_events, uint32_t &p_event_index, bool p_echo = false);
+ void _handle_selection_request_event(XSelectionRequestEvent *p_event);
+
+ String _clipboard_get_impl(Atom p_source, Window x11_window, Atom target) const;
+ String _clipboard_get(Atom p_source, Window x11_window) const;
//bool minimized;
//bool window_has_focus;
@@ -252,6 +257,16 @@ class DisplayServerX11 : public DisplayServer {
static void _dispatch_input_events(const Ref<InputEvent> &p_event);
void _dispatch_input_event(const Ref<InputEvent> &p_event);
+ mutable Mutex events_mutex;
+ Thread *events_thread = nullptr;
+ bool events_thread_done = false;
+ LocalVector<XEvent> polled_events;
+ static void _poll_events_thread(void *ud);
+ void _poll_events();
+
+ static Bool _predicate_all_events(Display *display, XEvent *event, XPointer arg);
+ static Bool _predicate_clipboard_selection(Display *display, XEvent *event, XPointer arg);
+
protected:
void _window_changed(XEvent *event);
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 {