diff --git a/SConstruct b/SConstruct
index ce49308d101a..1e76c431b79a 100644
--- a/SConstruct
+++ b/SConstruct
@@ -647,7 +647,11 @@ if env["scu_build"]:
# are actually handled to change compile options, etc.
detect.configure(env)
-print(f'Building for platform "{env["platform"]}", architecture "{env["arch"]}", target "{env["target"]}".')
+platform_string = env["platform"]
+if env.get("simulator"):
+ platform_string += " (simulator)"
+print(f'Building for platform "{platform_string}", architecture "{env["arch"]}", target "{env["target"]}".')
+
if env.dev_build:
print_info("Developer build, with debug optimization level and debug symbols (unless overridden).")
diff --git a/core/input/input_builders.py b/core/input/input_builders.py
index c63f595fced6..92b6495d8835 100644
--- a/core/input/input_builders.py
+++ b/core/input/input_builders.py
@@ -48,7 +48,7 @@ def make_default_controller_mappings(target, source, env):
"Windows": "WINDOWS",
"Mac OS X": "MACOS",
"Android": "ANDROID",
- "iOS": "IOS",
+ "iOS": "APPLE_EMBEDDED",
"Web": "WEB",
}
diff --git a/core/os/os.cpp b/core/os/os.cpp
index c4828fe1275a..29e49f8f65fe 100644
--- a/core/os/os.cpp
+++ b/core/os/os.cpp
@@ -585,7 +585,7 @@ bool OS::has_feature(const String &p_feature) {
}
#endif
-#if defined(IOS_SIMULATOR)
+#if defined(IOS_SIMULATOR) || defined(VISIONOS_SIMULATOR)
if (p_feature == "simulator") {
return true;
}
diff --git a/doc/classes/EditorExportPlatformAppleEmbedded.xml b/doc/classes/EditorExportPlatformAppleEmbedded.xml
new file mode 100644
index 000000000000..790ef3a88daa
--- /dev/null
+++ b/doc/classes/EditorExportPlatformAppleEmbedded.xml
@@ -0,0 +1,13 @@
+
+
+
+ Base class for the Apple embedded platform exporters (iOS and visionOS).
+
+
+ The base class for Apple embedded platform exporters. These include iOS and visionOS, but not macOS. See the classes inheriting from this one for more details.
+
+
+ $DOCS_URL/tutorials/export/exporting_for_ios.html
+ $DOCS_URL/tutorials/platform/ios/index.html
+
+
diff --git a/doc/classes/EditorExportPlatformPC.xml b/doc/classes/EditorExportPlatformPC.xml
index def14e595524..07e46c857c20 100644
--- a/doc/classes/EditorExportPlatformPC.xml
+++ b/doc/classes/EditorExportPlatformPC.xml
@@ -4,7 +4,7 @@
Base class for the desktop platform exporter (Windows and Linux/BSD).
- The base class for the desktop platform exporters. These include Windows and Linux/BSD, but not macOS. See the classes inheriting this one for more details.
+ The base class for the desktop platform exporters. These include Windows and Linux/BSD, but not macOS. See the classes inheriting from this one for more details.
$DOCS_URL/tutorials/export/exporting_for_windows.html
diff --git a/doc/classes/EditorExportPlugin.xml b/doc/classes/EditorExportPlugin.xml
index 01dff2b53a1b..c9e34d464020 100644
--- a/doc/classes/EditorExportPlugin.xml
+++ b/doc/classes/EditorExportPlugin.xml
@@ -251,6 +251,57 @@
If no modifications are needed, then an empty [PackedByteArray] should be returned.
+
+
+
+
+ Adds an Apple embedded platform bundle file from the given [param path] to the exported project.
+
+
+
+
+
+
+ Adds C++ code to the Apple embedded platform export. The final code is created from the code appended by each active export plugin.
+
+
+
+
+
+
+ Adds a dynamic library (*.dylib, *.framework) to the Linking Phase in the Apple embedded platform's Xcode project and embeds it into the resulting binary.
+ [b]Note:[/b] For static libraries (*.a), this works in the same way as [method add_apple_embedded_platform_framework].
+ [b]Note:[/b] This method should not be used for System libraries as they are already present on the device.
+
+
+
+
+
+
+ Adds a static library (*.a) or a dynamic library (*.dylib, *.framework) to the Linking Phase to the Apple embedded platform's Xcode project.
+
+
+
+
+
+
+ Adds linker flags for the Apple embedded platform export.
+
+
+
+
+
+
+ Adds additional fields to the Apple embedded platform's project Info.plist file.
+
+
+
+
+
+
+ Adds a static library from the given [param path] to the Apple embedded platform project.
+
+
@@ -262,55 +313,55 @@
[param file] will not be imported, so consider using [method _customize_resource] to remap imported resources.
-
+
Adds an iOS bundle file from the given [param path] to the exported project.
-
+
- Adds a C++ code to the iOS export. The final code is created from the code appended by each active export plugin.
+ Adds C++ code to the iOS export. The final code is created from the code appended by each active export plugin.
-
+
Adds a dynamic library (*.dylib, *.framework) to Linking Phase in iOS's Xcode project and embeds it into resulting binary.
- [b]Note:[/b] For static libraries (*.a) works in same way as [method add_ios_framework].
+ [b]Note:[/b] For static libraries (*.a), this works the in same way as [method add_apple_embedded_platform_framework].
[b]Note:[/b] This method should not be used for System libraries as they are already present on the device.
-
+
- Adds a static library (*.a) or dynamic library (*.dylib, *.framework) to Linking Phase in iOS's Xcode project.
+ Adds a static library (*.a) or a dynamic library (*.dylib, *.framework) to the Linking Phase to the iOS Xcode project.
-
+
Adds linker flags for the iOS export.
-
+
- Adds content for iOS Property List files.
+ Adds additional fields to the iOS project Info.plist file.
-
+
- Adds a static lib from the given [param path] to the iOS project.
+ Adds a static library from the given [param path] to the iOS project.
diff --git a/doc/classes/ProjectSettings.xml b/doc/classes/ProjectSettings.xml
index cef1253b6ddd..df7401ccd67e 100644
--- a/doc/classes/ProjectSettings.xml
+++ b/doc/classes/ProjectSettings.xml
@@ -900,6 +900,9 @@
MacOS override for [member display/display_server/driver].
+
+ visionOS override for [member display/display_server/driver].
+
Windows override for [member display/display_server/driver].
@@ -3163,6 +3166,11 @@
- [code]metal[/code] (default), Metal from native drivers, only supported on Apple Silicon Macs. On Intel Macs, it will automatically fall back to [code]vulkan[/code] as Metal support is not implemented.
- [code]vulkan[/code], Vulkan over Metal via MoltenVK, supported on both Apple Silicon and Intel Macs.
+
+ visionOS override for [member rendering/rendering_device/driver].
+ Only one option is supported:
+ - [code]metal[/code] (default), Metal from native drivers.
+
Windows override for [member rendering/rendering_device/driver].
Two options are supported:
diff --git a/drivers/SCsub b/drivers/SCsub
index b5624b177809..5b891631ce8d 100644
--- a/drivers/SCsub
+++ b/drivers/SCsub
@@ -14,8 +14,6 @@ SConscript("windows/SCsub")
# Sounds drivers
SConscript("alsa/SCsub")
-if env["platform"] == "ios" or env["platform"] == "macos":
- SConscript("coreaudio/SCsub")
SConscript("pulseaudio/SCsub")
if env["platform"] == "windows":
SConscript("wasapi/SCsub")
@@ -28,8 +26,11 @@ if env["xaudio2"]:
SConscript("xaudio2/SCsub")
# Shared Apple platform drivers
-if env["platform"] in ["macos", "ios"]:
+if env["platform"] in ["macos", "ios", "visionos"]:
SConscript("apple/SCsub")
+ SConscript("coreaudio/SCsub")
+if env["platform"] in ["ios", "visionos"]:
+ SConscript("apple_embedded/SCsub")
# Accessibility
if env["accesskit"] and env["platform"] in ["macos", "windows", "linuxbsd"]:
@@ -37,7 +38,7 @@ if env["accesskit"] and env["platform"] in ["macos", "windows", "linuxbsd"]:
# Midi drivers
SConscript("alsamidi/SCsub")
-if env["platform"] in ["macos", "ios"]:
+if env["platform"] in ["macos"]:
SConscript("coremidi/SCsub")
SConscript("winmidi/SCsub")
diff --git a/drivers/apple_embedded/SCsub b/drivers/apple_embedded/SCsub
new file mode 100644
index 000000000000..7a4b82bd9e08
--- /dev/null
+++ b/drivers/apple_embedded/SCsub
@@ -0,0 +1,16 @@
+#!/usr/bin/env python
+from misc.utility.scons_hints import *
+
+Import("env")
+
+env_apple_embedded = env.Clone()
+
+# Enable module support
+env_apple_embedded.Append(CCFLAGS=["-fmodules", "-fcxx-modules"])
+
+# Use bundled Vulkan headers
+vulkan_dir = "#thirdparty/vulkan"
+env_apple_embedded.Prepend(CPPPATH=[vulkan_dir, vulkan_dir + "/include"])
+
+# Driver source files
+env_apple_embedded.add_source_files(env.drivers_sources, "*.mm")
diff --git a/platform/ios/app_delegate.h b/drivers/apple_embedded/app_delegate_service.h
similarity index 81%
rename from platform/ios/app_delegate.h
rename to drivers/apple_embedded/app_delegate_service.h
index 2f29e0871da7..6314d6060b3a 100644
--- a/platform/ios/app_delegate.h
+++ b/drivers/apple_embedded/app_delegate_service.h
@@ -1,5 +1,5 @@
/**************************************************************************/
-/* app_delegate.h */
+/* app_delegate_service.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
@@ -32,18 +32,11 @@
#import
-@class ViewController;
+@class GDTViewController;
-// FIXME: Add support for both OpenGL and Vulkan when OpenGL is implemented again,
-// so it can't be done with compilation time branching.
-//#if defined(GLES3_ENABLED)
-//@interface AppDelegate : NSObject {
-//#endif
-//#if defined(VULKAN_ENABLED)
-@interface AppDelegate : NSObject
-//#endif
+@interface GDTAppDelegateService : NSObject
@property(strong, nonatomic) UIWindow *window;
-@property(strong, class, readonly, nonatomic) ViewController *viewController;
+@property(strong, class, readonly, nonatomic) GDTViewController *viewController;
@end
diff --git a/platform/ios/app_delegate.mm b/drivers/apple_embedded/app_delegate_service.mm
similarity index 88%
rename from platform/ios/app_delegate.mm
rename to drivers/apple_embedded/app_delegate_service.mm
index 37d269643488..07b52b9d9c66 100644
--- a/platform/ios/app_delegate.mm
+++ b/drivers/apple_embedded/app_delegate_service.mm
@@ -1,5 +1,5 @@
/**************************************************************************/
-/* app_delegate.mm */
+/* app_delegate_service.mm */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
@@ -28,10 +28,10 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
-#import "app_delegate.h"
+#import "app_delegate_service.h"
-#import "godot_view.h"
-#import "os_ios.h"
+#import "godot_view_apple_embedded.h"
+#import "os_apple_embedded.h"
#import "view_controller.h"
#include "core/config/project_settings.h"
@@ -46,10 +46,10 @@
extern int gargc;
extern char **gargv;
-extern int ios_main(int, char **);
-extern void ios_finish();
+extern int apple_embedded_main(int, char **);
+extern void apple_embedded_finish();
-@implementation AppDelegate
+@implementation GDTAppDelegateService
enum {
SESSION_CATEGORY_AMBIENT,
@@ -60,9 +60,9 @@ @implementation AppDelegate
SESSION_CATEGORY_SOLO_AMBIENT
};
-static ViewController *mainViewController = nil;
+static GDTViewController *mainViewController = nil;
-+ (ViewController *)viewController {
++ (GDTViewController *)viewController {
return mainViewController;
}
@@ -71,12 +71,15 @@ - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(
// TODO: logo screen is not displayed while shaders are compiling
// DummyViewController(Splash/LoadingViewController) -> setup -> GodotViewController
- CGRect windowBounds = [[UIScreen mainScreen] bounds];
-
+#if !defined(VISIONOS_ENABLED)
// Create a full-screen window
+ CGRect windowBounds = [[UIScreen mainScreen] bounds];
self.window = [[UIWindow alloc] initWithFrame:windowBounds];
+#else
+ self.window = [[UIWindow alloc] init];
+#endif
- int err = ios_main(gargc, gargv);
+ int err = apple_embedded_main(gargc, gargv);
if (err != 0) {
// bail, things did not go very well for us, should probably output a message on screen with our error code...
@@ -84,7 +87,7 @@ - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(
return NO;
}
- ViewController *viewController = [[ViewController alloc] init];
+ GDTViewController *viewController = [[GDTViewController alloc] init];
viewController.godotView.useCADisplayLink = bool(GLOBAL_DEF("display.iOS/use_cadisplaylink", true)) ? YES : NO;
viewController.godotView.renderingInterval = 1.0 / kRenderingFrequency;
@@ -135,10 +138,10 @@ - (void)onAudioInterruption:(NSNotification *)notification {
if ([notification.name isEqualToString:AVAudioSessionInterruptionNotification]) {
if ([[notification.userInfo valueForKey:AVAudioSessionInterruptionTypeKey] isEqualToNumber:[NSNumber numberWithInt:AVAudioSessionInterruptionTypeBegan]]) {
NSLog(@"Audio interruption began");
- OS_IOS::get_singleton()->on_focus_out();
+ OS_AppleEmbedded::get_singleton()->on_focus_out();
} else if ([[notification.userInfo valueForKey:AVAudioSessionInterruptionTypeKey] isEqualToNumber:[NSNumber numberWithInt:AVAudioSessionInterruptionTypeEnded]]) {
NSLog(@"Audio interruption ended");
- OS_IOS::get_singleton()->on_focus_in();
+ OS_AppleEmbedded::get_singleton()->on_focus_in();
}
}
}
@@ -150,7 +153,7 @@ - (void)applicationDidReceiveMemoryWarning:(UIApplication *)application {
}
- (void)applicationWillTerminate:(UIApplication *)application {
- ios_finish();
+ apple_embedded_finish();
}
// When application goes to background (e.g. user switches to another app or presses Home),
@@ -164,19 +167,19 @@ - (void)applicationWillTerminate:(UIApplication *)application {
// notification panel by swiping from the upper part of the screen.
- (void)applicationWillResignActive:(UIApplication *)application {
- OS_IOS::get_singleton()->on_focus_out();
+ OS_AppleEmbedded::get_singleton()->on_focus_out();
}
- (void)applicationDidBecomeActive:(UIApplication *)application {
- OS_IOS::get_singleton()->on_focus_in();
+ OS_AppleEmbedded::get_singleton()->on_focus_in();
}
- (void)applicationDidEnterBackground:(UIApplication *)application {
- OS_IOS::get_singleton()->on_enter_background();
+ OS_AppleEmbedded::get_singleton()->on_enter_background();
}
- (void)applicationWillEnterForeground:(UIApplication *)application {
- OS_IOS::get_singleton()->on_exit_background();
+ OS_AppleEmbedded::get_singleton()->on_exit_background();
}
- (void)dealloc {
diff --git a/drivers/apple_embedded/apple_embedded.h b/drivers/apple_embedded/apple_embedded.h
new file mode 100644
index 000000000000..f06d5796baa8
--- /dev/null
+++ b/drivers/apple_embedded/apple_embedded.h
@@ -0,0 +1,59 @@
+/**************************************************************************/
+/* apple_embedded.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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. */
+/**************************************************************************/
+
+#pragma once
+
+#include "core/object/class_db.h"
+
+#import
+
+class AppleEmbedded : public Object {
+ GDCLASS(AppleEmbedded, Object);
+
+ static void _bind_methods();
+
+private:
+ CHHapticEngine *haptic_engine API_AVAILABLE(ios(13)) = nullptr;
+
+ CHHapticEngine *get_haptic_engine_instance() API_AVAILABLE(ios(13));
+ void start_haptic_engine();
+ void stop_haptic_engine();
+
+public:
+ static void alert(const char *p_alert, const char *p_title);
+
+ bool supports_haptic_engine();
+ void vibrate_haptic_engine(float p_duration_seconds, float p_amplitude);
+
+ String get_model() const;
+ String get_rate_url(int p_app_id) const;
+
+ AppleEmbedded();
+};
diff --git a/platform/ios/ios.mm b/drivers/apple_embedded/apple_embedded.mm
similarity index 82%
rename from platform/ios/ios.mm
rename to drivers/apple_embedded/apple_embedded.mm
index e257b506a946..33d23061f071 100644
--- a/platform/ios/ios.mm
+++ b/drivers/apple_embedded/apple_embedded.mm
@@ -1,5 +1,5 @@
/**************************************************************************/
-/* ios.mm */
+/* apple_embedded.mm */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
@@ -28,23 +28,23 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
-#import "ios.h"
+#import "apple_embedded.h"
-#import "app_delegate.h"
+#import "app_delegate_service.h"
#import "view_controller.h"
#import
#import
#include
-void iOS::_bind_methods() {
- ClassDB::bind_method(D_METHOD("get_rate_url", "app_id"), &iOS::get_rate_url);
- ClassDB::bind_method(D_METHOD("supports_haptic_engine"), &iOS::supports_haptic_engine);
- ClassDB::bind_method(D_METHOD("start_haptic_engine"), &iOS::start_haptic_engine);
- ClassDB::bind_method(D_METHOD("stop_haptic_engine"), &iOS::stop_haptic_engine);
+void AppleEmbedded::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("get_rate_url", "app_id"), &AppleEmbedded::get_rate_url);
+ ClassDB::bind_method(D_METHOD("supports_haptic_engine"), &AppleEmbedded::supports_haptic_engine);
+ ClassDB::bind_method(D_METHOD("start_haptic_engine"), &AppleEmbedded::start_haptic_engine);
+ ClassDB::bind_method(D_METHOD("stop_haptic_engine"), &AppleEmbedded::stop_haptic_engine);
}
-bool iOS::supports_haptic_engine() {
+bool AppleEmbedded::supports_haptic_engine() {
if (@available(iOS 13, *)) {
id capabilities = [CHHapticEngine capabilitiesForHardware];
return capabilities.supportsHaptics;
@@ -53,7 +53,7 @@
return false;
}
-CHHapticEngine *iOS::get_haptic_engine_instance() API_AVAILABLE(ios(13)) {
+CHHapticEngine *AppleEmbedded::get_haptic_engine_instance() API_AVAILABLE(ios(13)) {
if (haptic_engine == nullptr) {
NSError *error = nullptr;
haptic_engine = [[CHHapticEngine alloc] initAndReturnError:&error];
@@ -69,7 +69,7 @@
return haptic_engine;
}
-void iOS::vibrate_haptic_engine(float p_duration_seconds, float p_amplitude) API_AVAILABLE(ios(13)) {
+void AppleEmbedded::vibrate_haptic_engine(float p_duration_seconds, float p_amplitude) API_AVAILABLE(ios(13)) {
if (@available(iOS 13, *)) { // We need the @available check every time to make the compiler happy...
if (supports_haptic_engine()) {
CHHapticEngine *cur_haptic_engine = get_haptic_engine_instance();
@@ -117,10 +117,10 @@
}
}
- NSLog(@"Haptic engine is not supported in this version of iOS");
+ NSLog(@"Haptic engine is not supported");
}
-void iOS::start_haptic_engine() {
+void AppleEmbedded::start_haptic_engine() {
if (@available(iOS 13, *)) {
if (supports_haptic_engine()) {
CHHapticEngine *cur_haptic_engine = get_haptic_engine_instance();
@@ -136,10 +136,10 @@
}
}
- NSLog(@"Haptic engine is not supported in this version of iOS");
+ NSLog(@"Haptic engine is not supported");
}
-void iOS::stop_haptic_engine() {
+void AppleEmbedded::stop_haptic_engine() {
if (@available(iOS 13, *)) {
if (supports_haptic_engine()) {
CHHapticEngine *cur_haptic_engine = get_haptic_engine_instance();
@@ -155,10 +155,10 @@
}
}
- NSLog(@"Haptic engine is not supported in this version of iOS");
+ NSLog(@"Haptic engine is not supported");
}
-void iOS::alert(const char *p_alert, const char *p_title) {
+void AppleEmbedded::alert(const char *p_alert, const char *p_title) {
NSString *title = [NSString stringWithUTF8String:p_title];
NSString *message = [NSString stringWithUTF8String:p_alert];
@@ -170,10 +170,10 @@
[alert addAction:button];
- [AppDelegate.viewController presentViewController:alert animated:YES completion:nil];
+ [GDTAppDelegateService.viewController presentViewController:alert animated:YES completion:nil];
}
-String iOS::get_model() const {
+String AppleEmbedded::get_model() const {
// [[UIDevice currentDevice] model] only returns "iPad" or "iPhone".
size_t size;
sysctlbyname("hw.machine", nullptr, &size, nullptr, 0);
@@ -188,7 +188,7 @@
return String::utf8(str != nullptr ? str : "");
}
-String iOS::get_rate_url(int p_app_id) const {
+String AppleEmbedded::get_rate_url(int p_app_id) const {
String app_url_path = "itms-apps://itunes.apple.com/app/idAPP_ID";
String ret = app_url_path.replace("APP_ID", String::num_int64(p_app_id));
@@ -197,4 +197,4 @@
return ret;
}
-iOS::iOS() {}
+AppleEmbedded::AppleEmbedded() {}
diff --git a/drivers/apple_embedded/display_layer_apple_embedded.h b/drivers/apple_embedded/display_layer_apple_embedded.h
new file mode 100644
index 000000000000..552b115a0843
--- /dev/null
+++ b/drivers/apple_embedded/display_layer_apple_embedded.h
@@ -0,0 +1,42 @@
+/**************************************************************************/
+/* display_layer_apple_embedded.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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. */
+/**************************************************************************/
+
+#pragma once
+
+#import
+
+@protocol GDTDisplayLayer
+
+- (void)startRenderDisplayLayer;
+- (void)stopRenderDisplayLayer;
+- (void)initializeDisplayLayer;
+- (void)layoutDisplayLayer;
+
+@end
diff --git a/drivers/apple_embedded/display_server_apple_embedded.h b/drivers/apple_embedded/display_server_apple_embedded.h
new file mode 100644
index 000000000000..df7bcd9b345f
--- /dev/null
+++ b/drivers/apple_embedded/display_server_apple_embedded.h
@@ -0,0 +1,233 @@
+/**************************************************************************/
+/* display_server_apple_embedded.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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. */
+/**************************************************************************/
+
+#pragma once
+
+#include "core/input/input.h"
+#include "servers/display_server.h"
+
+#if defined(RD_ENABLED)
+#include "servers/rendering/renderer_rd/renderer_compositor_rd.h"
+#include "servers/rendering/rendering_device.h"
+
+#if defined(VULKAN_ENABLED)
+#import "rendering_context_driver_vulkan_apple_embedded.h"
+
+#include "drivers/vulkan/godot_vulkan.h"
+#endif // VULKAN_ENABLED
+
+#if defined(METAL_ENABLED)
+#import "drivers/metal/rendering_context_driver_metal.h"
+#endif // METAL_ENABLED
+#endif // RD_ENABLED
+
+#if defined(GLES3_ENABLED)
+#include "drivers/gles3/rasterizer_gles3.h"
+#endif // GLES3_ENABLED
+
+#import
+#import
+
+class DisplayServerAppleEmbedded : public DisplayServer {
+ GDSOFTCLASS(DisplayServerAppleEmbedded, DisplayServer);
+
+ _THREAD_SAFE_CLASS_
+
+#if defined(RD_ENABLED)
+ RenderingContextDriver *rendering_context = nullptr;
+ RenderingDevice *rendering_device = nullptr;
+#endif
+ NativeMenu *native_menu = nullptr;
+
+ id tts = nullptr;
+
+ DisplayServer::ScreenOrientation screen_orientation;
+
+ ObjectID window_attached_instance_id;
+
+ Callable window_event_callback;
+ Callable window_resize_callback;
+ Callable input_event_callback;
+ Callable input_text_callback;
+
+ Callable system_theme_changed;
+
+ int virtual_keyboard_height = 0;
+
+ void perform_event(const Ref &p_event);
+
+ void initialize_tts() const;
+
+protected:
+ DisplayServerAppleEmbedded(const String &p_rendering_driver, DisplayServer::WindowMode p_mode, DisplayServer::VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i *p_position, const Vector2i &p_resolution, int p_screen, Context p_context, int64_t p_parent_window, Error &r_error);
+ ~DisplayServerAppleEmbedded();
+
+public:
+ String rendering_driver;
+
+ static DisplayServerAppleEmbedded *get_singleton();
+
+ static Vector get_rendering_drivers_func();
+
+ // MARK: - Events
+
+ virtual void process_events() override;
+
+ virtual void window_set_rect_changed_callback(const Callable &p_callable, WindowID p_window = MAIN_WINDOW_ID) override;
+ virtual void window_set_window_event_callback(const Callable &p_callable, WindowID p_window = MAIN_WINDOW_ID) override;
+ virtual void window_set_input_event_callback(const Callable &p_callable, WindowID p_window = MAIN_WINDOW_ID) override;
+ virtual void window_set_input_text_callback(const Callable &p_callable, WindowID p_window = MAIN_WINDOW_ID) override;
+ virtual void window_set_drop_files_callback(const Callable &p_callable, WindowID p_window = MAIN_WINDOW_ID) override;
+
+ static void _dispatch_input_events(const Ref &p_event);
+ void send_input_event(const Ref &p_event) const;
+ void send_input_text(const String &p_text) const;
+ void send_window_event(DisplayServer::WindowEvent p_event) const;
+ void _window_callback(const Callable &p_callable, const Variant &p_arg) const;
+
+ void emit_system_theme_changed();
+
+ // MARK: - Input
+
+ // MARK: Touches and Apple Pencil
+
+ void touch_press(int p_idx, int p_x, int p_y, bool p_pressed, bool p_double_click);
+ void touch_drag(int p_idx, int p_prev_x, int p_prev_y, int p_x, int p_y, float p_pressure, Vector2 p_tilt);
+ void touches_canceled(int p_idx);
+
+ // MARK: Keyboard
+
+ void key(Key p_key, char32_t p_char, Key p_unshifted, Key p_physical, NSInteger p_modifier, bool p_pressed, KeyLocation p_location);
+ bool is_keyboard_active() const;
+
+ // MARK: Motion
+
+ void update_gravity(const Vector3 &p_gravity);
+ void update_accelerometer(const Vector3 &p_accelerometer);
+ void update_magnetometer(const Vector3 &p_magnetometer);
+ void update_gyroscope(const Vector3 &p_gyroscope);
+
+ // MARK: -
+
+ virtual bool has_feature(Feature p_feature) const override;
+
+ virtual bool tts_is_speaking() const override;
+ virtual bool tts_is_paused() const override;
+ virtual TypedArray tts_get_voices() const override;
+
+ virtual void tts_speak(const String &p_text, const String &p_voice, int p_volume = 50, float p_pitch = 1.f, float p_rate = 1.f, int p_utterance_id = 0, bool p_interrupt = false) override;
+ virtual void tts_pause() override;
+ virtual void tts_resume() override;
+ virtual void tts_stop() override;
+
+ virtual bool is_dark_mode_supported() const override;
+ virtual bool is_dark_mode() const override;
+ virtual void set_system_theme_change_callback(const Callable &p_callable) override;
+
+ virtual Rect2i get_display_safe_area() const override;
+
+ virtual int get_screen_count() const override;
+ virtual int get_primary_screen() const override;
+ virtual Point2i screen_get_position(int p_screen = SCREEN_OF_MAIN_WINDOW) const override;
+ virtual Size2i screen_get_size(int p_screen = SCREEN_OF_MAIN_WINDOW) const override;
+ virtual Rect2i screen_get_usable_rect(int p_screen = SCREEN_OF_MAIN_WINDOW) const override;
+
+ virtual Vector get_window_list() const override;
+
+ virtual WindowID get_window_at_screen_position(const Point2i &p_position) const override;
+
+ virtual int64_t window_get_native_handle(HandleType p_handle_type, WindowID p_window = MAIN_WINDOW_ID) const override;
+
+ virtual void window_attach_instance_id(ObjectID p_instance, WindowID p_window = MAIN_WINDOW_ID) override;
+ virtual ObjectID window_get_attached_instance_id(WindowID p_window = MAIN_WINDOW_ID) const override;
+
+ virtual void window_set_title(const String &p_title, WindowID p_window = MAIN_WINDOW_ID) override;
+
+ virtual int window_get_current_screen(WindowID p_window = MAIN_WINDOW_ID) const override;
+ virtual void window_set_current_screen(int p_screen, WindowID p_window = MAIN_WINDOW_ID) override;
+
+ virtual Point2i window_get_position(WindowID p_window = MAIN_WINDOW_ID) const override;
+ virtual Point2i window_get_position_with_decorations(WindowID p_window = MAIN_WINDOW_ID) const override;
+ virtual void window_set_position(const Point2i &p_position, WindowID p_window = MAIN_WINDOW_ID) override;
+
+ virtual void window_set_transient(WindowID p_window, WindowID p_parent) override;
+
+ virtual void window_set_max_size(const Size2i p_size, WindowID p_window = MAIN_WINDOW_ID) override;
+ virtual Size2i window_get_max_size(WindowID p_window = MAIN_WINDOW_ID) const override;
+
+ virtual void window_set_min_size(const Size2i p_size, WindowID p_window = MAIN_WINDOW_ID) override;
+ virtual Size2i window_get_min_size(WindowID p_window = MAIN_WINDOW_ID) const override;
+
+ virtual void window_set_size(const Size2i p_size, WindowID p_window = MAIN_WINDOW_ID) override;
+ virtual Size2i window_get_size(WindowID p_window = MAIN_WINDOW_ID) const override;
+ virtual Size2i window_get_size_with_decorations(WindowID p_window = MAIN_WINDOW_ID) const override;
+
+ virtual void window_set_mode(WindowMode p_mode, WindowID p_window = MAIN_WINDOW_ID) override;
+ virtual WindowMode window_get_mode(WindowID p_window = MAIN_WINDOW_ID) const override;
+
+ virtual bool window_is_maximize_allowed(WindowID p_window = MAIN_WINDOW_ID) const override;
+
+ virtual void window_set_flag(WindowFlags p_flag, bool p_enabled, WindowID p_window = MAIN_WINDOW_ID) override;
+ virtual bool window_get_flag(WindowFlags p_flag, WindowID p_window = MAIN_WINDOW_ID) const override;
+
+ virtual void window_request_attention(WindowID p_window = MAIN_WINDOW_ID) override;
+ virtual void window_move_to_foreground(WindowID p_window = MAIN_WINDOW_ID) override;
+ virtual bool window_is_focused(WindowID p_window = MAIN_WINDOW_ID) const override;
+
+ virtual float screen_get_max_scale() const override;
+
+ virtual void screen_set_orientation(DisplayServer::ScreenOrientation p_orientation, int p_screen) override;
+ virtual DisplayServer::ScreenOrientation screen_get_orientation(int p_screen) const override;
+
+ virtual bool window_can_draw(WindowID p_window = MAIN_WINDOW_ID) const override;
+
+ virtual bool can_any_window_draw() const override;
+
+ virtual void window_set_vsync_mode(DisplayServer::VSyncMode p_vsync_mode, WindowID p_window = MAIN_WINDOW_ID) override;
+ virtual DisplayServer::VSyncMode window_get_vsync_mode(WindowID p_vsync_mode) const override;
+
+ virtual bool is_touchscreen_available() const override;
+
+ virtual void virtual_keyboard_show(const String &p_existing_text, const Rect2 &p_screen_rect, VirtualKeyboardType p_type, int p_max_length, int p_cursor_start, int p_cursor_end) override;
+ virtual void virtual_keyboard_hide() override;
+
+ void virtual_keyboard_set_height(int height);
+ virtual int virtual_keyboard_get_height() const override;
+ virtual bool has_hardware_keyboard() const override;
+
+ virtual void clipboard_set(const String &p_text) override;
+ virtual String clipboard_get() const override;
+
+ virtual void screen_set_keep_on(bool p_enable) override;
+ virtual bool screen_is_kept_on() const override;
+
+ void resize_window(CGSize size);
+ virtual void swap_buffers() override {}
+};
diff --git a/drivers/apple_embedded/display_server_apple_embedded.mm b/drivers/apple_embedded/display_server_apple_embedded.mm
new file mode 100644
index 000000000000..3f13b23b9ffa
--- /dev/null
+++ b/drivers/apple_embedded/display_server_apple_embedded.mm
@@ -0,0 +1,794 @@
+/**************************************************************************/
+/* display_server_apple_embedded.mm */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 "display_server_apple_embedded.h"
+
+#import "app_delegate_service.h"
+#import "apple_embedded.h"
+#import "godot_view_apple_embedded.h"
+#import "key_mapping_apple_embedded.h"
+#import "keyboard_input_view.h"
+#import "os_apple_embedded.h"
+#import "tts_apple_embedded.h"
+#import "view_controller.h"
+
+#include "core/config/project_settings.h"
+#include "core/io/file_access_pack.h"
+
+#import
+
+static const float kDisplayServerIOSAcceleration = 1.f;
+
+DisplayServerAppleEmbedded *DisplayServerAppleEmbedded::get_singleton() {
+ return (DisplayServerAppleEmbedded *)DisplayServer::get_singleton();
+}
+
+DisplayServerAppleEmbedded::DisplayServerAppleEmbedded(const String &p_rendering_driver, WindowMode p_mode, DisplayServer::VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i *p_position, const Vector2i &p_resolution, int p_screen, Context p_context, int64_t p_parent_window, Error &r_error) {
+ KeyMappingAppleEmbedded::initialize();
+
+ rendering_driver = p_rendering_driver;
+
+ // Init TTS
+ bool tts_enabled = GLOBAL_GET("audio/general/text_to_speech");
+ if (tts_enabled) {
+ initialize_tts();
+ }
+ native_menu = memnew(NativeMenu);
+
+ bool has_made_render_compositor_current = false;
+
+#if defined(RD_ENABLED)
+ rendering_context = nullptr;
+ rendering_device = nullptr;
+
+ CALayer *layer = nullptr;
+
+ union {
+#ifdef VULKAN_ENABLED
+ RenderingContextDriverVulkanAppleEmbedded::WindowPlatformData vulkan;
+#endif
+#ifdef METAL_ENABLED
+ GODOT_CLANG_WARNING_PUSH_AND_IGNORE("-Wunguarded-availability")
+ // Eliminate "RenderingContextDriverMetal is only available on iOS 14.0 or newer".
+ RenderingContextDriverMetal::WindowPlatformData metal;
+ GODOT_CLANG_WARNING_POP
+#endif
+ } wpd;
+
+#if defined(VULKAN_ENABLED)
+ if (rendering_driver == "vulkan") {
+ layer = [GDTAppDelegateService.viewController.godotView initializeRenderingForDriver:@"vulkan"];
+ if (!layer) {
+ ERR_FAIL_MSG("Failed to create iOS Vulkan rendering layer.");
+ }
+ wpd.vulkan.layer_ptr = (CAMetalLayer *const *)&layer;
+ rendering_context = memnew(RenderingContextDriverVulkanAppleEmbedded);
+ }
+#endif
+#ifdef METAL_ENABLED
+ if (rendering_driver == "metal") {
+ if (@available(iOS 14.0, *)) {
+ layer = [GDTAppDelegateService.viewController.godotView initializeRenderingForDriver:@"metal"];
+ wpd.metal.layer = (CAMetalLayer *)layer;
+ rendering_context = memnew(RenderingContextDriverMetal);
+ } else {
+ OS::get_singleton()->alert("Metal is only supported on iOS 14.0 and later.");
+ r_error = ERR_UNAVAILABLE;
+ return;
+ }
+ }
+#endif
+ if (rendering_context) {
+ if (rendering_context->initialize() != OK) {
+ memdelete(rendering_context);
+ rendering_context = nullptr;
+#if defined(GLES3_ENABLED)
+ bool fallback_to_opengl3 = GLOBAL_GET("rendering/rendering_device/fallback_to_opengl3");
+ if (fallback_to_opengl3 && rendering_driver != "opengl3") {
+ WARN_PRINT("Your device seem not to support MoltenVK or Metal, switching to OpenGL 3.");
+ rendering_driver = "opengl3";
+ OS::get_singleton()->set_current_rendering_method("gl_compatibility");
+ OS::get_singleton()->set_current_rendering_driver_name(rendering_driver);
+ } else
+#endif
+ {
+ ERR_PRINT(vformat("Failed to initialize %s context", rendering_driver));
+ r_error = ERR_UNAVAILABLE;
+ return;
+ }
+ }
+ }
+
+ if (rendering_context) {
+ if (rendering_context->window_create(MAIN_WINDOW_ID, &wpd) != OK) {
+ ERR_PRINT(vformat("Failed to create %s window.", rendering_driver));
+ memdelete(rendering_context);
+ rendering_context = nullptr;
+ r_error = ERR_UNAVAILABLE;
+ return;
+ }
+
+ Size2i size = Size2i(layer.bounds.size.width, layer.bounds.size.height) * screen_get_max_scale();
+ rendering_context->window_set_size(MAIN_WINDOW_ID, size.width, size.height);
+ rendering_context->window_set_vsync_mode(MAIN_WINDOW_ID, p_vsync_mode);
+
+ rendering_device = memnew(RenderingDevice);
+ if (rendering_device->initialize(rendering_context, MAIN_WINDOW_ID) != OK) {
+ rendering_device = nullptr;
+ memdelete(rendering_context);
+ rendering_context = nullptr;
+ r_error = ERR_UNAVAILABLE;
+ return;
+ }
+ rendering_device->screen_create(MAIN_WINDOW_ID);
+
+ RendererCompositorRD::make_current();
+ has_made_render_compositor_current = true;
+ }
+#endif
+
+#if defined(GLES3_ENABLED)
+ if (rendering_driver == "opengl3") {
+ CALayer *layer = [GDTAppDelegateService.viewController.godotView initializeRenderingForDriver:@"opengl3"];
+
+ if (!layer) {
+ ERR_FAIL_MSG("Failed to create iOS OpenGLES rendering layer.");
+ }
+
+ RasterizerGLES3::make_current(false);
+ has_made_render_compositor_current = true;
+ }
+#endif
+
+ ERR_FAIL_COND_MSG(!has_made_render_compositor_current, vformat("Failed to make RendererCompositor current for rendering driver %s", rendering_driver));
+
+ bool keep_screen_on = bool(GLOBAL_GET("display/window/energy_saving/keep_screen_on"));
+ screen_set_keep_on(keep_screen_on);
+
+ Input::get_singleton()->set_event_dispatch_function(_dispatch_input_events);
+
+ r_error = OK;
+}
+
+DisplayServerAppleEmbedded::~DisplayServerAppleEmbedded() {
+ if (native_menu) {
+ memdelete(native_menu);
+ native_menu = nullptr;
+ }
+
+#if defined(RD_ENABLED)
+ if (rendering_device) {
+ rendering_device->screen_free(MAIN_WINDOW_ID);
+ memdelete(rendering_device);
+ rendering_device = nullptr;
+ }
+
+ if (rendering_context) {
+ rendering_context->window_destroy(MAIN_WINDOW_ID);
+ memdelete(rendering_context);
+ rendering_context = nullptr;
+ }
+#endif
+}
+
+Vector DisplayServerAppleEmbedded::get_rendering_drivers_func() {
+ Vector drivers;
+
+#if defined(VULKAN_ENABLED)
+ drivers.push_back("vulkan");
+#endif
+#if defined(METAL_ENABLED)
+ if (@available(ios 14.0, *)) {
+ drivers.push_back("metal");
+ }
+#endif
+#if defined(GLES3_ENABLED)
+ drivers.push_back("opengl3");
+#endif
+
+ return drivers;
+}
+
+// MARK: Events
+
+void DisplayServerAppleEmbedded::window_set_rect_changed_callback(const Callable &p_callable, WindowID p_window) {
+ window_resize_callback = p_callable;
+}
+
+void DisplayServerAppleEmbedded::window_set_window_event_callback(const Callable &p_callable, WindowID p_window) {
+ window_event_callback = p_callable;
+}
+void DisplayServerAppleEmbedded::window_set_input_event_callback(const Callable &p_callable, WindowID p_window) {
+ input_event_callback = p_callable;
+}
+
+void DisplayServerAppleEmbedded::window_set_input_text_callback(const Callable &p_callable, WindowID p_window) {
+ input_text_callback = p_callable;
+}
+
+void DisplayServerAppleEmbedded::window_set_drop_files_callback(const Callable &p_callable, WindowID p_window) {
+ // Probably not supported for iOS
+}
+
+void DisplayServerAppleEmbedded::process_events() {
+ Input::get_singleton()->flush_buffered_events();
+}
+
+void DisplayServerAppleEmbedded::_dispatch_input_events(const Ref &p_event) {
+ DisplayServerAppleEmbedded::get_singleton()->send_input_event(p_event);
+}
+
+void DisplayServerAppleEmbedded::send_input_event(const Ref &p_event) const {
+ _window_callback(input_event_callback, p_event);
+}
+
+void DisplayServerAppleEmbedded::send_input_text(const String &p_text) const {
+ _window_callback(input_text_callback, p_text);
+}
+
+void DisplayServerAppleEmbedded::send_window_event(DisplayServer::WindowEvent p_event) const {
+ _window_callback(window_event_callback, int(p_event));
+}
+
+void DisplayServerAppleEmbedded::_window_callback(const Callable &p_callable, const Variant &p_arg) const {
+ if (p_callable.is_valid()) {
+ p_callable.call(p_arg);
+ }
+}
+
+// MARK: - Input
+
+// MARK: Touches
+
+void DisplayServerAppleEmbedded::touch_press(int p_idx, int p_x, int p_y, bool p_pressed, bool p_double_click) {
+ Ref ev;
+ ev.instantiate();
+
+ ev->set_index(p_idx);
+ ev->set_pressed(p_pressed);
+ ev->set_position(Vector2(p_x, p_y));
+ ev->set_double_tap(p_double_click);
+ perform_event(ev);
+}
+
+void DisplayServerAppleEmbedded::touch_drag(int p_idx, int p_prev_x, int p_prev_y, int p_x, int p_y, float p_pressure, Vector2 p_tilt) {
+ Ref ev;
+ ev.instantiate();
+ ev->set_index(p_idx);
+ ev->set_pressure(p_pressure);
+ ev->set_tilt(p_tilt);
+ ev->set_position(Vector2(p_x, p_y));
+ ev->set_relative(Vector2(p_x - p_prev_x, p_y - p_prev_y));
+ ev->set_relative_screen_position(ev->get_relative());
+ perform_event(ev);
+}
+
+void DisplayServerAppleEmbedded::perform_event(const Ref &p_event) {
+ Input::get_singleton()->parse_input_event(p_event);
+}
+
+void DisplayServerAppleEmbedded::touches_canceled(int p_idx) {
+ touch_press(p_idx, -1, -1, false, false);
+}
+
+// MARK: Keyboard
+
+void DisplayServerAppleEmbedded::key(Key p_key, char32_t p_char, Key p_unshifted, Key p_physical, NSInteger p_modifier, bool p_pressed, KeyLocation p_location) {
+ Ref ev;
+ ev.instantiate();
+ ev->set_echo(false);
+ ev->set_pressed(p_pressed);
+ ev->set_keycode(fix_keycode(p_char, p_key));
+ if (@available(iOS 13.4, *)) {
+ if (p_key != Key::SHIFT) {
+ ev->set_shift_pressed(p_modifier & UIKeyModifierShift);
+ }
+ if (p_key != Key::CTRL) {
+ ev->set_ctrl_pressed(p_modifier & UIKeyModifierControl);
+ }
+ if (p_key != Key::ALT) {
+ ev->set_alt_pressed(p_modifier & UIKeyModifierAlternate);
+ }
+ if (p_key != Key::META) {
+ ev->set_meta_pressed(p_modifier & UIKeyModifierCommand);
+ }
+ }
+ ev->set_key_label(p_unshifted);
+ ev->set_physical_keycode(p_physical);
+ ev->set_unicode(fix_unicode(p_char));
+ ev->set_location(p_location);
+ perform_event(ev);
+}
+
+// MARK: Motion
+
+void DisplayServerAppleEmbedded::update_gravity(const Vector3 &p_gravity) {
+ Input::get_singleton()->set_gravity(p_gravity);
+}
+
+void DisplayServerAppleEmbedded::update_accelerometer(const Vector3 &p_accelerometer) {
+ Input::get_singleton()->set_accelerometer(p_accelerometer / kDisplayServerIOSAcceleration);
+}
+
+void DisplayServerAppleEmbedded::update_magnetometer(const Vector3 &p_magnetometer) {
+ Input::get_singleton()->set_magnetometer(p_magnetometer);
+}
+
+void DisplayServerAppleEmbedded::update_gyroscope(const Vector3 &p_gyroscope) {
+ Input::get_singleton()->set_gyroscope(p_gyroscope);
+}
+
+// MARK: -
+
+bool DisplayServerAppleEmbedded::has_feature(Feature p_feature) const {
+ switch (p_feature) {
+#ifndef DISABLE_DEPRECATED
+ case FEATURE_GLOBAL_MENU: {
+ return (native_menu && native_menu->has_feature(NativeMenu::FEATURE_GLOBAL_MENU));
+ } break;
+#endif
+ // case FEATURE_CURSOR_SHAPE:
+ // case FEATURE_CUSTOM_CURSOR_SHAPE:
+ // case FEATURE_HIDPI:
+ // case FEATURE_ICON:
+ // case FEATURE_IME:
+ // case FEATURE_MOUSE:
+ // case FEATURE_MOUSE_WARP:
+ // case FEATURE_NATIVE_DIALOG:
+ // case FEATURE_NATIVE_DIALOG_INPUT:
+ // case FEATURE_NATIVE_DIALOG_FILE:
+ // case FEATURE_NATIVE_DIALOG_FILE_EXTRA:
+ // case FEATURE_NATIVE_DIALOG_FILE_MIME:
+ // case FEATURE_NATIVE_ICON:
+ // case FEATURE_WINDOW_TRANSPARENCY:
+ case FEATURE_CLIPBOARD:
+ case FEATURE_KEEP_SCREEN_ON:
+ case FEATURE_ORIENTATION:
+ case FEATURE_TOUCHSCREEN:
+ case FEATURE_VIRTUAL_KEYBOARD:
+ case FEATURE_TEXT_TO_SPEECH:
+ return true;
+ default:
+ return false;
+ }
+}
+
+void DisplayServerAppleEmbedded::initialize_tts() const {
+ const_cast(this)->tts = [[GDTTTS alloc] init];
+}
+
+bool DisplayServerAppleEmbedded::tts_is_speaking() const {
+ if (unlikely(!tts)) {
+ initialize_tts();
+ }
+ ERR_FAIL_NULL_V(tts, false);
+ return [tts isSpeaking];
+}
+
+bool DisplayServerAppleEmbedded::tts_is_paused() const {
+ if (unlikely(!tts)) {
+ initialize_tts();
+ }
+ ERR_FAIL_NULL_V(tts, false);
+ return [tts isPaused];
+}
+
+TypedArray DisplayServerAppleEmbedded::tts_get_voices() const {
+ if (unlikely(!tts)) {
+ initialize_tts();
+ }
+ ERR_FAIL_NULL_V(tts, TypedArray());
+ return [tts getVoices];
+}
+
+void DisplayServerAppleEmbedded::tts_speak(const String &p_text, const String &p_voice, int p_volume, float p_pitch, float p_rate, int p_utterance_id, bool p_interrupt) {
+ if (unlikely(!tts)) {
+ initialize_tts();
+ }
+ ERR_FAIL_NULL(tts);
+ [tts speak:p_text voice:p_voice volume:p_volume pitch:p_pitch rate:p_rate utterance_id:p_utterance_id interrupt:p_interrupt];
+}
+
+void DisplayServerAppleEmbedded::tts_pause() {
+ if (unlikely(!tts)) {
+ initialize_tts();
+ }
+ ERR_FAIL_NULL(tts);
+ [tts pauseSpeaking];
+}
+
+void DisplayServerAppleEmbedded::tts_resume() {
+ if (unlikely(!tts)) {
+ initialize_tts();
+ }
+ ERR_FAIL_NULL(tts);
+ [tts resumeSpeaking];
+}
+
+void DisplayServerAppleEmbedded::tts_stop() {
+ if (unlikely(!tts)) {
+ initialize_tts();
+ }
+ ERR_FAIL_NULL(tts);
+ [tts stopSpeaking];
+}
+
+bool DisplayServerAppleEmbedded::is_dark_mode_supported() const {
+ if (@available(iOS 13.0, *)) {
+ return true;
+ } else {
+ return false;
+ }
+}
+
+bool DisplayServerAppleEmbedded::is_dark_mode() const {
+ if (@available(iOS 13.0, *)) {
+ return [UITraitCollection currentTraitCollection].userInterfaceStyle == UIUserInterfaceStyleDark;
+ } else {
+ return false;
+ }
+}
+
+void DisplayServerAppleEmbedded::set_system_theme_change_callback(const Callable &p_callable) {
+ system_theme_changed = p_callable;
+}
+
+void DisplayServerAppleEmbedded::emit_system_theme_changed() {
+ if (system_theme_changed.is_valid()) {
+ Variant ret;
+ Callable::CallError ce;
+ system_theme_changed.callp(nullptr, 0, ret, ce);
+ if (ce.error != Callable::CallError::CALL_OK) {
+ ERR_PRINT(vformat("Failed to execute system theme changed callback: %s.", Variant::get_callable_error_text(system_theme_changed, nullptr, 0, ce)));
+ }
+ }
+}
+
+Rect2i DisplayServerAppleEmbedded::get_display_safe_area() const {
+ UIEdgeInsets insets = UIEdgeInsetsZero;
+ UIView *view = GDTAppDelegateService.viewController.godotView;
+ if ([view respondsToSelector:@selector(safeAreaInsets)]) {
+ insets = [view safeAreaInsets];
+ }
+ float scale = screen_get_scale();
+ Size2i insets_position = Size2i(insets.left, insets.top) * scale;
+ Size2i insets_size = Size2i(insets.left + insets.right, insets.top + insets.bottom) * scale;
+ return Rect2i(screen_get_position() + insets_position, screen_get_size() - insets_size);
+}
+
+int DisplayServerAppleEmbedded::get_screen_count() const {
+ return 1;
+}
+
+int DisplayServerAppleEmbedded::get_primary_screen() const {
+ return 0;
+}
+
+Point2i DisplayServerAppleEmbedded::screen_get_position(int p_screen) const {
+ return Size2i();
+}
+
+Size2i DisplayServerAppleEmbedded::screen_get_size(int p_screen) const {
+ CALayer *layer = GDTAppDelegateService.viewController.godotView.renderingLayer;
+
+ if (!layer) {
+ return Size2i();
+ }
+
+ return Size2i(layer.bounds.size.width, layer.bounds.size.height) * screen_get_scale(p_screen);
+}
+
+Rect2i DisplayServerAppleEmbedded::screen_get_usable_rect(int p_screen) const {
+ return Rect2i(screen_get_position(p_screen), screen_get_size(p_screen));
+}
+
+Vector DisplayServerAppleEmbedded::get_window_list() const {
+ Vector list;
+ list.push_back(MAIN_WINDOW_ID);
+ return list;
+}
+
+DisplayServer::WindowID DisplayServerAppleEmbedded::get_window_at_screen_position(const Point2i &p_position) const {
+ return MAIN_WINDOW_ID;
+}
+
+int64_t DisplayServerAppleEmbedded::window_get_native_handle(HandleType p_handle_type, WindowID p_window) const {
+ ERR_FAIL_COND_V(p_window != MAIN_WINDOW_ID, 0);
+ switch (p_handle_type) {
+ case DISPLAY_HANDLE: {
+ return 0; // Not supported.
+ }
+ case WINDOW_HANDLE: {
+ return (int64_t)GDTAppDelegateService.viewController;
+ }
+ case WINDOW_VIEW: {
+ return (int64_t)GDTAppDelegateService.viewController.godotView;
+ }
+ default: {
+ return 0;
+ }
+ }
+}
+
+void DisplayServerAppleEmbedded::window_attach_instance_id(ObjectID p_instance, WindowID p_window) {
+ window_attached_instance_id = p_instance;
+}
+
+ObjectID DisplayServerAppleEmbedded::window_get_attached_instance_id(WindowID p_window) const {
+ return window_attached_instance_id;
+}
+
+void DisplayServerAppleEmbedded::window_set_title(const String &p_title, WindowID p_window) {
+ // Probably not supported for iOS
+}
+
+int DisplayServerAppleEmbedded::window_get_current_screen(WindowID p_window) const {
+ return SCREEN_OF_MAIN_WINDOW;
+}
+
+void DisplayServerAppleEmbedded::window_set_current_screen(int p_screen, WindowID p_window) {
+ // Probably not supported for iOS
+}
+
+Point2i DisplayServerAppleEmbedded::window_get_position(WindowID p_window) const {
+ return Point2i();
+}
+
+Point2i DisplayServerAppleEmbedded::window_get_position_with_decorations(WindowID p_window) const {
+ return Point2i();
+}
+
+void DisplayServerAppleEmbedded::window_set_position(const Point2i &p_position, WindowID p_window) {
+ // Probably not supported for single window iOS app
+}
+
+void DisplayServerAppleEmbedded::window_set_transient(WindowID p_window, WindowID p_parent) {
+ // Probably not supported for iOS
+}
+
+void DisplayServerAppleEmbedded::window_set_max_size(const Size2i p_size, WindowID p_window) {
+ // Probably not supported for iOS
+}
+
+Size2i DisplayServerAppleEmbedded::window_get_max_size(WindowID p_window) const {
+ return Size2i();
+}
+
+void DisplayServerAppleEmbedded::window_set_min_size(const Size2i p_size, WindowID p_window) {
+ // Probably not supported for iOS
+}
+
+Size2i DisplayServerAppleEmbedded::window_get_min_size(WindowID p_window) const {
+ return Size2i();
+}
+
+void DisplayServerAppleEmbedded::window_set_size(const Size2i p_size, WindowID p_window) {
+ // Probably not supported for iOS
+}
+
+Size2i DisplayServerAppleEmbedded::window_get_size(WindowID p_window) const {
+ id appDelegate = [[UIApplication sharedApplication] delegate];
+ CGRect windowBounds = appDelegate.window.bounds;
+ return Size2i(windowBounds.size.width, windowBounds.size.height) * screen_get_max_scale();
+}
+
+Size2i DisplayServerAppleEmbedded::window_get_size_with_decorations(WindowID p_window) const {
+ return window_get_size(p_window);
+}
+
+void DisplayServerAppleEmbedded::window_set_mode(WindowMode p_mode, WindowID p_window) {
+ // Probably not supported for iOS
+}
+
+DisplayServer::WindowMode DisplayServerAppleEmbedded::window_get_mode(WindowID p_window) const {
+ return WindowMode::WINDOW_MODE_FULLSCREEN;
+}
+
+bool DisplayServerAppleEmbedded::window_is_maximize_allowed(WindowID p_window) const {
+ return false;
+}
+
+void DisplayServerAppleEmbedded::window_set_flag(WindowFlags p_flag, bool p_enabled, WindowID p_window) {
+ // Probably not supported for iOS
+}
+
+bool DisplayServerAppleEmbedded::window_get_flag(WindowFlags p_flag, WindowID p_window) const {
+ return false;
+}
+
+void DisplayServerAppleEmbedded::window_request_attention(WindowID p_window) {
+ // Probably not supported for iOS
+}
+
+void DisplayServerAppleEmbedded::window_move_to_foreground(WindowID p_window) {
+ // Probably not supported for iOS
+}
+
+bool DisplayServerAppleEmbedded::window_is_focused(WindowID p_window) const {
+ return true;
+}
+
+float DisplayServerAppleEmbedded::screen_get_max_scale() const {
+ return screen_get_scale(SCREEN_OF_MAIN_WINDOW);
+}
+
+void DisplayServerAppleEmbedded::screen_set_orientation(DisplayServer::ScreenOrientation p_orientation, int p_screen) {
+ screen_orientation = p_orientation;
+ if (@available(iOS 16.0, *)) {
+ [GDTAppDelegateService.viewController setNeedsUpdateOfSupportedInterfaceOrientations];
+ }
+#if !defined(VISIONOS_ENABLED)
+ else {
+ [UIViewController attemptRotationToDeviceOrientation];
+ }
+#endif
+}
+
+DisplayServer::ScreenOrientation DisplayServerAppleEmbedded::screen_get_orientation(int p_screen) const {
+ return screen_orientation;
+}
+
+bool DisplayServerAppleEmbedded::window_can_draw(WindowID p_window) const {
+ return true;
+}
+
+bool DisplayServerAppleEmbedded::can_any_window_draw() const {
+ return true;
+}
+
+bool DisplayServerAppleEmbedded::is_touchscreen_available() const {
+ return true;
+}
+
+_FORCE_INLINE_ int _convert_utf32_offset_to_utf16(const String &p_existing_text, int p_pos) {
+ int limit = p_pos;
+ for (int i = 0; i < MIN(p_existing_text.length(), p_pos); i++) {
+ if (p_existing_text[i] > 0xffff) {
+ limit++;
+ }
+ }
+ return limit;
+}
+
+void DisplayServerAppleEmbedded::virtual_keyboard_show(const String &p_existing_text, const Rect2 &p_screen_rect, VirtualKeyboardType p_type, int p_max_length, int p_cursor_start, int p_cursor_end) {
+ NSString *existingString = [[NSString alloc] initWithUTF8String:p_existing_text.utf8().get_data()];
+
+ GDTAppDelegateService.viewController.keyboardView.keyboardType = UIKeyboardTypeDefault;
+ GDTAppDelegateService.viewController.keyboardView.textContentType = nil;
+ switch (p_type) {
+ case KEYBOARD_TYPE_DEFAULT: {
+ GDTAppDelegateService.viewController.keyboardView.keyboardType = UIKeyboardTypeDefault;
+ } break;
+ case KEYBOARD_TYPE_MULTILINE: {
+ GDTAppDelegateService.viewController.keyboardView.keyboardType = UIKeyboardTypeDefault;
+ } break;
+ case KEYBOARD_TYPE_NUMBER: {
+ GDTAppDelegateService.viewController.keyboardView.keyboardType = UIKeyboardTypeNumberPad;
+ } break;
+ case KEYBOARD_TYPE_NUMBER_DECIMAL: {
+ GDTAppDelegateService.viewController.keyboardView.keyboardType = UIKeyboardTypeDecimalPad;
+ } break;
+ case KEYBOARD_TYPE_PHONE: {
+ GDTAppDelegateService.viewController.keyboardView.keyboardType = UIKeyboardTypePhonePad;
+ GDTAppDelegateService.viewController.keyboardView.textContentType = UITextContentTypeTelephoneNumber;
+ } break;
+ case KEYBOARD_TYPE_EMAIL_ADDRESS: {
+ GDTAppDelegateService.viewController.keyboardView.keyboardType = UIKeyboardTypeEmailAddress;
+ GDTAppDelegateService.viewController.keyboardView.textContentType = UITextContentTypeEmailAddress;
+ } break;
+ case KEYBOARD_TYPE_PASSWORD: {
+ GDTAppDelegateService.viewController.keyboardView.keyboardType = UIKeyboardTypeDefault;
+ GDTAppDelegateService.viewController.keyboardView.textContentType = UITextContentTypePassword;
+ } break;
+ case KEYBOARD_TYPE_URL: {
+ GDTAppDelegateService.viewController.keyboardView.keyboardType = UIKeyboardTypeWebSearch;
+ GDTAppDelegateService.viewController.keyboardView.textContentType = UITextContentTypeURL;
+ } break;
+ }
+
+ [GDTAppDelegateService.viewController.keyboardView
+ becomeFirstResponderWithString:existingString
+ cursorStart:_convert_utf32_offset_to_utf16(p_existing_text, p_cursor_start)
+ cursorEnd:_convert_utf32_offset_to_utf16(p_existing_text, p_cursor_end)];
+}
+
+bool DisplayServerAppleEmbedded::is_keyboard_active() const {
+ return [GDTAppDelegateService.viewController.keyboardView isFirstResponder];
+}
+
+void DisplayServerAppleEmbedded::virtual_keyboard_hide() {
+ [GDTAppDelegateService.viewController.keyboardView resignFirstResponder];
+}
+
+void DisplayServerAppleEmbedded::virtual_keyboard_set_height(int height) {
+ virtual_keyboard_height = height * screen_get_max_scale();
+}
+
+int DisplayServerAppleEmbedded::virtual_keyboard_get_height() const {
+ return virtual_keyboard_height;
+}
+
+bool DisplayServerAppleEmbedded::has_hardware_keyboard() const {
+ if (@available(iOS 14.0, *)) {
+ return [GCKeyboard coalescedKeyboard];
+ } else {
+ return false;
+ }
+}
+
+void DisplayServerAppleEmbedded::clipboard_set(const String &p_text) {
+ [UIPasteboard generalPasteboard].string = [NSString stringWithUTF8String:p_text.utf8().get_data()];
+}
+
+String DisplayServerAppleEmbedded::clipboard_get() const {
+ NSString *text = [UIPasteboard generalPasteboard].string;
+
+ return String::utf8([text UTF8String]);
+}
+
+void DisplayServerAppleEmbedded::screen_set_keep_on(bool p_enable) {
+ [UIApplication sharedApplication].idleTimerDisabled = p_enable;
+}
+
+bool DisplayServerAppleEmbedded::screen_is_kept_on() const {
+ return [UIApplication sharedApplication].idleTimerDisabled;
+}
+
+void DisplayServerAppleEmbedded::resize_window(CGSize viewSize) {
+ Size2i size = Size2i(viewSize.width, viewSize.height) * screen_get_max_scale();
+
+#if defined(RD_ENABLED)
+ if (rendering_context) {
+ rendering_context->window_set_size(MAIN_WINDOW_ID, size.x, size.y);
+ }
+#endif
+
+ Variant resize_rect = Rect2i(Point2i(), size);
+ _window_callback(window_resize_callback, resize_rect);
+}
+
+void DisplayServerAppleEmbedded::window_set_vsync_mode(DisplayServer::VSyncMode p_vsync_mode, WindowID p_window) {
+ _THREAD_SAFE_METHOD_
+#if defined(RD_ENABLED)
+ if (rendering_context) {
+ rendering_context->window_set_vsync_mode(p_window, p_vsync_mode);
+ }
+#endif
+}
+
+DisplayServer::VSyncMode DisplayServerAppleEmbedded::window_get_vsync_mode(WindowID p_window) const {
+ _THREAD_SAFE_METHOD_
+#if defined(RD_ENABLED)
+ if (rendering_context) {
+ return rendering_context->window_get_vsync_mode(p_window);
+ }
+#endif
+ return DisplayServer::VSYNC_ENABLED;
+}
diff --git a/platform/ios/godot_app_delegate.h b/drivers/apple_embedded/godot_app_delegate.h
similarity index 88%
rename from platform/ios/godot_app_delegate.h
rename to drivers/apple_embedded/godot_app_delegate.h
index 35a90171108a..7c1287189268 100644
--- a/platform/ios/godot_app_delegate.h
+++ b/drivers/apple_embedded/godot_app_delegate.h
@@ -32,12 +32,12 @@
#import
-typedef NSObject ApplicationDelegateService;
+typedef NSObject GDTAppDelegateServiceProtocol;
-@interface GodotApplicationDelegate : NSObject
+@interface GDTApplicationDelegate : NSObject
-@property(class, readonly, strong) NSArray *services;
+@property(class, readonly, strong) NSArray *services;
-+ (void)addService:(ApplicationDelegateService *)service;
++ (void)addService:(GDTAppDelegateServiceProtocol *)service;
@end
diff --git a/platform/ios/godot_app_delegate.m b/drivers/apple_embedded/godot_app_delegate.mm
similarity index 84%
rename from platform/ios/godot_app_delegate.m
rename to drivers/apple_embedded/godot_app_delegate.mm
index 53e53cd0c658..7e78b3fc3ba1 100644
--- a/platform/ios/godot_app_delegate.m
+++ b/drivers/apple_embedded/godot_app_delegate.mm
@@ -1,5 +1,5 @@
/**************************************************************************/
-/* godot_app_delegate.m */
+/* godot_app_delegate.mm */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
@@ -30,26 +30,22 @@
#import "godot_app_delegate.h"
-#import "app_delegate.h"
+#import "app_delegate_service.h"
-@interface GodotApplicationDelegate ()
+@implementation GDTApplicationDelegate
-@end
-
-@implementation GodotApplicationDelegate
-
-static NSMutableArray *services = nil;
+static NSMutableArray *services = nil;
-+ (NSArray *)services {
++ (NSArray *)services {
return services;
}
+ (void)load {
services = [NSMutableArray new];
- [services addObject:[AppDelegate new]];
+ [services addObject:[GDTAppDelegateService new]];
}
-+ (void)addService:(ApplicationDelegateService *)service {
++ (void)addService:(GDTAppDelegateServiceProtocol *)service {
if (!services || !service) {
return;
}
@@ -63,7 +59,7 @@ + (void)addService:(ApplicationDelegateService *)service {
- (UIWindow *)window {
UIWindow *result = nil;
- for (ApplicationDelegateService *service in services) {
+ for (GDTAppDelegateServiceProtocol *service in services) {
if (![service respondsToSelector:_cmd]) {
continue;
}
@@ -83,7 +79,7 @@ - (UIWindow *)window {
- (BOOL)application:(UIApplication *)application willFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
BOOL result = NO;
- for (ApplicationDelegateService *service in services) {
+ for (GDTAppDelegateServiceProtocol *service in services) {
if (![service respondsToSelector:_cmd]) {
continue;
}
@@ -99,7 +95,7 @@ - (BOOL)application:(UIApplication *)application willFinishLaunchingWithOptions:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
BOOL result = NO;
- for (ApplicationDelegateService *service in services) {
+ for (GDTAppDelegateServiceProtocol *service in services) {
if (![service respondsToSelector:_cmd]) {
continue;
}
@@ -125,7 +121,7 @@ - (void)application:(UIApplication *)application didDiscardSceneSessions:(NSSet<
// MARK: Life-Cycle
- (void)applicationDidBecomeActive:(UIApplication *)application {
- for (ApplicationDelegateService *service in services) {
+ for (GDTAppDelegateServiceProtocol *service in services) {
if (![service respondsToSelector:_cmd]) {
continue;
}
@@ -135,7 +131,7 @@ - (void)applicationDidBecomeActive:(UIApplication *)application {
}
- (void)applicationWillResignActive:(UIApplication *)application {
- for (ApplicationDelegateService *service in services) {
+ for (GDTAppDelegateServiceProtocol *service in services) {
if (![service respondsToSelector:_cmd]) {
continue;
}
@@ -145,7 +141,7 @@ - (void)applicationWillResignActive:(UIApplication *)application {
}
- (void)applicationDidEnterBackground:(UIApplication *)application {
- for (ApplicationDelegateService *service in services) {
+ for (GDTAppDelegateServiceProtocol *service in services) {
if (![service respondsToSelector:_cmd]) {
continue;
}
@@ -155,7 +151,7 @@ - (void)applicationDidEnterBackground:(UIApplication *)application {
}
- (void)applicationWillEnterForeground:(UIApplication *)application {
- for (ApplicationDelegateService *service in services) {
+ for (GDTAppDelegateServiceProtocol *service in services) {
if (![service respondsToSelector:_cmd]) {
continue;
}
@@ -165,7 +161,7 @@ - (void)applicationWillEnterForeground:(UIApplication *)application {
}
- (void)applicationWillTerminate:(UIApplication *)application {
- for (ApplicationDelegateService *service in services) {
+ for (GDTAppDelegateServiceProtocol *service in services) {
if (![service respondsToSelector:_cmd]) {
continue;
}
@@ -177,7 +173,7 @@ - (void)applicationWillTerminate:(UIApplication *)application {
// MARK: Environment Changes
- (void)applicationProtectedDataDidBecomeAvailable:(UIApplication *)application {
- for (ApplicationDelegateService *service in services) {
+ for (GDTAppDelegateServiceProtocol *service in services) {
if (![service respondsToSelector:_cmd]) {
continue;
}
@@ -187,7 +183,7 @@ - (void)applicationProtectedDataDidBecomeAvailable:(UIApplication *)application
}
- (void)applicationProtectedDataWillBecomeUnavailable:(UIApplication *)application {
- for (ApplicationDelegateService *service in services) {
+ for (GDTAppDelegateServiceProtocol *service in services) {
if (![service respondsToSelector:_cmd]) {
continue;
}
@@ -197,7 +193,7 @@ - (void)applicationProtectedDataWillBecomeUnavailable:(UIApplication *)applicati
}
- (void)applicationDidReceiveMemoryWarning:(UIApplication *)application {
- for (ApplicationDelegateService *service in services) {
+ for (GDTAppDelegateServiceProtocol *service in services) {
if (![service respondsToSelector:_cmd]) {
continue;
}
@@ -207,7 +203,7 @@ - (void)applicationDidReceiveMemoryWarning:(UIApplication *)application {
}
- (void)applicationSignificantTimeChange:(UIApplication *)application {
- for (ApplicationDelegateService *service in services) {
+ for (GDTAppDelegateServiceProtocol *service in services) {
if (![service respondsToSelector:_cmd]) {
continue;
}
@@ -221,7 +217,7 @@ - (void)applicationSignificantTimeChange:(UIApplication *)application {
- (BOOL)application:(UIApplication *)application shouldSaveSecureApplicationState:(NSCoder *)coder API_AVAILABLE(ios(13.2)) {
BOOL result = NO;
- for (ApplicationDelegateService *service in services) {
+ for (GDTAppDelegateServiceProtocol *service in services) {
if (![service respondsToSelector:_cmd]) {
continue;
}
@@ -237,7 +233,7 @@ - (BOOL)application:(UIApplication *)application shouldSaveSecureApplicationStat
- (BOOL)application:(UIApplication *)application shouldRestoreSecureApplicationState:(NSCoder *)coder API_AVAILABLE(ios(13.2)) {
BOOL result = NO;
- for (ApplicationDelegateService *service in services) {
+ for (GDTAppDelegateServiceProtocol *service in services) {
if (![service respondsToSelector:_cmd]) {
continue;
}
@@ -251,7 +247,7 @@ - (BOOL)application:(UIApplication *)application shouldRestoreSecureApplicationS
}
- (UIViewController *)application:(UIApplication *)application viewControllerWithRestorationIdentifierPath:(NSArray *)identifierComponents coder:(NSCoder *)coder {
- for (ApplicationDelegateService *service in services) {
+ for (GDTAppDelegateServiceProtocol *service in services) {
if (![service respondsToSelector:_cmd]) {
continue;
}
@@ -267,7 +263,7 @@ - (UIViewController *)application:(UIApplication *)application viewControllerWit
}
- (void)application:(UIApplication *)application willEncodeRestorableStateWithCoder:(NSCoder *)coder {
- for (ApplicationDelegateService *service in services) {
+ for (GDTAppDelegateServiceProtocol *service in services) {
if (![service respondsToSelector:_cmd]) {
continue;
}
@@ -277,7 +273,7 @@ - (void)application:(UIApplication *)application willEncodeRestorableStateWithCo
}
- (void)application:(UIApplication *)application didDecodeRestorableStateWithCoder:(NSCoder *)coder {
- for (ApplicationDelegateService *service in services) {
+ for (GDTAppDelegateServiceProtocol *service in services) {
if (![service respondsToSelector:_cmd]) {
continue;
}
@@ -289,7 +285,7 @@ - (void)application:(UIApplication *)application didDecodeRestorableStateWithCod
// MARK: Download Data in Background
- (void)application:(UIApplication *)application handleEventsForBackgroundURLSession:(NSString *)identifier completionHandler:(void (^)(void))completionHandler {
- for (ApplicationDelegateService *service in services) {
+ for (GDTAppDelegateServiceProtocol *service in services) {
if (![service respondsToSelector:_cmd]) {
continue;
}
@@ -309,7 +305,7 @@ - (void)application:(UIApplication *)application handleEventsForBackgroundURLSes
- (BOOL)application:(UIApplication *)application willContinueUserActivityWithType:(NSString *)userActivityType {
BOOL result = NO;
- for (ApplicationDelegateService *service in services) {
+ for (GDTAppDelegateServiceProtocol *service in services) {
if (![service respondsToSelector:_cmd]) {
continue;
}
@@ -325,7 +321,7 @@ - (BOOL)application:(UIApplication *)application willContinueUserActivityWithTyp
- (BOOL)application:(UIApplication *)application continueUserActivity:(NSUserActivity *)userActivity restorationHandler:(void (^)(NSArray> *restorableObjects))restorationHandler {
BOOL result = NO;
- for (ApplicationDelegateService *service in services) {
+ for (GDTAppDelegateServiceProtocol *service in services) {
if (![service respondsToSelector:_cmd]) {
continue;
}
@@ -339,7 +335,7 @@ - (BOOL)application:(UIApplication *)application continueUserActivity:(NSUserAct
}
- (void)application:(UIApplication *)application didUpdateUserActivity:(NSUserActivity *)userActivity {
- for (ApplicationDelegateService *service in services) {
+ for (GDTAppDelegateServiceProtocol *service in services) {
if (![service respondsToSelector:_cmd]) {
continue;
}
@@ -349,7 +345,7 @@ - (void)application:(UIApplication *)application didUpdateUserActivity:(NSUserAc
}
- (void)application:(UIApplication *)application didFailToContinueUserActivityWithType:(NSString *)userActivityType error:(NSError *)error {
- for (ApplicationDelegateService *service in services) {
+ for (GDTAppDelegateServiceProtocol *service in services) {
if (![service respondsToSelector:_cmd]) {
continue;
}
@@ -359,7 +355,7 @@ - (void)application:(UIApplication *)application didFailToContinueUserActivityWi
}
- (void)application:(UIApplication *)application performActionForShortcutItem:(UIApplicationShortcutItem *)shortcutItem completionHandler:(void (^)(BOOL succeeded))completionHandler {
- for (ApplicationDelegateService *service in services) {
+ for (GDTAppDelegateServiceProtocol *service in services) {
if (![service respondsToSelector:_cmd]) {
continue;
}
@@ -371,7 +367,7 @@ - (void)application:(UIApplication *)application performActionForShortcutItem:(U
// MARK: WatchKit
- (void)application:(UIApplication *)application handleWatchKitExtensionRequest:(NSDictionary *)userInfo reply:(void (^)(NSDictionary *replyInfo))reply {
- for (ApplicationDelegateService *service in services) {
+ for (GDTAppDelegateServiceProtocol *service in services) {
if (![service respondsToSelector:_cmd]) {
continue;
}
@@ -383,7 +379,7 @@ - (void)application:(UIApplication *)application handleWatchKitExtensionRequest:
// MARK: HealthKit
- (void)applicationShouldRequestHealthAuthorization:(UIApplication *)application {
- for (ApplicationDelegateService *service in services) {
+ for (GDTAppDelegateServiceProtocol *service in services) {
if (![service respondsToSelector:_cmd]) {
continue;
}
@@ -395,7 +391,7 @@ - (void)applicationShouldRequestHealthAuthorization:(UIApplication *)application
// MARK: Opening an URL
- (BOOL)application:(UIApplication *)app openURL:(NSURL *)url options:(NSDictionary *)options {
- for (ApplicationDelegateService *service in services) {
+ for (GDTAppDelegateServiceProtocol *service in services) {
if (![service respondsToSelector:_cmd]) {
continue;
}
@@ -413,7 +409,7 @@ - (BOOL)application:(UIApplication *)app openURL:(NSURL *)url options:(NSDiction
- (BOOL)application:(UIApplication *)application shouldAllowExtensionPointIdentifier:(UIApplicationExtensionPointIdentifier)extensionPointIdentifier {
BOOL result = NO;
- for (ApplicationDelegateService *service in services) {
+ for (GDTAppDelegateServiceProtocol *service in services) {
if (![service respondsToSelector:_cmd]) {
continue;
}
@@ -429,7 +425,7 @@ - (BOOL)application:(UIApplication *)application shouldAllowExtensionPointIdenti
// MARK: SiriKit
- (id)application:(UIApplication *)application handlerForIntent:(INIntent *)intent API_AVAILABLE(ios(14.0)) {
- for (ApplicationDelegateService *service in services) {
+ for (GDTAppDelegateServiceProtocol *service in services) {
if (![service respondsToSelector:_cmd]) {
continue;
}
@@ -447,7 +443,7 @@ - (id)application:(UIApplication *)application handlerForIntent:(INIntent *)inte
// MARK: CloudKit
- (void)application:(UIApplication *)application userDidAcceptCloudKitShareWithMetadata:(CKShareMetadata *)cloudKitShareMetadata {
- for (ApplicationDelegateService *service in services) {
+ for (GDTAppDelegateServiceProtocol *service in services) {
if (![service respondsToSelector:_cmd]) {
continue;
}
diff --git a/platform/ios/godot_view.h b/drivers/apple_embedded/godot_view_apple_embedded.h
similarity index 78%
rename from platform/ios/godot_view.h
rename to drivers/apple_embedded/godot_view_apple_embedded.h
index ca774be339c4..0694d3a4c900 100644
--- a/platform/ios/godot_view.h
+++ b/drivers/apple_embedded/godot_view_apple_embedded.h
@@ -1,5 +1,5 @@
/**************************************************************************/
-/* godot_view.h */
+/* godot_view_apple_embedded.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
@@ -34,31 +34,39 @@
class String;
-@class GodotView;
-@protocol DisplayLayer;
-@protocol GodotViewRendererProtocol;
+@class GDTView;
+@protocol GDTDisplayLayer;
+@protocol GDTViewRendererProtocol;
-@protocol GodotViewDelegate
+@protocol GDTViewDelegate
-- (BOOL)godotViewFinishedSetup:(GodotView *)view;
+- (BOOL)godotViewFinishedSetup:(GDTView *)view;
@end
-@interface GodotView : UIView
+@interface GDTView : UIView
-@property(assign, nonatomic) id renderer;
-@property(assign, nonatomic) id delegate;
+@property(assign, nonatomic) id renderer;
+@property(assign, nonatomic) id delegate;
@property(assign, readonly, nonatomic) BOOL isActive;
@property(assign, nonatomic) BOOL useCADisplayLink;
-@property(strong, readonly, nonatomic) CALayer *renderingLayer;
+@property(strong, readonly, nonatomic) CALayer *renderingLayer;
@property(assign, readonly, nonatomic) BOOL canRender;
@property(assign, nonatomic) NSTimeInterval renderingInterval;
-- (CALayer *)initializeRenderingForDriver:(NSString *)driverName;
+// Can be extended by subclasses
+- (void)godot_commonInit;
+
+// Implemented in subclasses
+- (CALayer *)initializeRenderingForDriver:(NSString *)driverName;
+
- (void)stopRendering;
- (void)startRendering;
@end
+
+// Implemented in subclasses
+extern GDTView *GDTViewCreate();
diff --git a/platform/ios/godot_view.mm b/drivers/apple_embedded/godot_view_apple_embedded.mm
similarity index 72%
rename from platform/ios/godot_view.mm
rename to drivers/apple_embedded/godot_view_apple_embedded.mm
index f2416f2fbfdf..dd6ba3f5d2b7 100644
--- a/platform/ios/godot_view.mm
+++ b/drivers/apple_embedded/godot_view_apple_embedded.mm
@@ -1,5 +1,5 @@
/**************************************************************************/
-/* godot_view.mm */
+/* godot_view_apple_embedded.mm */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
@@ -28,10 +28,10 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
-#import "godot_view.h"
+#import "godot_view_apple_embedded.h"
-#import "display_layer.h"
-#import "display_server_ios.h"
+#import "display_layer_apple_embedded.h"
+#import "display_server_apple_embedded.h"
#import "godot_view_renderer.h"
#include "core/config/project_settings.h"
@@ -43,7 +43,7 @@
static const int max_touches = 32;
static const float earth_gravity = 9.80665;
-@interface GodotView () {
+@interface GDTView () {
UITouch *godot_touches[max_touches];
}
@@ -56,48 +56,17 @@ @interface GodotView () {
// Only used if CADisplayLink is not
@property(strong, nonatomic) NSTimer *animationTimer;
-@property(strong, nonatomic) CALayer *renderingLayer;
+@property(strong, nonatomic) CALayer *renderingLayer;
@property(strong, nonatomic) CMMotionManager *motionManager;
@end
-@implementation GodotView
+@implementation GDTView
-- (CALayer *)initializeRenderingForDriver:(NSString *)driverName {
- if (self.renderingLayer) {
- return self.renderingLayer;
- }
-
- CALayer *layer;
-
- if ([driverName isEqualToString:@"vulkan"] || [driverName isEqualToString:@"metal"]) {
-#if defined(TARGET_OS_SIMULATOR) && TARGET_OS_SIMULATOR
- if (@available(iOS 13, *)) {
- layer = [GodotMetalLayer layer];
- } else {
- return nil;
- }
-#else
- layer = [GodotMetalLayer layer];
-#endif
- } else if ([driverName isEqualToString:@"opengl3"]) {
- GODOT_CLANG_WARNING_PUSH_AND_IGNORE("-Wdeprecated-declarations") // OpenGL is deprecated in iOS 12.0.
- layer = [GodotOpenGLLayer layer];
- GODOT_CLANG_WARNING_POP
- } else {
- return nil;
- }
-
- layer.frame = self.bounds;
- layer.contentsScale = self.contentScaleFactor;
-
- [self.layer addSublayer:layer];
- self.renderingLayer = layer;
-
- [layer initializeDisplayLayer];
-
- return self.renderingLayer;
+// Implemented in subclasses
+- (CALayer *)initializeRenderingForDriver:(NSString *)driverName {
+ return nil;
}
- (instancetype)initWithCoder:(NSCoder *)coder {
@@ -148,7 +117,13 @@ - (void)dealloc {
}
- (void)godot_commonInit {
+#if !defined(VISIONOS_ENABLED)
self.contentScaleFactor = [UIScreen mainScreen].scale;
+#endif
+
+ if (@available(iOS 17.0, *)) {
+ [self registerForTraitChanges:@[ [UITraitUserInterfaceStyle class] ] withTarget:self action:@selector(traitCollectionDidChangeWithView:previousTraitCollection:)];
+ }
[self initTouches];
@@ -167,7 +142,7 @@ - (void)godot_commonInit {
}
- (void)system_theme_changed {
- DisplayServerIOS *ds = (DisplayServerIOS *)DisplayServer::get_singleton();
+ DisplayServerAppleEmbedded *ds = (DisplayServerAppleEmbedded *)DisplayServer::get_singleton();
if (ds) {
ds->emit_system_theme_changed();
}
@@ -175,8 +150,16 @@ - (void)system_theme_changed {
- (void)traitCollectionDidChange:(UITraitCollection *)previousTraitCollection {
if (@available(iOS 13.0, *)) {
+#if !defined(VISIONOS_ENABLED)
[super traitCollectionDidChange:previousTraitCollection];
+#endif
+ [self traitCollectionDidChangeWithView:self
+ previousTraitCollection:previousTraitCollection];
+ }
+}
+- (void)traitCollectionDidChangeWithView:(UIView *)view previousTraitCollection:(UITraitCollection *)previousTraitCollection {
+ if (@available(iOS 13.0, *)) {
if ([UITraitCollection currentTraitCollection].userInterfaceStyle != previousTraitCollection.userInterfaceStyle) {
[self system_theme_changed];
}
@@ -293,8 +276,8 @@ - (void)layoutSubviews {
self.renderingLayer.frame = self.bounds;
[self.renderingLayer layoutDisplayLayer];
- if (DisplayServerIOS::get_singleton()) {
- DisplayServerIOS::get_singleton()->resize_window(self.bounds.size);
+ if (DisplayServerAppleEmbedded::get_singleton()) {
+ DisplayServerAppleEmbedded::get_singleton()->resize_window(self.bounds.size);
}
}
@@ -357,7 +340,7 @@ - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
int tid = [self getTouchIDForTouch:touch];
ERR_FAIL_COND(tid == -1);
CGPoint touchPoint = [touch locationInView:self];
- DisplayServerIOS::get_singleton()->touch_press(tid, touchPoint.x * self.contentScaleFactor, touchPoint.y * self.contentScaleFactor, true, touch.tapCount > 1);
+ DisplayServerAppleEmbedded::get_singleton()->touch_press(tid, touchPoint.x * self.contentScaleFactor, touchPoint.y * self.contentScaleFactor, true, touch.tapCount > 1);
}
}
@@ -369,7 +352,7 @@ - (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
CGPoint prev_point = [touch previousLocationInView:self];
CGFloat alt = [touch altitudeAngle];
CGVector azim = [touch azimuthUnitVectorInView:self];
- DisplayServerIOS::get_singleton()->touch_drag(tid, prev_point.x * self.contentScaleFactor, prev_point.y * self.contentScaleFactor, touchPoint.x * self.contentScaleFactor, touchPoint.y * self.contentScaleFactor, [touch force] / [touch maximumPossibleForce], Vector2(azim.dx, azim.dy) * Math::cos(alt));
+ DisplayServerAppleEmbedded::get_singleton()->touch_drag(tid, prev_point.x * self.contentScaleFactor, prev_point.y * self.contentScaleFactor, touchPoint.x * self.contentScaleFactor, touchPoint.y * self.contentScaleFactor, [touch force] / [touch maximumPossibleForce], Vector2(azim.dx, azim.dy) * Math::cos(alt));
}
}
@@ -379,7 +362,7 @@ - (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
ERR_FAIL_COND(tid == -1);
[self removeTouch:touch];
CGPoint touchPoint = [touch locationInView:self];
- DisplayServerIOS::get_singleton()->touch_press(tid, touchPoint.x * self.contentScaleFactor, touchPoint.y * self.contentScaleFactor, false, false);
+ DisplayServerAppleEmbedded::get_singleton()->touch_press(tid, touchPoint.x * self.contentScaleFactor, touchPoint.y * self.contentScaleFactor, false, false);
}
}
@@ -387,7 +370,7 @@ - (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event {
for (UITouch *touch in touches) {
int tid = [self getTouchIDForTouch:touch];
ERR_FAIL_COND(tid == -1);
- DisplayServerIOS::get_singleton()->touches_canceled(tid);
+ DisplayServerAppleEmbedded::get_singleton()->touches_canceled(tid);
}
[self clearTouches];
}
@@ -440,6 +423,7 @@ - (void)handleMotion {
UIInterfaceOrientation interfaceOrientation = UIInterfaceOrientationUnknown;
+#if !defined(VISIONOS_ENABLED)
#if __IPHONE_OS_VERSION_MAX_ALLOWED < 140000
interfaceOrientation = [[UIApplication sharedApplication] statusBarOrientation];
#else
@@ -451,31 +435,34 @@ - (void)handleMotion {
#endif
}
#endif
+#else
+ interfaceOrientation = [UIApplication sharedApplication].delegate.window.windowScene.interfaceOrientation;
+#endif
switch (interfaceOrientation) {
case UIInterfaceOrientationLandscapeLeft: {
- DisplayServerIOS::get_singleton()->update_gravity(Vector3(gravity.x, gravity.y, gravity.z).rotated(Vector3(0, 0, 1), -Math::PI * 0.5));
- DisplayServerIOS::get_singleton()->update_accelerometer(Vector3(acceleration.x + gravity.x, acceleration.y + gravity.y, acceleration.z + gravity.z).rotated(Vector3(0, 0, 1), -Math::PI * 0.5));
- DisplayServerIOS::get_singleton()->update_magnetometer(Vector3(magnetic.x, magnetic.y, magnetic.z).rotated(Vector3(0, 0, 1), -Math::PI * 0.5));
- DisplayServerIOS::get_singleton()->update_gyroscope(Vector3(rotation.x, rotation.y, rotation.z).rotated(Vector3(0, 0, 1), -Math::PI * 0.5));
+ DisplayServerAppleEmbedded::get_singleton()->update_gravity(Vector3(gravity.x, gravity.y, gravity.z).rotated(Vector3(0, 0, 1), -Math::PI * 0.5));
+ DisplayServerAppleEmbedded::get_singleton()->update_accelerometer(Vector3(acceleration.x + gravity.x, acceleration.y + gravity.y, acceleration.z + gravity.z).rotated(Vector3(0, 0, 1), -Math::PI * 0.5));
+ DisplayServerAppleEmbedded::get_singleton()->update_magnetometer(Vector3(magnetic.x, magnetic.y, magnetic.z).rotated(Vector3(0, 0, 1), -Math::PI * 0.5));
+ DisplayServerAppleEmbedded::get_singleton()->update_gyroscope(Vector3(rotation.x, rotation.y, rotation.z).rotated(Vector3(0, 0, 1), -Math::PI * 0.5));
} break;
case UIInterfaceOrientationLandscapeRight: {
- DisplayServerIOS::get_singleton()->update_gravity(Vector3(gravity.x, gravity.y, gravity.z).rotated(Vector3(0, 0, 1), Math::PI * 0.5));
- DisplayServerIOS::get_singleton()->update_accelerometer(Vector3(acceleration.x + gravity.x, acceleration.y + gravity.y, acceleration.z + gravity.z).rotated(Vector3(0, 0, 1), Math::PI * 0.5));
- DisplayServerIOS::get_singleton()->update_magnetometer(Vector3(magnetic.x, magnetic.y, magnetic.z).rotated(Vector3(0, 0, 1), Math::PI * 0.5));
- DisplayServerIOS::get_singleton()->update_gyroscope(Vector3(rotation.x, rotation.y, rotation.z).rotated(Vector3(0, 0, 1), Math::PI * 0.5));
+ DisplayServerAppleEmbedded::get_singleton()->update_gravity(Vector3(gravity.x, gravity.y, gravity.z).rotated(Vector3(0, 0, 1), Math::PI * 0.5));
+ DisplayServerAppleEmbedded::get_singleton()->update_accelerometer(Vector3(acceleration.x + gravity.x, acceleration.y + gravity.y, acceleration.z + gravity.z).rotated(Vector3(0, 0, 1), Math::PI * 0.5));
+ DisplayServerAppleEmbedded::get_singleton()->update_magnetometer(Vector3(magnetic.x, magnetic.y, magnetic.z).rotated(Vector3(0, 0, 1), Math::PI * 0.5));
+ DisplayServerAppleEmbedded::get_singleton()->update_gyroscope(Vector3(rotation.x, rotation.y, rotation.z).rotated(Vector3(0, 0, 1), Math::PI * 0.5));
} break;
case UIInterfaceOrientationPortraitUpsideDown: {
- DisplayServerIOS::get_singleton()->update_gravity(Vector3(gravity.x, gravity.y, gravity.z).rotated(Vector3(0, 0, 1), Math::PI));
- DisplayServerIOS::get_singleton()->update_accelerometer(Vector3(acceleration.x + gravity.x, acceleration.y + gravity.y, acceleration.z + gravity.z).rotated(Vector3(0, 0, 1), Math::PI));
- DisplayServerIOS::get_singleton()->update_magnetometer(Vector3(magnetic.x, magnetic.y, magnetic.z).rotated(Vector3(0, 0, 1), Math::PI));
- DisplayServerIOS::get_singleton()->update_gyroscope(Vector3(rotation.x, rotation.y, rotation.z).rotated(Vector3(0, 0, 1), Math::PI));
+ DisplayServerAppleEmbedded::get_singleton()->update_gravity(Vector3(gravity.x, gravity.y, gravity.z).rotated(Vector3(0, 0, 1), Math::PI));
+ DisplayServerAppleEmbedded::get_singleton()->update_accelerometer(Vector3(acceleration.x + gravity.x, acceleration.y + gravity.y, acceleration.z + gravity.z).rotated(Vector3(0, 0, 1), Math::PI));
+ DisplayServerAppleEmbedded::get_singleton()->update_magnetometer(Vector3(magnetic.x, magnetic.y, magnetic.z).rotated(Vector3(0, 0, 1), Math::PI));
+ DisplayServerAppleEmbedded::get_singleton()->update_gyroscope(Vector3(rotation.x, rotation.y, rotation.z).rotated(Vector3(0, 0, 1), Math::PI));
} break;
default: { // assume portrait
- DisplayServerIOS::get_singleton()->update_gravity(Vector3(gravity.x, gravity.y, gravity.z));
- DisplayServerIOS::get_singleton()->update_accelerometer(Vector3(acceleration.x + gravity.x, acceleration.y + gravity.y, acceleration.z + gravity.z));
- DisplayServerIOS::get_singleton()->update_magnetometer(Vector3(magnetic.x, magnetic.y, magnetic.z));
- DisplayServerIOS::get_singleton()->update_gyroscope(Vector3(rotation.x, rotation.y, rotation.z));
+ DisplayServerAppleEmbedded::get_singleton()->update_gravity(Vector3(gravity.x, gravity.y, gravity.z));
+ DisplayServerAppleEmbedded::get_singleton()->update_accelerometer(Vector3(acceleration.x + gravity.x, acceleration.y + gravity.y, acceleration.z + gravity.z));
+ DisplayServerAppleEmbedded::get_singleton()->update_magnetometer(Vector3(magnetic.x, magnetic.y, magnetic.z));
+ DisplayServerAppleEmbedded::get_singleton()->update_gyroscope(Vector3(rotation.x, rotation.y, rotation.z));
} break;
}
}
diff --git a/platform/ios/godot_view_renderer.h b/drivers/apple_embedded/godot_view_renderer.h
similarity index 95%
rename from platform/ios/godot_view_renderer.h
rename to drivers/apple_embedded/godot_view_renderer.h
index b6a78ffbf57b..1d409d9b4500 100644
--- a/platform/ios/godot_view_renderer.h
+++ b/drivers/apple_embedded/godot_view_renderer.h
@@ -32,7 +32,7 @@
#import
-@protocol GodotViewRendererProtocol
+@protocol GDTViewRendererProtocol
@property(assign, readonly, nonatomic) BOOL hasFinishedSetup;
@@ -41,6 +41,6 @@
@end
-@interface GodotViewRenderer : NSObject
+@interface GDTViewRenderer : NSObject
@end
diff --git a/platform/ios/godot_view_renderer.mm b/drivers/apple_embedded/godot_view_renderer.mm
similarity index 93%
rename from platform/ios/godot_view_renderer.mm
rename to drivers/apple_embedded/godot_view_renderer.mm
index 9c56ca342fb8..95a41e7bd4ff 100644
--- a/platform/ios/godot_view_renderer.mm
+++ b/drivers/apple_embedded/godot_view_renderer.mm
@@ -30,8 +30,8 @@
#import "godot_view_renderer.h"
-#import "display_server_ios.h"
-#import "os_ios.h"
+#import "display_server_apple_embedded.h"
+#import "os_apple_embedded.h"
#include "core/config/project_settings.h"
#include "core/os/keyboard.h"
@@ -44,7 +44,7 @@
#import
#import
-@interface GodotViewRenderer ()
+@interface GDTViewRenderer ()
@property(assign, nonatomic) BOOL hasFinishedProjectDataSetup;
@property(assign, nonatomic) BOOL hasStartedMain;
@@ -52,7 +52,7 @@ @interface GodotViewRenderer ()
@end
-@implementation GodotViewRenderer
+@implementation GDTViewRenderer
- (BOOL)setupView:(UIView *)view {
if (self.hasFinishedSetup) {
@@ -70,7 +70,7 @@ - (BOOL)setupView:(UIView *)view {
if (!self.hasStartedMain) {
self.hasStartedMain = YES;
- OS_IOS::get_singleton()->start();
+ OS_AppleEmbedded::get_singleton()->start();
return YES;
}
@@ -109,11 +109,11 @@ - (void)setupProjectData {
}
- (void)renderOnView:(UIView *)view {
- if (!OS_IOS::get_singleton()) {
+ if (!OS_AppleEmbedded::get_singleton()) {
return;
}
- OS_IOS::get_singleton()->iterate();
+ OS_AppleEmbedded::get_singleton()->iterate();
}
@end
diff --git a/platform/ios/key_mapping_ios.h b/drivers/apple_embedded/key_mapping_apple_embedded.h
similarity index 94%
rename from platform/ios/key_mapping_ios.h
rename to drivers/apple_embedded/key_mapping_apple_embedded.h
index 81abee3ce995..184c628a74da 100644
--- a/platform/ios/key_mapping_ios.h
+++ b/drivers/apple_embedded/key_mapping_apple_embedded.h
@@ -1,5 +1,5 @@
/**************************************************************************/
-/* key_mapping_ios.h */
+/* key_mapping_apple_embedded.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
@@ -34,8 +34,8 @@
#import
-class KeyMappingIOS {
- KeyMappingIOS() {}
+class KeyMappingAppleEmbedded {
+ KeyMappingAppleEmbedded() {}
public:
static void initialize();
diff --git a/platform/ios/key_mapping_ios.mm b/drivers/apple_embedded/key_mapping_apple_embedded.mm
similarity index 97%
rename from platform/ios/key_mapping_ios.mm
rename to drivers/apple_embedded/key_mapping_apple_embedded.mm
index 61f28aa84bf7..137328b43f2c 100644
--- a/platform/ios/key_mapping_ios.mm
+++ b/drivers/apple_embedded/key_mapping_apple_embedded.mm
@@ -1,5 +1,5 @@
/**************************************************************************/
-/* key_mapping_ios.mm */
+/* key_mapping_apple_embedded.mm */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
@@ -28,7 +28,7 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
-#import "key_mapping_ios.h"
+#import "key_mapping_apple_embedded.h"
#include "core/templates/hash_map.h"
@@ -40,7 +40,7 @@
HashMap keyusage_map;
HashMap location_map;
-void KeyMappingIOS::initialize() {
+void KeyMappingAppleEmbedded::initialize() {
if (@available(iOS 13.4, *)) {
keyusage_map[UIKeyboardHIDUsageKeyboardA] = Key::A;
keyusage_map[UIKeyboardHIDUsageKeyboardB] = Key::B;
@@ -185,7 +185,7 @@
}
}
-Key KeyMappingIOS::remap_key(CFIndex p_keycode) {
+Key KeyMappingAppleEmbedded::remap_key(CFIndex p_keycode) {
if (@available(iOS 13.4, *)) {
const Key *key = keyusage_map.getptr(p_keycode);
if (key) {
@@ -195,7 +195,7 @@
return Key::NONE;
}
-KeyLocation KeyMappingIOS::key_location(CFIndex p_keycode) {
+KeyLocation KeyMappingAppleEmbedded::key_location(CFIndex p_keycode) {
if (@available(iOS 13.4, *)) {
const KeyLocation *location = location_map.getptr(p_keycode);
if (location) {
diff --git a/platform/ios/keyboard_input_view.h b/drivers/apple_embedded/keyboard_input_view.h
similarity index 98%
rename from platform/ios/keyboard_input_view.h
rename to drivers/apple_embedded/keyboard_input_view.h
index c2e789f53d00..3f57d6d2687b 100644
--- a/platform/ios/keyboard_input_view.h
+++ b/drivers/apple_embedded/keyboard_input_view.h
@@ -32,7 +32,7 @@
#import
-@interface GodotKeyboardInputView : UITextView
+@interface GDTKeyboardInputView : UITextView
- (BOOL)becomeFirstResponderWithString:(NSString *)existingString cursorStart:(NSInteger)start cursorEnd:(NSInteger)end;
diff --git a/platform/ios/keyboard_input_view.mm b/drivers/apple_embedded/keyboard_input_view.mm
similarity index 90%
rename from platform/ios/keyboard_input_view.mm
rename to drivers/apple_embedded/keyboard_input_view.mm
index a2e58efbdc4f..3079d5a5235f 100644
--- a/platform/ios/keyboard_input_view.mm
+++ b/drivers/apple_embedded/keyboard_input_view.mm
@@ -30,19 +30,19 @@
#import "keyboard_input_view.h"
-#import "display_server_ios.h"
-#import "os_ios.h"
+#import "display_server_apple_embedded.h"
+#import "os_apple_embedded.h"
#include "core/os/keyboard.h"
-@interface GodotKeyboardInputView ()
+@interface GDTKeyboardInputView ()
@property(nonatomic, copy) NSString *previousText;
@property(nonatomic, assign) NSRange previousSelectedRange;
@end
-@implementation GodotKeyboardInputView
+@implementation GDTKeyboardInputView
- (instancetype)initWithCoder:(NSCoder *)coder {
self = [super initWithCoder:coder];
@@ -116,8 +116,8 @@ - (BOOL)resignFirstResponder {
- (void)deleteText:(NSInteger)charactersToDelete {
for (int i = 0; i < charactersToDelete; i++) {
- DisplayServerIOS::get_singleton()->key(Key::BACKSPACE, 0, Key::BACKSPACE, Key::NONE, 0, true, KeyLocation::UNSPECIFIED);
- DisplayServerIOS::get_singleton()->key(Key::BACKSPACE, 0, Key::BACKSPACE, Key::NONE, 0, false, KeyLocation::UNSPECIFIED);
+ DisplayServerAppleEmbedded::get_singleton()->key(Key::BACKSPACE, 0, Key::BACKSPACE, Key::NONE, 0, true, KeyLocation::UNSPECIFIED);
+ DisplayServerAppleEmbedded::get_singleton()->key(Key::BACKSPACE, 0, Key::BACKSPACE, Key::NONE, 0, false, KeyLocation::UNSPECIFIED);
}
}
@@ -136,8 +136,8 @@ - (void)enterText:(NSString *)substring {
key = Key::SPACE;
}
- DisplayServerIOS::get_singleton()->key(key, character, key, Key::NONE, 0, true, KeyLocation::UNSPECIFIED);
- DisplayServerIOS::get_singleton()->key(key, character, key, Key::NONE, 0, false, KeyLocation::UNSPECIFIED);
+ DisplayServerAppleEmbedded::get_singleton()->key(key, character, key, Key::NONE, 0, true, KeyLocation::UNSPECIFIED);
+ DisplayServerAppleEmbedded::get_singleton()->key(key, character, key, Key::NONE, 0, false, KeyLocation::UNSPECIFIED);
}
}
diff --git a/drivers/apple_embedded/main_utilities.h b/drivers/apple_embedded/main_utilities.h
new file mode 100644
index 000000000000..441a7dddf36b
--- /dev/null
+++ b/drivers/apple_embedded/main_utilities.h
@@ -0,0 +1,35 @@
+/**************************************************************************/
+/* main_utilities.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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. */
+/**************************************************************************/
+
+#pragma once
+
+void change_to_launch_dir(char **p_args);
+
+int process_args(int p_argc, char **p_args, char **r_args);
diff --git a/platform/ios/godot_ios.mm b/drivers/apple_embedded/main_utilities.mm
similarity index 77%
rename from platform/ios/godot_ios.mm
rename to drivers/apple_embedded/main_utilities.mm
index 42b9658fbf99..608232625023 100644
--- a/platform/ios/godot_ios.mm
+++ b/drivers/apple_embedded/main_utilities.mm
@@ -1,5 +1,5 @@
/**************************************************************************/
-/* godot_ios.mm */
+/* main_utilities.mm */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
@@ -28,15 +28,29 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
-#import "os_ios.h"
-
#include "core/string/ustring.h"
-#include "main/main.h"
+
+#import
#include
#include
-static OS_IOS *os = nullptr;
+void change_to_launch_dir(char **p_args) {
+ size_t len = strlen(p_args[0]);
+
+ while (len--) {
+ if (p_args[0][len] == '/') {
+ break;
+ }
+ }
+
+ if (len >= 0) {
+ char path[512];
+ memcpy(path, p_args[0], len > sizeof(path) ? sizeof(path) : len);
+ path[len] = 0;
+ chdir(path);
+ }
+}
int add_path(int p_argc, char **p_args) {
NSString *str = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"godot_path"];
@@ -70,50 +84,12 @@ int add_cmdline(int p_argc, char **p_args) {
return p_argc;
}
-int ios_main(int argc, char **argv) {
- size_t len = strlen(argv[0]);
-
- while (len--) {
- if (argv[0][len] == '/') {
- break;
- }
+int process_args(int p_argc, char **p_args, char **r_args) {
+ for (int i = 0; i < p_argc; i++) {
+ r_args[i] = p_args[i];
}
-
- if (len >= 0) {
- char path[512];
- memcpy(path, argv[0], len > sizeof(path) ? sizeof(path) : len);
- path[len] = 0;
- chdir(path);
- }
-
- os = new OS_IOS();
-
- // We must override main when testing is enabled
- TEST_MAIN_OVERRIDE
-
- char *fargv[64];
- for (int i = 0; i < argc; i++) {
- fargv[i] = argv[i];
- }
- fargv[argc] = nullptr;
- argc = add_path(argc, fargv);
- argc = add_cmdline(argc, fargv);
-
- Error err = Main::setup(fargv[0], argc - 1, &fargv[1], false);
-
- if (err != OK) {
- if (err == ERR_HELP) { // Returned by --help and --version, so success.
- return EXIT_SUCCESS;
- }
- return EXIT_FAILURE;
- }
-
- os->initialize_modules();
-
- return os->get_exit_code();
-}
-
-void ios_finish() {
- Main::cleanup();
- delete os;
+ r_args[p_argc] = nullptr;
+ p_argc = add_path(p_argc, r_args);
+ p_argc = add_cmdline(p_argc, r_args);
+ return p_argc;
}
diff --git a/drivers/apple_embedded/os_apple_embedded.h b/drivers/apple_embedded/os_apple_embedded.h
new file mode 100644
index 000000000000..6bbf31852315
--- /dev/null
+++ b/drivers/apple_embedded/os_apple_embedded.h
@@ -0,0 +1,137 @@
+/**************************************************************************/
+/* os_apple_embedded.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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. */
+/**************************************************************************/
+
+#pragma once
+
+#ifdef APPLE_EMBEDDED_ENABLED
+
+#import "apple_embedded.h"
+
+#import "drivers/apple/joypad_apple.h"
+#import "drivers/coreaudio/audio_driver_coreaudio.h"
+#include "drivers/unix/os_unix.h"
+#include "servers/audio_server.h"
+#include "servers/rendering/renderer_compositor.h"
+
+#if defined(RD_ENABLED)
+#include "servers/rendering/rendering_device.h"
+
+#if defined(VULKAN_ENABLED)
+#import "rendering_context_driver_vulkan_apple_embedded.h"
+#endif
+#endif
+
+class OS_AppleEmbedded : public OS_Unix {
+private:
+ static HashMap dynamic_symbol_lookup_table;
+ friend void register_dynamic_symbol(char *name, void *address);
+
+ AudioDriverCoreAudio audio_driver;
+
+ AppleEmbedded *apple_embedded = nullptr;
+
+ JoypadApple *joypad_apple = nullptr;
+
+ MainLoop *main_loop = nullptr;
+
+ virtual void initialize_core() override;
+ virtual void initialize() override;
+
+ virtual void initialize_joypads() override;
+
+ virtual void set_main_loop(MainLoop *p_main_loop) override;
+ virtual MainLoop *get_main_loop() const override;
+
+ virtual void delete_main_loop() override;
+
+ virtual void finalize() override;
+
+ bool is_focused = false;
+
+ CGFloat _weight_to_ct(int p_weight) const;
+ CGFloat _stretch_to_ct(int p_stretch) const;
+ String _get_default_fontname(const String &p_font_name) const;
+
+ static _FORCE_INLINE_ String get_framework_executable(const String &p_path);
+
+ void deinitialize_modules();
+
+public:
+ static OS_AppleEmbedded *get_singleton();
+
+ OS_AppleEmbedded();
+ ~OS_AppleEmbedded();
+
+ void initialize_modules();
+
+ bool iterate();
+
+ void start();
+
+ virtual void alert(const String &p_alert, const String &p_title = "ALERT!") override;
+
+ virtual Vector get_system_fonts() const override;
+ virtual Vector get_system_font_path_for_text(const String &p_font_name, const String &p_text, const String &p_locale = String(), const String &p_script = String(), int p_weight = 400, int p_stretch = 100, bool p_italic = false) const override;
+ virtual String get_system_font_path(const String &p_font_name, int p_weight = 400, int p_stretch = 100, bool p_italic = false) const override;
+
+ virtual Error open_dynamic_library(const String &p_path, void *&p_library_handle, GDExtensionData *p_data = nullptr) override;
+ virtual Error close_dynamic_library(void *p_library_handle) override;
+ virtual Error get_dynamic_library_symbol_handle(void *p_library_handle, const String &p_name, void *&p_symbol_handle, bool p_optional = false) override;
+
+ virtual String get_distribution_name() const override;
+ virtual String get_version() const override;
+ virtual String get_model_name() const override;
+
+ virtual Error shell_open(const String &p_uri) override;
+
+ virtual String get_user_data_dir(const String &p_user_dir) const override;
+
+ virtual String get_cache_path() const override;
+ virtual String get_temp_path() const override;
+
+ virtual String get_locale() const override;
+
+ virtual String get_unique_id() const override;
+ virtual String get_processor_name() const override;
+
+ virtual void vibrate_handheld(int p_duration_ms = 500, float p_amplitude = -1.0) override;
+
+ virtual bool _check_internal_feature_support(const String &p_feature) override;
+
+ void on_focus_out();
+ void on_focus_in();
+
+ void on_enter_background();
+ void on_exit_background();
+
+ virtual Rect2 calculate_boot_screen_rect(const Size2 &p_window_size, const Size2 &p_imgrect_size) const override;
+};
+
+#endif // APPLE_EMBEDDED_ENABLED
diff --git a/drivers/apple_embedded/os_apple_embedded.mm b/drivers/apple_embedded/os_apple_embedded.mm
new file mode 100644
index 000000000000..fa71c96e100f
--- /dev/null
+++ b/drivers/apple_embedded/os_apple_embedded.mm
@@ -0,0 +1,717 @@
+/**************************************************************************/
+/* os_apple_embedded.mm */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 "os_apple_embedded.h"
+
+#ifdef APPLE_EMBEDDED_ENABLED
+
+#import "app_delegate_service.h"
+#import "display_server_apple_embedded.h"
+#import "godot_view_apple_embedded.h"
+#import "terminal_logger_apple_embedded.h"
+#import "view_controller.h"
+
+#include "core/config/project_settings.h"
+#include "core/io/dir_access.h"
+#include "core/io/file_access.h"
+#include "core/io/file_access_pack.h"
+#include "drivers/unix/syslog_logger.h"
+#include "main/main.h"
+
+#import
+#import
+#import
+#import
+#include
+
+#if defined(RD_ENABLED)
+#include "servers/rendering/renderer_rd/renderer_compositor_rd.h"
+#import
+
+#if defined(VULKAN_ENABLED)
+#include "drivers/vulkan/godot_vulkan.h"
+#endif // VULKAN_ENABLED
+#endif
+
+// Initialization order between compilation units is not guaranteed,
+// so we use this as a hack to ensure certain code is called before
+// everything else, but after all units are initialized.
+typedef void (*init_callback)();
+static init_callback *apple_init_callbacks = nullptr;
+static int apple_embedded_platform_init_callbacks_count = 0;
+static int apple_embedded_platform_init_callbacks_capacity = 0;
+HashMap OS_AppleEmbedded::dynamic_symbol_lookup_table;
+
+void add_apple_embedded_platform_init_callback(init_callback cb) {
+ if (apple_embedded_platform_init_callbacks_count == apple_embedded_platform_init_callbacks_capacity) {
+ void *new_ptr = realloc(apple_init_callbacks, sizeof(cb) * (apple_embedded_platform_init_callbacks_capacity + 32));
+ if (new_ptr) {
+ apple_init_callbacks = (init_callback *)(new_ptr);
+ apple_embedded_platform_init_callbacks_capacity += 32;
+ } else {
+ ERR_FAIL_MSG("Unable to allocate memory for extension callbacks.");
+ }
+ }
+ apple_init_callbacks[apple_embedded_platform_init_callbacks_count++] = cb;
+}
+
+void register_dynamic_symbol(char *name, void *address) {
+ OS_AppleEmbedded::dynamic_symbol_lookup_table[String(name)] = address;
+}
+
+Rect2 fit_keep_aspect_centered(const Vector2 &p_container, const Vector2 &p_rect) {
+ real_t available_ratio = p_container.width / p_container.height;
+ real_t fit_ratio = p_rect.width / p_rect.height;
+ Rect2 result;
+ if (fit_ratio < available_ratio) {
+ // Fit height - we'll have horizontal gaps
+ result.size.height = p_container.height;
+ result.size.width = p_container.height * fit_ratio;
+ result.position.y = 0;
+ result.position.x = (p_container.width - result.size.width) * 0.5f;
+ } else {
+ // Fit width - we'll have vertical gaps
+ result.size.width = p_container.width;
+ result.size.height = p_container.width / fit_ratio;
+ result.position.x = 0;
+ result.position.y = (p_container.height - result.size.height) * 0.5f;
+ }
+ return result;
+}
+
+Rect2 fit_keep_aspect_covered(const Vector2 &p_container, const Vector2 &p_rect) {
+ real_t available_ratio = p_container.width / p_container.height;
+ real_t fit_ratio = p_rect.width / p_rect.height;
+ Rect2 result;
+ if (fit_ratio < available_ratio) {
+ // Need to scale up to fit width, and crop height
+ result.size.width = p_container.width;
+ result.size.height = p_container.width / fit_ratio;
+ result.position.x = 0;
+ result.position.y = (p_container.height - result.size.height) * 0.5f;
+ } else {
+ // Need to scale up to fit height, and crop width
+ result.size.width = p_container.height * fit_ratio;
+ result.size.height = p_container.height;
+ result.position.x = (p_container.width - result.size.width) * 0.5f;
+ result.position.y = 0;
+ }
+ return result;
+}
+
+OS_AppleEmbedded *OS_AppleEmbedded::get_singleton() {
+ return (OS_AppleEmbedded *)OS::get_singleton();
+}
+
+OS_AppleEmbedded::OS_AppleEmbedded() {
+ for (int i = 0; i < apple_embedded_platform_init_callbacks_count; ++i) {
+ apple_init_callbacks[i]();
+ }
+ free(apple_init_callbacks);
+ apple_init_callbacks = nullptr;
+ apple_embedded_platform_init_callbacks_count = 0;
+ apple_embedded_platform_init_callbacks_capacity = 0;
+
+ main_loop = nullptr;
+
+ Vector loggers;
+ loggers.push_back(memnew(TerminalLoggerAppleEmbedded));
+ _set_logger(memnew(CompositeLogger(loggers)));
+
+ AudioDriverManager::add_driver(&audio_driver);
+}
+
+OS_AppleEmbedded::~OS_AppleEmbedded() {}
+
+void OS_AppleEmbedded::alert(const String &p_alert, const String &p_title) {
+ const CharString utf8_alert = p_alert.utf8();
+ const CharString utf8_title = p_title.utf8();
+ AppleEmbedded::alert(utf8_alert.get_data(), utf8_title.get_data());
+}
+
+void OS_AppleEmbedded::initialize_core() {
+ OS_Unix::initialize_core();
+}
+
+void OS_AppleEmbedded::initialize() {
+ initialize_core();
+}
+
+void OS_AppleEmbedded::initialize_joypads() {
+ joypad_apple = memnew(JoypadApple);
+}
+
+void OS_AppleEmbedded::initialize_modules() {
+ apple_embedded = memnew(AppleEmbedded);
+ Engine::get_singleton()->add_singleton(Engine::Singleton("AppleEmbedded", apple_embedded));
+}
+
+void OS_AppleEmbedded::deinitialize_modules() {
+ if (joypad_apple) {
+ memdelete(joypad_apple);
+ }
+
+ if (apple_embedded) {
+ memdelete(apple_embedded);
+ }
+}
+
+void OS_AppleEmbedded::set_main_loop(MainLoop *p_main_loop) {
+ main_loop = p_main_loop;
+}
+
+MainLoop *OS_AppleEmbedded::get_main_loop() const {
+ return main_loop;
+}
+
+void OS_AppleEmbedded::delete_main_loop() {
+ if (main_loop) {
+ main_loop->finalize();
+ memdelete(main_loop);
+ }
+
+ main_loop = nullptr;
+}
+
+bool OS_AppleEmbedded::iterate() {
+ if (!main_loop) {
+ return true;
+ }
+
+ if (DisplayServer::get_singleton()) {
+ DisplayServer::get_singleton()->process_events();
+ }
+
+ joypad_apple->process_joypads();
+
+ return Main::iteration();
+}
+
+void OS_AppleEmbedded::start() {
+ if (Main::start() == EXIT_SUCCESS) {
+ main_loop->initialize();
+ }
+}
+
+void OS_AppleEmbedded::finalize() {
+ deinitialize_modules();
+
+ // Already gets called
+ //delete_main_loop();
+}
+
+// MARK: Dynamic Libraries
+
+_FORCE_INLINE_ String OS_AppleEmbedded::get_framework_executable(const String &p_path) {
+ Ref da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
+
+ // Read framework bundle to get executable name.
+ NSURL *url = [NSURL fileURLWithPath:@(p_path.utf8().get_data())];
+ NSBundle *bundle = [NSBundle bundleWithURL:url];
+ if (bundle) {
+ String exe_path = String::utf8([[bundle executablePath] UTF8String]);
+ if (da->file_exists(exe_path)) {
+ return exe_path;
+ }
+ }
+
+ // Try default executable name (invalid framework).
+ if (da->dir_exists(p_path) && da->file_exists(p_path.path_join(p_path.get_file().get_basename()))) {
+ return p_path.path_join(p_path.get_file().get_basename());
+ }
+
+ // Not a framework, try loading as .dylib.
+ return p_path;
+}
+
+Error OS_AppleEmbedded::open_dynamic_library(const String &p_path, void *&p_library_handle, GDExtensionData *p_data) {
+ if (p_path.length() == 0) {
+ // Static xcframework.
+ p_library_handle = RTLD_SELF;
+
+ if (p_data != nullptr && p_data->r_resolved_path != nullptr) {
+ *p_data->r_resolved_path = p_path;
+ }
+
+ return OK;
+ }
+
+ String path = get_framework_executable(p_path);
+
+ if (!FileAccess::exists(path)) {
+ // Load .dylib or framework from within the executable path.
+ path = get_framework_executable(get_executable_path().get_base_dir().path_join(p_path.get_file()));
+ }
+
+ if (!FileAccess::exists(path)) {
+ // Load .dylib converted to framework from within the executable path.
+ path = get_framework_executable(get_executable_path().get_base_dir().path_join(p_path.get_file().get_basename() + ".framework"));
+ }
+
+ if (!FileAccess::exists(path)) {
+ // Load .dylib or framework from a standard iOS location.
+ path = get_framework_executable(get_executable_path().get_base_dir().path_join("Frameworks").path_join(p_path.get_file()));
+ }
+
+ if (!FileAccess::exists(path)) {
+ // Load .dylib converted to framework from a standard iOS location.
+ path = get_framework_executable(get_executable_path().get_base_dir().path_join("Frameworks").path_join(p_path.get_file().get_basename() + ".framework"));
+ }
+
+ ERR_FAIL_COND_V(!FileAccess::exists(path), ERR_FILE_NOT_FOUND);
+
+ p_library_handle = dlopen(path.utf8().get_data(), RTLD_NOW);
+ ERR_FAIL_NULL_V_MSG(p_library_handle, ERR_CANT_OPEN, vformat("Can't open dynamic library: %s. Error: %s.", p_path, dlerror()));
+
+ if (p_data != nullptr && p_data->r_resolved_path != nullptr) {
+ *p_data->r_resolved_path = path;
+ }
+
+ return OK;
+}
+
+Error OS_AppleEmbedded::close_dynamic_library(void *p_library_handle) {
+ if (p_library_handle == RTLD_SELF) {
+ return OK;
+ }
+ return OS_Unix::close_dynamic_library(p_library_handle);
+}
+
+Error OS_AppleEmbedded::get_dynamic_library_symbol_handle(void *p_library_handle, const String &p_name, void *&p_symbol_handle, bool p_optional) {
+ if (p_library_handle == RTLD_SELF) {
+ void **ptr = OS_AppleEmbedded::dynamic_symbol_lookup_table.getptr(p_name);
+ if (ptr) {
+ p_symbol_handle = *ptr;
+ return OK;
+ }
+ }
+ return OS_Unix::get_dynamic_library_symbol_handle(p_library_handle, p_name, p_symbol_handle, p_optional);
+}
+
+String OS_AppleEmbedded::get_distribution_name() const {
+ return get_name();
+}
+
+String OS_AppleEmbedded::get_version() const {
+ NSOperatingSystemVersion ver = [NSProcessInfo processInfo].operatingSystemVersion;
+ return vformat("%d.%d.%d", (int64_t)ver.majorVersion, (int64_t)ver.minorVersion, (int64_t)ver.patchVersion);
+}
+
+String OS_AppleEmbedded::get_model_name() const {
+ String model = apple_embedded->get_model();
+ if (model != "") {
+ return model;
+ }
+
+ return OS_Unix::get_model_name();
+}
+
+Error OS_AppleEmbedded::shell_open(const String &p_uri) {
+ NSString *urlPath = [[NSString alloc] initWithUTF8String:p_uri.utf8().get_data()];
+ NSURL *url = [NSURL URLWithString:urlPath];
+
+ if (![[UIApplication sharedApplication] canOpenURL:url]) {
+ return ERR_CANT_OPEN;
+ }
+
+ print_verbose(vformat("Opening URL %s", p_uri));
+
+ [[UIApplication sharedApplication] openURL:url options:@{} completionHandler:nil];
+
+ return OK;
+}
+
+String OS_AppleEmbedded::get_user_data_dir(const String &p_user_dir) const {
+ static String ret;
+ if (ret.is_empty()) {
+ NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
+ if (paths && [paths count] >= 1) {
+ ret.append_utf8([[paths firstObject] UTF8String]);
+ }
+ }
+ return ret;
+}
+
+String OS_AppleEmbedded::get_cache_path() const {
+ static String ret;
+ if (ret.is_empty()) {
+ NSArray *paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES);
+ if (paths && [paths count] >= 1) {
+ ret.append_utf8([[paths firstObject] UTF8String]);
+ }
+ }
+ return ret;
+}
+
+String OS_AppleEmbedded::get_temp_path() const {
+ static String ret;
+ if (ret.is_empty()) {
+ NSURL *url = [NSURL fileURLWithPath:NSTemporaryDirectory()
+ isDirectory:YES];
+ if (url) {
+ ret = String::utf8([url.path UTF8String]);
+ ret = ret.trim_prefix("file://");
+ }
+ }
+ return ret;
+}
+
+String OS_AppleEmbedded::get_locale() const {
+ NSString *preferredLanguage = [NSLocale preferredLanguages].firstObject;
+
+ if (preferredLanguage) {
+ return String::utf8([preferredLanguage UTF8String]).replace_char('-', '_');
+ }
+
+ NSString *localeIdentifier = [[NSLocale currentLocale] localeIdentifier];
+ return String::utf8([localeIdentifier UTF8String]).replace_char('-', '_');
+}
+
+String OS_AppleEmbedded::get_unique_id() const {
+ NSString *uuid = [UIDevice currentDevice].identifierForVendor.UUIDString;
+ return String::utf8([uuid UTF8String]);
+}
+
+String OS_AppleEmbedded::get_processor_name() const {
+ char buffer[256];
+ size_t buffer_len = 256;
+ if (sysctlbyname("machdep.cpu.brand_string", &buffer, &buffer_len, nullptr, 0) == 0) {
+ return String::utf8(buffer, buffer_len);
+ }
+ ERR_FAIL_V_MSG("", String("Couldn't get the CPU model name. Returning an empty string."));
+}
+
+Vector OS_AppleEmbedded::get_system_fonts() const {
+ HashSet font_names;
+ CFArrayRef fonts = CTFontManagerCopyAvailableFontFamilyNames();
+ if (fonts) {
+ for (CFIndex i = 0; i < CFArrayGetCount(fonts); i++) {
+ CFStringRef cf_name = (CFStringRef)CFArrayGetValueAtIndex(fonts, i);
+ if (cf_name && (CFStringGetLength(cf_name) > 0) && (CFStringCompare(cf_name, CFSTR("LastResort"), kCFCompareCaseInsensitive) != kCFCompareEqualTo) && (CFStringGetCharacterAtIndex(cf_name, 0) != '.')) {
+ NSString *ns_name = (__bridge NSString *)cf_name;
+ font_names.insert(String::utf8([ns_name UTF8String]));
+ }
+ }
+ CFRelease(fonts);
+ }
+
+ Vector ret;
+ for (const String &E : font_names) {
+ ret.push_back(E);
+ }
+ return ret;
+}
+
+String OS_AppleEmbedded::_get_default_fontname(const String &p_font_name) const {
+ String font_name = p_font_name;
+ if (font_name.to_lower() == "sans-serif") {
+ font_name = "Helvetica";
+ } else if (font_name.to_lower() == "serif") {
+ font_name = "Times";
+ } else if (font_name.to_lower() == "monospace") {
+ font_name = "Courier";
+ } else if (font_name.to_lower() == "fantasy") {
+ font_name = "Papyrus";
+ } else if (font_name.to_lower() == "cursive") {
+ font_name = "Apple Chancery";
+ };
+ return font_name;
+}
+
+CGFloat OS_AppleEmbedded::_weight_to_ct(int p_weight) const {
+ if (p_weight < 150) {
+ return -0.80;
+ } else if (p_weight < 250) {
+ return -0.60;
+ } else if (p_weight < 350) {
+ return -0.40;
+ } else if (p_weight < 450) {
+ return 0.0;
+ } else if (p_weight < 550) {
+ return 0.23;
+ } else if (p_weight < 650) {
+ return 0.30;
+ } else if (p_weight < 750) {
+ return 0.40;
+ } else if (p_weight < 850) {
+ return 0.56;
+ } else if (p_weight < 925) {
+ return 0.62;
+ } else {
+ return 1.00;
+ }
+}
+
+CGFloat OS_AppleEmbedded::_stretch_to_ct(int p_stretch) const {
+ if (p_stretch < 56) {
+ return -0.5;
+ } else if (p_stretch < 69) {
+ return -0.37;
+ } else if (p_stretch < 81) {
+ return -0.25;
+ } else if (p_stretch < 93) {
+ return -0.13;
+ } else if (p_stretch < 106) {
+ return 0.0;
+ } else if (p_stretch < 137) {
+ return 0.13;
+ } else if (p_stretch < 144) {
+ return 0.25;
+ } else if (p_stretch < 162) {
+ return 0.37;
+ } else {
+ return 0.5;
+ }
+}
+
+Vector OS_AppleEmbedded::get_system_font_path_for_text(const String &p_font_name, const String &p_text, const String &p_locale, const String &p_script, int p_weight, int p_stretch, bool p_italic) const {
+ Vector ret;
+ String font_name = _get_default_fontname(p_font_name);
+
+ CFStringRef name = CFStringCreateWithCString(kCFAllocatorDefault, font_name.utf8().get_data(), kCFStringEncodingUTF8);
+ CTFontSymbolicTraits traits = 0;
+ if (p_weight >= 700) {
+ traits |= kCTFontBoldTrait;
+ }
+ if (p_italic) {
+ traits |= kCTFontItalicTrait;
+ }
+ if (p_stretch < 100) {
+ traits |= kCTFontCondensedTrait;
+ } else if (p_stretch > 100) {
+ traits |= kCTFontExpandedTrait;
+ }
+
+ CFNumberRef sym_traits = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &traits);
+ CFMutableDictionaryRef traits_dict = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, nullptr, nullptr);
+ CFDictionaryAddValue(traits_dict, kCTFontSymbolicTrait, sym_traits);
+
+ CGFloat weight = _weight_to_ct(p_weight);
+ CFNumberRef font_weight = CFNumberCreate(kCFAllocatorDefault, kCFNumberCGFloatType, &weight);
+ CFDictionaryAddValue(traits_dict, kCTFontWeightTrait, font_weight);
+
+ CGFloat stretch = _stretch_to_ct(p_stretch);
+ CFNumberRef font_stretch = CFNumberCreate(kCFAllocatorDefault, kCFNumberCGFloatType, &stretch);
+ CFDictionaryAddValue(traits_dict, kCTFontWidthTrait, font_stretch);
+
+ CFMutableDictionaryRef attributes = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, nullptr, nullptr);
+ CFDictionaryAddValue(attributes, kCTFontFamilyNameAttribute, name);
+ CFDictionaryAddValue(attributes, kCTFontTraitsAttribute, traits_dict);
+
+ CTFontDescriptorRef font = CTFontDescriptorCreateWithAttributes(attributes);
+ if (font) {
+ CTFontRef family = CTFontCreateWithFontDescriptor(font, 0, nullptr);
+ if (family) {
+ CFStringRef string = CFStringCreateWithCString(kCFAllocatorDefault, p_text.utf8().get_data(), kCFStringEncodingUTF8);
+ CFRange range = CFRangeMake(0, CFStringGetLength(string));
+ CTFontRef fallback_family = CTFontCreateForString(family, string, range);
+ if (fallback_family) {
+ CTFontDescriptorRef fallback_font = CTFontCopyFontDescriptor(fallback_family);
+ if (fallback_font) {
+ CFURLRef url = (CFURLRef)CTFontDescriptorCopyAttribute(fallback_font, kCTFontURLAttribute);
+ if (url) {
+ NSString *font_path = [NSString stringWithString:[(__bridge NSURL *)url path]];
+ ret.push_back(String::utf8([font_path UTF8String]));
+ CFRelease(url);
+ }
+ CFRelease(fallback_font);
+ }
+ CFRelease(fallback_family);
+ }
+ CFRelease(string);
+ CFRelease(family);
+ }
+ CFRelease(font);
+ }
+
+ CFRelease(attributes);
+ CFRelease(traits_dict);
+ CFRelease(sym_traits);
+ CFRelease(font_stretch);
+ CFRelease(font_weight);
+ CFRelease(name);
+
+ return ret;
+}
+
+String OS_AppleEmbedded::get_system_font_path(const String &p_font_name, int p_weight, int p_stretch, bool p_italic) const {
+ String ret;
+ String font_name = _get_default_fontname(p_font_name);
+
+ CFStringRef name = CFStringCreateWithCString(kCFAllocatorDefault, font_name.utf8().get_data(), kCFStringEncodingUTF8);
+
+ CTFontSymbolicTraits traits = 0;
+ if (p_weight >= 700) {
+ traits |= kCTFontBoldTrait;
+ }
+ if (p_italic) {
+ traits |= kCTFontItalicTrait;
+ }
+ if (p_stretch < 100) {
+ traits |= kCTFontCondensedTrait;
+ } else if (p_stretch > 100) {
+ traits |= kCTFontExpandedTrait;
+ }
+
+ CFNumberRef sym_traits = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &traits);
+ CFMutableDictionaryRef traits_dict = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, nullptr, nullptr);
+ CFDictionaryAddValue(traits_dict, kCTFontSymbolicTrait, sym_traits);
+
+ CGFloat weight = _weight_to_ct(p_weight);
+ CFNumberRef font_weight = CFNumberCreate(kCFAllocatorDefault, kCFNumberCGFloatType, &weight);
+ CFDictionaryAddValue(traits_dict, kCTFontWeightTrait, font_weight);
+
+ CGFloat stretch = _stretch_to_ct(p_stretch);
+ CFNumberRef font_stretch = CFNumberCreate(kCFAllocatorDefault, kCFNumberCGFloatType, &stretch);
+ CFDictionaryAddValue(traits_dict, kCTFontWidthTrait, font_stretch);
+
+ CFMutableDictionaryRef attributes = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, nullptr, nullptr);
+ CFDictionaryAddValue(attributes, kCTFontFamilyNameAttribute, name);
+ CFDictionaryAddValue(attributes, kCTFontTraitsAttribute, traits_dict);
+
+ CTFontDescriptorRef font = CTFontDescriptorCreateWithAttributes(attributes);
+ if (font) {
+ CFURLRef url = (CFURLRef)CTFontDescriptorCopyAttribute(font, kCTFontURLAttribute);
+ if (url) {
+ NSString *font_path = [NSString stringWithString:[(__bridge NSURL *)url path]];
+ ret = String::utf8([font_path UTF8String]);
+ CFRelease(url);
+ }
+ CFRelease(font);
+ }
+
+ CFRelease(attributes);
+ CFRelease(traits_dict);
+ CFRelease(sym_traits);
+ CFRelease(font_stretch);
+ CFRelease(font_weight);
+ CFRelease(name);
+
+ return ret;
+}
+
+void OS_AppleEmbedded::vibrate_handheld(int p_duration_ms, float p_amplitude) {
+ if (apple_embedded->supports_haptic_engine()) {
+ if (p_amplitude > 0.0) {
+ p_amplitude = CLAMP(p_amplitude, 0.0, 1.0);
+ }
+
+ apple_embedded->vibrate_haptic_engine((float)p_duration_ms / 1000.f, p_amplitude);
+ } else {
+ // iOS <13 does not support duration for vibration
+ AudioServicesPlaySystemSound(kSystemSoundID_Vibrate);
+ }
+}
+
+bool OS_AppleEmbedded::_check_internal_feature_support(const String &p_feature) {
+ if (p_feature == "system_fonts") {
+ return true;
+ }
+ if (p_feature == "mobile") {
+ return true;
+ }
+
+ return false;
+}
+
+void OS_AppleEmbedded::on_focus_out() {
+ if (is_focused) {
+ is_focused = false;
+
+ if (DisplayServerAppleEmbedded::get_singleton()) {
+ DisplayServerAppleEmbedded::get_singleton()->send_window_event(DisplayServer::WINDOW_EVENT_FOCUS_OUT);
+ }
+
+ if (OS::get_singleton()->get_main_loop()) {
+ OS::get_singleton()->get_main_loop()->notification(MainLoop::NOTIFICATION_APPLICATION_FOCUS_OUT);
+ }
+
+ [GDTAppDelegateService.viewController.godotView stopRendering];
+
+ audio_driver.stop();
+ }
+}
+
+void OS_AppleEmbedded::on_focus_in() {
+ if (!is_focused) {
+ is_focused = true;
+
+ if (DisplayServerAppleEmbedded::get_singleton()) {
+ DisplayServerAppleEmbedded::get_singleton()->send_window_event(DisplayServer::WINDOW_EVENT_FOCUS_IN);
+ }
+
+ if (OS::get_singleton()->get_main_loop()) {
+ OS::get_singleton()->get_main_loop()->notification(MainLoop::NOTIFICATION_APPLICATION_FOCUS_IN);
+ }
+
+ [GDTAppDelegateService.viewController.godotView startRendering];
+
+ audio_driver.start();
+ }
+}
+
+void OS_AppleEmbedded::on_enter_background() {
+ // Do not check for is_focused, because on_focus_out will always be fired first by applicationWillResignActive.
+
+ if (OS::get_singleton()->get_main_loop()) {
+ OS::get_singleton()->get_main_loop()->notification(MainLoop::NOTIFICATION_APPLICATION_PAUSED);
+ }
+
+ on_focus_out();
+}
+
+void OS_AppleEmbedded::on_exit_background() {
+ if (!is_focused) {
+ on_focus_in();
+
+ if (OS::get_singleton()->get_main_loop()) {
+ OS::get_singleton()->get_main_loop()->notification(MainLoop::NOTIFICATION_APPLICATION_RESUMED);
+ }
+ }
+}
+
+Rect2 OS_AppleEmbedded::calculate_boot_screen_rect(const Size2 &p_window_size, const Size2 &p_imgrect_size) const {
+ String scalemodestr = GLOBAL_GET("ios/launch_screen_image_mode");
+
+ if (scalemodestr == "scaleAspectFit") {
+ return fit_keep_aspect_centered(p_window_size, p_imgrect_size);
+ } else if (scalemodestr == "scaleAspectFill") {
+ return fit_keep_aspect_covered(p_window_size, p_imgrect_size);
+ } else if (scalemodestr == "scaleToFill") {
+ return Rect2(Point2(), p_window_size);
+ } else if (scalemodestr == "center") {
+ return OS_Unix::calculate_boot_screen_rect(p_window_size, p_imgrect_size);
+ } else {
+ WARN_PRINT(vformat("Boot screen scale mode mismatch between iOS and Godot: %s not supported", scalemodestr));
+ return OS_Unix::calculate_boot_screen_rect(p_window_size, p_imgrect_size);
+ }
+}
+
+#endif // APPLE_EMBEDDED_ENABLED
diff --git a/drivers/apple_embedded/platform_config.h b/drivers/apple_embedded/platform_config.h
new file mode 100644
index 000000000000..bc8b45603ab5
--- /dev/null
+++ b/drivers/apple_embedded/platform_config.h
@@ -0,0 +1,44 @@
+/**************************************************************************/
+/* platform_config.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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. */
+/**************************************************************************/
+
+#pragma once
+
+#include
+
+#define PLATFORM_THREAD_OVERRIDE
+
+#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/ios/rendering_context_driver_vulkan_ios.h b/drivers/apple_embedded/rendering_context_driver_vulkan_apple_embedded.h
similarity index 91%
rename from platform/ios/rendering_context_driver_vulkan_ios.h
rename to drivers/apple_embedded/rendering_context_driver_vulkan_apple_embedded.h
index 07a08cc8613c..8e12bc163a64 100644
--- a/platform/ios/rendering_context_driver_vulkan_ios.h
+++ b/drivers/apple_embedded/rendering_context_driver_vulkan_apple_embedded.h
@@ -1,5 +1,5 @@
/**************************************************************************/
-/* rendering_context_driver_vulkan_ios.h */
+/* rendering_context_driver_vulkan_apple_embedded.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
@@ -36,7 +36,7 @@
#import
-class RenderingContextDriverVulkanIOS : public RenderingContextDriverVulkan {
+class RenderingContextDriverVulkanAppleEmbedded : public RenderingContextDriverVulkan {
private:
virtual const char *_get_platform_surface_extension() const override final;
@@ -48,8 +48,8 @@ class RenderingContextDriverVulkanIOS : public RenderingContextDriverVulkan {
CAMetalLayer *const *layer_ptr;
};
- RenderingContextDriverVulkanIOS();
- ~RenderingContextDriverVulkanIOS();
+ RenderingContextDriverVulkanAppleEmbedded();
+ ~RenderingContextDriverVulkanAppleEmbedded();
};
#endif // VULKAN_ENABLED
diff --git a/platform/ios/rendering_context_driver_vulkan_ios.mm b/drivers/apple_embedded/rendering_context_driver_vulkan_apple_embedded.mm
similarity index 85%
rename from platform/ios/rendering_context_driver_vulkan_ios.mm
rename to drivers/apple_embedded/rendering_context_driver_vulkan_apple_embedded.mm
index 8747bfd76abd..f25931377663 100644
--- a/platform/ios/rendering_context_driver_vulkan_ios.mm
+++ b/drivers/apple_embedded/rendering_context_driver_vulkan_apple_embedded.mm
@@ -1,5 +1,5 @@
/**************************************************************************/
-/* rendering_context_driver_vulkan_ios.mm */
+/* rendering_context_driver_vulkan_apple_embedded.mm */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
@@ -28,7 +28,7 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
-#import "rendering_context_driver_vulkan_ios.h"
+#import "rendering_context_driver_vulkan_apple_embedded.h"
#ifdef VULKAN_ENABLED
@@ -38,11 +38,11 @@
#include
#endif
-const char *RenderingContextDriverVulkanIOS::_get_platform_surface_extension() const {
+const char *RenderingContextDriverVulkanAppleEmbedded::_get_platform_surface_extension() const {
return VK_EXT_METAL_SURFACE_EXTENSION_NAME;
}
-RenderingContextDriver::SurfaceID RenderingContextDriverVulkanIOS::surface_create(const void *p_platform_data) {
+RenderingContextDriver::SurfaceID RenderingContextDriverVulkanAppleEmbedded::surface_create(const void *p_platform_data) {
const WindowPlatformData *wpd = (const WindowPlatformData *)(p_platform_data);
VkMetalSurfaceCreateInfoEXT create_info = {};
@@ -58,11 +58,11 @@
return SurfaceID(surface);
}
-RenderingContextDriverVulkanIOS::RenderingContextDriverVulkanIOS() {
+RenderingContextDriverVulkanAppleEmbedded::RenderingContextDriverVulkanAppleEmbedded() {
// Does nothing.
}
-RenderingContextDriverVulkanIOS::~RenderingContextDriverVulkanIOS() {
+RenderingContextDriverVulkanAppleEmbedded::~RenderingContextDriverVulkanAppleEmbedded() {
// Does nothing.
}
diff --git a/platform/ios/ios_terminal_logger.h b/drivers/apple_embedded/terminal_logger_apple_embedded.h
similarity index 93%
rename from platform/ios/ios_terminal_logger.h
rename to drivers/apple_embedded/terminal_logger_apple_embedded.h
index 7f4f0445d28c..046d30017589 100644
--- a/platform/ios/ios_terminal_logger.h
+++ b/drivers/apple_embedded/terminal_logger_apple_embedded.h
@@ -1,5 +1,5 @@
/**************************************************************************/
-/* ios_terminal_logger.h */
+/* terminal_logger_apple_embedded.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
@@ -30,13 +30,13 @@
#pragma once
-#ifdef IOS_ENABLED
+#ifdef APPLE_EMBEDDED_ENABLED
#include "core/io/logger.h"
-class IOSTerminalLogger : public StdLogger {
+class TerminalLoggerAppleEmbedded : public StdLogger {
public:
virtual void log_error(const char *p_function, const char *p_file, int p_line, const char *p_code, const char *p_rationale, bool p_editor_notify = false, ErrorType p_type = ERR_ERROR, const Vector[> &p_script_backtraces = {}) override;
};
-#endif // IOS_ENABLED
+#endif // APPLE_EMBEDDED_ENABLED
diff --git a/platform/ios/ios_terminal_logger.mm b/drivers/apple_embedded/terminal_logger_apple_embedded.mm
similarity index 88%
rename from platform/ios/ios_terminal_logger.mm
rename to drivers/apple_embedded/terminal_logger_apple_embedded.mm
index 0dfd7b890fad..647d371c1190 100644
--- a/platform/ios/ios_terminal_logger.mm
+++ b/drivers/apple_embedded/terminal_logger_apple_embedded.mm
@@ -1,5 +1,5 @@
/**************************************************************************/
-/* ios_terminal_logger.mm */
+/* terminal_logger_apple_embedded.mm */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
@@ -28,13 +28,13 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
-#import "ios_terminal_logger.h"
+#import "terminal_logger_apple_embedded.h"
-#ifdef IOS_ENABLED
+#ifdef APPLE_EMBEDDED_ENABLED
#import
-void IOSTerminalLogger::log_error(const char *p_function, const char *p_file, int p_line, const char *p_code, const char *p_rationale, bool p_editor_notify, ErrorType p_type, const Vector][> &p_script_backtraces) {
+void TerminalLoggerAppleEmbedded::log_error(const char *p_function, const char *p_file, int p_line, const char *p_code, const char *p_rationale, bool p_editor_notify, ErrorType p_type, const Vector][> &p_script_backtraces) {
if (!should_log(true)) {
return;
}
@@ -77,4 +77,4 @@
}
}
-#endif // IOS_ENABLED
+#endif // APPLE_EMBEDDED_ENABLED
diff --git a/platform/ios/tts_ios.h b/drivers/apple_embedded/tts_apple_embedded.h
similarity index 95%
rename from platform/ios/tts_ios.h
rename to drivers/apple_embedded/tts_apple_embedded.h
index 0282a084dfcd..33103b2578cc 100644
--- a/platform/ios/tts_ios.h
+++ b/drivers/apple_embedded/tts_apple_embedded.h
@@ -1,5 +1,5 @@
/**************************************************************************/
-/* tts_ios.h */
+/* tts_apple_embedded.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
@@ -42,7 +42,7 @@
#import
#endif
-@interface TTS_IOS : NSObject {
+@interface GDTTTS : NSObject {
bool speaking;
HashMap ids;
diff --git a/platform/ios/tts_ios.mm b/drivers/apple_embedded/tts_apple_embedded.mm
similarity index 98%
rename from platform/ios/tts_ios.mm
rename to drivers/apple_embedded/tts_apple_embedded.mm
index 33b30b17c906..1cd67b0bcea8 100644
--- a/platform/ios/tts_ios.mm
+++ b/drivers/apple_embedded/tts_apple_embedded.mm
@@ -1,5 +1,5 @@
/**************************************************************************/
-/* tts_ios.mm */
+/* tts_apple_embedded.mm */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
@@ -28,9 +28,9 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
-#import "tts_ios.h"
+#import "tts_apple_embedded.h"
-@implementation TTS_IOS
+@implementation GDTTTS
- (id)init {
self = [super init];
diff --git a/platform/ios/view_controller.h b/drivers/apple_embedded/view_controller.h
similarity index 89%
rename from platform/ios/view_controller.h
rename to drivers/apple_embedded/view_controller.h
index b39164ca1a5a..949e3040c906 100644
--- a/platform/ios/view_controller.h
+++ b/drivers/apple_embedded/view_controller.h
@@ -32,13 +32,12 @@
#import
-@class GodotView;
-@class GodotNativeVideoView;
-@class GodotKeyboardInputView;
+@class GDTView;
+@class GDTKeyboardInputView;
-@interface ViewController : UIViewController
+@interface GDTViewController : UIViewController
-@property(nonatomic, readonly, strong) GodotView *godotView;
-@property(nonatomic, readonly, strong) GodotKeyboardInputView *keyboardView;
+@property(nonatomic, readonly, strong) GDTView *godotView;
+@property(nonatomic, readonly, strong) GDTKeyboardInputView *keyboardView;
@end
diff --git a/platform/ios/view_controller.mm b/drivers/apple_embedded/view_controller.mm
similarity index 78%
rename from platform/ios/view_controller.mm
rename to drivers/apple_embedded/view_controller.mm
index 787e767109d2..15dc1daf16e2 100644
--- a/platform/ios/view_controller.mm
+++ b/drivers/apple_embedded/view_controller.mm
@@ -30,44 +30,44 @@
#import "view_controller.h"
-#import "display_server_ios.h"
-#import "godot_view.h"
+#import "display_server_apple_embedded.h"
+#import "godot_view_apple_embedded.h"
#import "godot_view_renderer.h"
-#import "key_mapping_ios.h"
+#import "key_mapping_apple_embedded.h"
#import "keyboard_input_view.h"
-#import "os_ios.h"
+#import "os_apple_embedded.h"
#include "core/config/project_settings.h"
#import
#import
-@interface ViewController ()
+@interface GDTViewController ()
-@property(strong, nonatomic) GodotViewRenderer *renderer;
-@property(strong, nonatomic) GodotKeyboardInputView *keyboardView;
+@property(strong, nonatomic) GDTViewRenderer *renderer;
+@property(strong, nonatomic) GDTKeyboardInputView *keyboardView;
@property(strong, nonatomic) UIView *godotLoadingOverlay;
@end
-@implementation ViewController
+@implementation GDTViewController
-- (GodotView *)godotView {
- return (GodotView *)self.view;
+- (GDTView *)godotView {
+ return (GDTView *)self.view;
}
- (void)pressesBegan:(NSSet *)presses withEvent:(UIPressesEvent *)event {
[super pressesBegan:presses withEvent:event];
- if (!DisplayServerIOS::get_singleton() || DisplayServerIOS::get_singleton()->is_keyboard_active()) {
+ if (!DisplayServerAppleEmbedded::get_singleton() || DisplayServerAppleEmbedded::get_singleton()->is_keyboard_active()) {
return;
}
if (@available(iOS 13.4, *)) {
for (UIPress *press in presses) {
String u32lbl = String::utf8([press.key.charactersIgnoringModifiers UTF8String]);
String u32text = String::utf8([press.key.characters UTF8String]);
- Key key = KeyMappingIOS::remap_key(press.key.keyCode);
+ Key key = KeyMappingAppleEmbedded::remap_key(press.key.keyCode);
if (press.key.keyCode == 0 && u32text.is_empty() && u32lbl.is_empty()) {
continue;
@@ -78,15 +78,15 @@ - (void)pressesBegan:(NSSet *)presses withEvent:(UIPressesEvent *)eve
us = u32lbl[0];
}
- KeyLocation location = KeyMappingIOS::key_location(press.key.keyCode);
+ KeyLocation location = KeyMappingAppleEmbedded::key_location(press.key.keyCode);
if (!u32text.is_empty() && !u32text.begins_with("UIKey")) {
for (int i = 0; i < u32text.length(); i++) {
const char32_t c = u32text[i];
- DisplayServerIOS::get_singleton()->key(fix_keycode(us, key), c, fix_key_label(us, key), key, press.key.modifierFlags, true, location);
+ DisplayServerAppleEmbedded::get_singleton()->key(fix_keycode(us, key), c, fix_key_label(us, key), key, press.key.modifierFlags, true, location);
}
} else {
- DisplayServerIOS::get_singleton()->key(fix_keycode(us, key), 0, fix_key_label(us, key), key, press.key.modifierFlags, true, location);
+ DisplayServerAppleEmbedded::get_singleton()->key(fix_keycode(us, key), 0, fix_key_label(us, key), key, press.key.modifierFlags, true, location);
}
}
}
@@ -95,13 +95,13 @@ - (void)pressesBegan:(NSSet *)presses withEvent:(UIPressesEvent *)eve
- (void)pressesEnded:(NSSet *)presses withEvent:(UIPressesEvent *)event {
[super pressesEnded:presses withEvent:event];
- if (!DisplayServerIOS::get_singleton() || DisplayServerIOS::get_singleton()->is_keyboard_active()) {
+ if (!DisplayServerAppleEmbedded::get_singleton() || DisplayServerAppleEmbedded::get_singleton()->is_keyboard_active()) {
return;
}
if (@available(iOS 13.4, *)) {
for (UIPress *press in presses) {
String u32lbl = String::utf8([press.key.charactersIgnoringModifiers UTF8String]);
- Key key = KeyMappingIOS::remap_key(press.key.keyCode);
+ Key key = KeyMappingAppleEmbedded::remap_key(press.key.keyCode);
if (press.key.keyCode == 0 && u32lbl.is_empty()) {
continue;
@@ -112,16 +112,16 @@ - (void)pressesEnded:(NSSet *)presses withEvent:(UIPressesEvent *)eve
us = u32lbl[0];
}
- KeyLocation location = KeyMappingIOS::key_location(press.key.keyCode);
+ KeyLocation location = KeyMappingAppleEmbedded::key_location(press.key.keyCode);
- DisplayServerIOS::get_singleton()->key(fix_keycode(us, key), 0, fix_key_label(us, key), key, press.key.modifierFlags, false, location);
+ DisplayServerAppleEmbedded::get_singleton()->key(fix_keycode(us, key), 0, fix_key_label(us, key), key, press.key.modifierFlags, false, location);
}
}
}
- (void)loadView {
- GodotView *view = [[GodotView alloc] init];
- GodotViewRenderer *renderer = [[GodotViewRenderer alloc] init];
+ GDTView *view = GDTViewCreate();
+ GDTViewRenderer *renderer = [[GDTViewRenderer alloc] init];
self.renderer = renderer;
self.view = view;
@@ -170,7 +170,7 @@ - (void)viewDidLoad {
- (void)observeKeyboard {
print_verbose("Setting up keyboard input view.");
- self.keyboardView = [GodotKeyboardInputView new];
+ self.keyboardView = [GDTKeyboardInputView new];
[self.view addSubview:self.keyboardView];
print_verbose("Adding observer for keyboard show/hide.");
@@ -187,6 +187,7 @@ - (void)observeKeyboard {
}
- (void)displayLoadingOverlay {
+#if !defined(VISIONOS_ENABLED)
NSBundle *bundle = [NSBundle mainBundle];
NSString *storyboardName = @"Launch Screen";
@@ -202,9 +203,10 @@ - (void)displayLoadingOverlay {
self.godotLoadingOverlay.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth;
[self.view addSubview:self.godotLoadingOverlay];
+#endif
}
-- (BOOL)godotViewFinishedSetup:(GodotView *)view {
+- (BOOL)godotViewFinishedSetup:(GDTView *)view {
[self.godotLoadingOverlay removeFromSuperview];
self.godotLoadingOverlay = nil;
@@ -235,11 +237,11 @@ - (UIRectEdge)preferredScreenEdgesDeferringSystemGestures {
}
- (BOOL)shouldAutorotate {
- if (!DisplayServerIOS::get_singleton()) {
+ if (!DisplayServerAppleEmbedded::get_singleton()) {
return NO;
}
- switch (DisplayServerIOS::get_singleton()->screen_get_orientation(DisplayServer::SCREEN_OF_MAIN_WINDOW)) {
+ switch (DisplayServerAppleEmbedded::get_singleton()->screen_get_orientation(DisplayServer::SCREEN_OF_MAIN_WINDOW)) {
case DisplayServer::SCREEN_SENSOR:
case DisplayServer::SCREEN_SENSOR_LANDSCAPE:
case DisplayServer::SCREEN_SENSOR_PORTRAIT:
@@ -250,11 +252,11 @@ - (BOOL)shouldAutorotate {
}
- (UIInterfaceOrientationMask)supportedInterfaceOrientations {
- if (!DisplayServerIOS::get_singleton()) {
+ if (!DisplayServerAppleEmbedded::get_singleton()) {
return UIInterfaceOrientationMaskAll;
}
- switch (DisplayServerIOS::get_singleton()->screen_get_orientation(DisplayServer::SCREEN_OF_MAIN_WINDOW)) {
+ switch (DisplayServerAppleEmbedded::get_singleton()->screen_get_orientation(DisplayServer::SCREEN_OF_MAIN_WINDOW)) {
case DisplayServer::SCREEN_PORTRAIT:
return UIInterfaceOrientationMaskPortrait;
case DisplayServer::SCREEN_REVERSE_LANDSCAPE:
@@ -305,14 +307,14 @@ - (void)keyboardOnScreen:(NSNotification *)notification {
CGRect rawFrame = [value CGRectValue];
CGRect keyboardFrame = [self.view convertRect:rawFrame fromView:nil];
- if (DisplayServerIOS::get_singleton()) {
- DisplayServerIOS::get_singleton()->virtual_keyboard_set_height(keyboardFrame.size.height);
+ if (DisplayServerAppleEmbedded::get_singleton()) {
+ DisplayServerAppleEmbedded::get_singleton()->virtual_keyboard_set_height(keyboardFrame.size.height);
}
}
- (void)keyboardHidden:(NSNotification *)notification {
- if (DisplayServerIOS::get_singleton()) {
- DisplayServerIOS::get_singleton()->virtual_keyboard_set_height(0);
+ if (DisplayServerAppleEmbedded::get_singleton()) {
+ DisplayServerAppleEmbedded::get_singleton()->virtual_keyboard_set_height(0);
}
}
diff --git a/drivers/coremidi/midi_driver_coremidi.mm b/drivers/coremidi/midi_driver_coremidi.mm
index b280d20960ab..9c83556177f7 100644
--- a/drivers/coremidi/midi_driver_coremidi.mm
+++ b/drivers/coremidi/midi_driver_coremidi.mm
@@ -34,7 +34,6 @@
#include "core/string/print_string.h"
-#import
#import
Mutex MIDIDriverCoreMidi::mutex;
diff --git a/drivers/metal/SCsub b/drivers/metal/SCsub
index ced6dfbbfdcf..a4c1c65b8297 100644
--- a/drivers/metal/SCsub
+++ b/drivers/metal/SCsub
@@ -39,6 +39,9 @@ if "-std=gnu++17" in env_metal["CXXFLAGS"]:
env_metal["CXXFLAGS"].remove("-std=gnu++17")
env_metal.Append(CXXFLAGS=["-std=c++20"])
+# Enable module support
+env_metal.Append(CCFLAGS=["-fmodules", "-fcxx-modules"])
+
# Driver source files
driver_obj = []
diff --git a/drivers/metal/metal_device_properties.mm b/drivers/metal/metal_device_properties.mm
index 713f69e41dfe..1d3b78a1b69f 100644
--- a/drivers/metal/metal_device_properties.mm
+++ b/drivers/metal/metal_device_properties.mm
@@ -50,6 +50,8 @@
#import "metal_device_properties.h"
+#include "servers/rendering/renderer_rd/effects/metal_fx.h"
+
#import
#import
#import
@@ -63,7 +65,7 @@
#define MTLGPUFamilyApple9 (MTLGPUFamily)1009
#endif
-API_AVAILABLE(macos(11.0), ios(14.0), tvos(14.0))
+API_AVAILABLE(macos(11.0), ios(14.0), tvos(14.0), visionos(1.0))
MTLGPUFamily &operator--(MTLGPUFamily &p_family) {
p_family = static_cast(static_cast(p_family) - 1);
if (p_family < MTLGPUFamilyApple1) {
@@ -126,7 +128,11 @@
if (@available(macOS 13.0, iOS 16.0, tvOS 16.0, *)) {
features.metal_fx_spatial = [MTLFXSpatialScalerDescriptor supportsDevice:p_device];
+#ifdef METAL_MFXTEMPORAL_ENABLED
features.metal_fx_temporal = [MTLFXTemporalScalerDescriptor supportsDevice:p_device];
+#else
+ features.metal_fx_temporal = false;
+#endif
}
MTLCompileOptions *opts = [MTLCompileOptions new];
@@ -136,7 +142,7 @@
features.mslVersion = SPIRV_CROSS_NAMESPACE::CompilerMSL::Options::make_msl_version(m_maj, m_min)
switch (features.mslVersionEnum) {
-#if __MAC_OS_X_VERSION_MAX_ALLOWED >= 150000 || __IPHONE_OS_VERSION_MAX_ALLOWED >= 180000 || __TV_OS_VERSION_MAX_ALLOWED >= 180000
+#if __MAC_OS_X_VERSION_MAX_ALLOWED >= 150000 || __IPHONE_OS_VERSION_MAX_ALLOWED >= 180000 || __TV_OS_VERSION_MAX_ALLOWED >= 180000 || __VISION_OS_VERSION_MAX_ALLOWED >= 20000
case MTLLanguageVersion3_2:
setMSLVersion(3, 2);
break;
@@ -170,7 +176,7 @@
case MTLLanguageVersion1_1:
setMSLVersion(1, 1);
break;
-#if TARGET_OS_IPHONE && !TARGET_OS_MACCATALYST
+#if TARGET_OS_IPHONE && !TARGET_OS_MACCATALYST && !TARGET_OS_VISION
case MTLLanguageVersion1_0:
setMSLVersion(1, 0);
break;
@@ -324,6 +330,7 @@
limits.maxDrawIndexedIndexValue = std::numeric_limits::max() - 1;
+#ifdef METAL_MFXTEMPORAL_ENABLED
if (@available(macOS 14.0, iOS 17.0, tvOS 17.0, *)) {
limits.temporalScalerInputContentMinScale = (double)[MTLFXTemporalScalerDescriptor supportedInputContentMinScaleForDevice:p_device];
limits.temporalScalerInputContentMaxScale = (double)[MTLFXTemporalScalerDescriptor supportedInputContentMaxScaleForDevice:p_device];
@@ -332,6 +339,11 @@
limits.temporalScalerInputContentMinScale = 1.0;
limits.temporalScalerInputContentMaxScale = 3.0;
}
+#else
+ // Defaults taken from macOS 14+
+ limits.temporalScalerInputContentMinScale = 1.0;
+ limits.temporalScalerInputContentMaxScale = 3.0;
+#endif
}
MetalDeviceProperties::MetalDeviceProperties(id p_device) {
diff --git a/drivers/metal/pixel_formats.mm b/drivers/metal/pixel_formats.mm
index 86924c6530ac..9ce9cdda733a 100644
--- a/drivers/metal/pixel_formats.mm
+++ b/drivers/metal/pixel_formats.mm
@@ -114,6 +114,9 @@ void clear(T *p_val, size_t p_count = 1) {
}
bool PixelFormats::isPVRTCFormat(MTLPixelFormat p_format) {
+#if defined(VISIONOS_ENABLED)
+ return false;
+#else
switch (p_format) {
case MTLPixelFormatPVRTC_RGBA_2BPP:
case MTLPixelFormatPVRTC_RGBA_2BPP_sRGB:
@@ -127,6 +130,7 @@ void clear(T *p_val, size_t p_count = 1) {
default:
return false;
}
+#endif
}
MTLFormatType PixelFormats::getFormatType(DataFormat p_format) {
@@ -668,11 +672,13 @@ void clear(T *p_val, size_t p_count = 1) {
addMTLPixelFormatDesc(RGBA32Sint, Color128, RWC);
addMTLPixelFormatDesc(RGBA32Float, Color128, All);
+#if !defined(VISIONOS_ENABLED)
// Compressed pixel formats
addMTLPixelFormatDesc(PVRTC_RGBA_2BPP, PVRTC_RGBA_2BPP, RF);
addMTLPixelFormatDescSRGB(PVRTC_RGBA_2BPP_sRGB, PVRTC_RGBA_2BPP, RF, PVRTC_RGBA_2BPP);
addMTLPixelFormatDesc(PVRTC_RGBA_4BPP, PVRTC_RGBA_4BPP, RF);
addMTLPixelFormatDescSRGB(PVRTC_RGBA_4BPP_sRGB, PVRTC_RGBA_4BPP, RF, PVRTC_RGBA_4BPP);
+#endif
addMTLPixelFormatDesc(ETC2_RGB8, ETC2_RGB8, RF);
addMTLPixelFormatDescSRGB(ETC2_RGB8_sRGB, ETC2_RGB8, RF, ETC2_RGB8);
diff --git a/drivers/metal/rendering_device_driver_metal.mm b/drivers/metal/rendering_device_driver_metal.mm
index 53af8e49c5ee..3d61c63c2db8 100644
--- a/drivers/metal/rendering_device_driver_metal.mm
+++ b/drivers/metal/rendering_device_driver_metal.mm
@@ -290,7 +290,11 @@ _FORCE_INLINE_ MTLSize mipmapLevelSizeFromSize(MTLSize p_size, NSUInteger p_leve
// Usage.
MTLResourceOptions options = 0;
+#if defined(VISIONOS_ENABLED)
+ const bool supports_memoryless = true;
+#else
const bool supports_memoryless = (*device_properties).features.highestFamily >= MTLGPUFamilyApple2 && (*device_properties).features.highestFamily < MTLGPUFamilyMac1;
+#endif
if (supports_memoryless && p_format.usage_bits & TEXTURE_USAGE_TRANSIENT_BIT) {
options = MTLResourceStorageModeMemoryless | MTLResourceHazardTrackingModeTracked;
desc.storageMode = MTLStorageModeMemoryless;
@@ -2493,7 +2497,11 @@ void deserialize(BufReader &p_reader) {
cd->name = binary_data.shader_name;
cd->stage = shader_data.stage;
options.preserveInvariance = shader_data.is_position_invariant;
+#if defined(VISIONOS_ENABLED)
+ options.mathMode = MTLMathModeFast;
+#else
options.fastMathEnabled = YES;
+#endif
MDLibrary *library = [MDLibrary newLibraryWithCacheEntry:cd
device:device
source:source
@@ -4184,8 +4192,8 @@ bool isArrayTexture(MTLTextureType p_type) {
error_string += "- No support for image cube arrays.\n";
}
-#if defined(IOS_ENABLED)
- // iOS platform ports currently don't exit themselves when this method returns `ERR_CANT_CREATE`.
+#if defined(APPLE_EMBEDDED_ENABLED)
+ // Apple Embedded platforms exports currently don't exit themselves when this method returns `ERR_CANT_CREATE`.
OS::get_singleton()->alert(error_string + "\nClick OK to exit (black screen will be visible).");
#else
OS::get_singleton()->alert(error_string + "\nClick OK to exit.");
diff --git a/editor/export/editor_export_platform_apple_embedded.cpp b/editor/export/editor_export_platform_apple_embedded.cpp
new file mode 100644
index 000000000000..02e8b86609bd
--- /dev/null
+++ b/editor/export/editor_export_platform_apple_embedded.cpp
@@ -0,0 +1,2757 @@
+/**************************************************************************/
+/* editor_export_platform_apple_embedded.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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. */
+/**************************************************************************/
+
+#include "editor_export_platform_apple_embedded.h"
+
+#include "core/io/json.h"
+#include "core/io/plist.h"
+#include "core/string/translation.h"
+#include "editor/editor_node.h"
+#include "editor/editor_paths.h"
+#include "editor/editor_string_names.h"
+#include "editor/export/editor_export.h"
+#include "editor/export/lipo.h"
+#include "editor/export/macho.h"
+#include "editor/import/resource_importer_texture_settings.h"
+#include "editor/plugins/script_editor_plugin.h"
+#include "editor/themes/editor_scale.h"
+
+#include "modules/modules_enabled.gen.h" // For mono.
+#include "modules/svg/image_loader_svg.h"
+
+void EditorExportPlatformAppleEmbedded::get_preset_features(const Ref &p_preset, List *r_features) const {
+ // Vulkan and OpenGL ES 3.0 both mandate ETC2 support.
+ r_features->push_back("etc2");
+ r_features->push_back("astc");
+
+ Vector architectures = _get_preset_architectures(p_preset);
+ for (int i = 0; i < architectures.size(); ++i) {
+ r_features->push_back(architectures[i]);
+ }
+}
+
+Vector EditorExportPlatformAppleEmbedded::_get_supported_architectures() const {
+ Vector archs;
+ archs.push_back(ExportArchitecture("arm64", true));
+ return archs;
+}
+
+struct APIAccessInfo {
+ String prop_name;
+ String type_name;
+ Vector prop_flag_value;
+ Vector prop_flag_name;
+ int default_value;
+};
+
+static const APIAccessInfo api_info[] = {
+ { "file_timestamp",
+ "NSPrivacyAccessedAPICategoryFileTimestamp",
+ { "DDA9.1", "C617.1", "3B52.1" },
+ { "Display to user on-device:", "Inside app or group container", "Files provided to app by user" },
+ 3 },
+ { "system_boot_time",
+ "NSPrivacyAccessedAPICategorySystemBootTime",
+ { "35F9.1", "8FFB.1", "3D61.1" },
+ { "Measure time on-device", "Calculate absolute event timestamps", "User-initiated bug report" },
+ 1 },
+ { "disk_space",
+ "NSPrivacyAccessedAPICategoryDiskSpace",
+ { "E174.1", "85F4.1", "7D9E.1", "B728.1" },
+ { "Write or delete file on-device", "Display to user on-device", "User-initiated bug report", "Health research app" },
+ 3 },
+ { "active_keyboard",
+ "NSPrivacyAccessedAPICategoryActiveKeyboards",
+ { "3EC4.1", "54BD.1" },
+ { "Custom keyboard app on-device", "Customize UI on-device:2" },
+ 0 },
+ { "user_defaults",
+ "NSPrivacyAccessedAPICategoryUserDefaults",
+ { "1C8F.1", "AC6B.1", "CA92.1" },
+ { "Access info from same App Group", "Access managed app configuration", "Access info from same app" },
+ 0 },
+};
+
+struct DataCollectionInfo {
+ String prop_name;
+ String type_name;
+};
+
+static const DataCollectionInfo data_collect_type_info[] = {
+ { "name", "NSPrivacyCollectedDataTypeName" },
+ { "email_address", "NSPrivacyCollectedDataTypeEmailAddress" },
+ { "phone_number", "NSPrivacyCollectedDataTypePhoneNumber" },
+ { "physical_address", "NSPrivacyCollectedDataTypePhysicalAddress" },
+ { "other_contact_info", "NSPrivacyCollectedDataTypeOtherUserContactInfo" },
+ { "health", "NSPrivacyCollectedDataTypeHealth" },
+ { "fitness", "NSPrivacyCollectedDataTypeFitness" },
+ { "payment_info", "NSPrivacyCollectedDataTypePaymentInfo" },
+ { "credit_info", "NSPrivacyCollectedDataTypeCreditInfo" },
+ { "other_financial_info", "NSPrivacyCollectedDataTypeOtherFinancialInfo" },
+ { "precise_location", "NSPrivacyCollectedDataTypePreciseLocation" },
+ { "coarse_location", "NSPrivacyCollectedDataTypeCoarseLocation" },
+ { "sensitive_info", "NSPrivacyCollectedDataTypeSensitiveInfo" },
+ { "contacts", "NSPrivacyCollectedDataTypeContacts" },
+ { "emails_or_text_messages", "NSPrivacyCollectedDataTypeEmailsOrTextMessages" },
+ { "photos_or_videos", "NSPrivacyCollectedDataTypePhotosorVideos" },
+ { "audio_data", "NSPrivacyCollectedDataTypeAudioData" },
+ { "gameplay_content", "NSPrivacyCollectedDataTypeGameplayContent" },
+ { "customer_support", "NSPrivacyCollectedDataTypeCustomerSupport" },
+ { "other_user_content", "NSPrivacyCollectedDataTypeOtherUserContent" },
+ { "browsing_history", "NSPrivacyCollectedDataTypeBrowsingHistory" },
+ { "search_hhistory", "NSPrivacyCollectedDataTypeSearchHistory" },
+ { "user_id", "NSPrivacyCollectedDataTypeUserID" },
+ { "device_id", "NSPrivacyCollectedDataTypeDeviceID" },
+ { "purchase_history", "NSPrivacyCollectedDataTypePurchaseHistory" },
+ { "product_interaction", "NSPrivacyCollectedDataTypeProductInteraction" },
+ { "advertising_data", "NSPrivacyCollectedDataTypeAdvertisingData" },
+ { "other_usage_data", "NSPrivacyCollectedDataTypeOtherUsageData" },
+ { "crash_data", "NSPrivacyCollectedDataTypeCrashData" },
+ { "performance_data", "NSPrivacyCollectedDataTypePerformanceData" },
+ { "other_diagnostic_data", "NSPrivacyCollectedDataTypeOtherDiagnosticData" },
+ { "environment_scanning", "NSPrivacyCollectedDataTypeEnvironmentScanning" },
+ { "hands", "NSPrivacyCollectedDataTypeHands" },
+ { "head", "NSPrivacyCollectedDataTypeHead" },
+ { "other_data_types", "NSPrivacyCollectedDataTypeOtherDataTypes" },
+};
+
+static const DataCollectionInfo data_collect_purpose_info[] = {
+ { "Analytics", "NSPrivacyCollectedDataTypePurposeAnalytics" },
+ { "App Functionality", "NSPrivacyCollectedDataTypePurposeAppFunctionality" },
+ { "Developer Advertising", "NSPrivacyCollectedDataTypePurposeDeveloperAdvertising" },
+ { "Third-party Advertising", "NSPrivacyCollectedDataTypePurposeThirdPartyAdvertising" },
+ { "Product Personalization", "NSPrivacyCollectedDataTypePurposeProductPersonalization" },
+ { "Other", "NSPrivacyCollectedDataTypePurposeOther" },
+};
+
+static const String export_method_string[] = {
+ "app-store",
+ "development",
+ "ad-hoc",
+ "enterprise",
+};
+
+String EditorExportPlatformAppleEmbedded::get_export_option_warning(const EditorExportPreset *p_preset, const StringName &p_name) const {
+ if (p_preset) {
+ if (p_name == "application/app_store_team_id") {
+ String team_id = p_preset->get("application/app_store_team_id");
+ if (team_id.is_empty()) {
+ return TTR("App Store Team ID not specified.") + "\n";
+ }
+ } else if (p_name == "application/bundle_identifier") {
+ String identifier = p_preset->get("application/bundle_identifier");
+ String pn_err;
+ if (!is_package_name_valid(identifier, &pn_err)) {
+ return TTR("Invalid Identifier:") + " " + pn_err;
+ }
+ } else if (p_name == "privacy/file_timestamp_access_reasons") {
+ int access = p_preset->get("privacy/file_timestamp_access_reasons");
+ if (access == 0) {
+ return TTR("At least one file timestamp access reason should be selected.");
+ }
+ } else if (p_name == "privacy/disk_space_access_reasons") {
+ int access = p_preset->get("privacy/disk_space_access_reasons");
+ if (access == 0) {
+ return TTR("At least one disk space access reason should be selected.");
+ }
+ } else if (p_name == "privacy/system_boot_time_access_reasons") {
+ int access = p_preset->get("privacy/system_boot_time_access_reasons");
+ if (access == 0) {
+ return TTR("At least one system boot time access reason should be selected.");
+ }
+ }
+ }
+ return String();
+}
+
+void EditorExportPlatformAppleEmbedded::_notification(int p_what) {
+#ifdef MACOS_ENABLED
+ if (p_what == NOTIFICATION_POSTINITIALIZE) {
+ if (EditorExport::get_singleton()) {
+ EditorExport::get_singleton()->connect_presets_runnable_updated(callable_mp(this, &EditorExportPlatformAppleEmbedded::_update_preset_status));
+ }
+ }
+#endif
+}
+
+bool EditorExportPlatformAppleEmbedded::get_export_option_visibility(const EditorExportPreset *p_preset, const String &p_option) const {
+ // Hide unsupported .NET embedding option.
+ if (p_option == "dotnet/embed_build_outputs") {
+ return false;
+ }
+
+ if (p_preset == nullptr) {
+ return true;
+ }
+
+ bool advanced_options_enabled = p_preset->are_advanced_options_enabled();
+ if (p_option.begins_with("privacy") ||
+ (p_option.begins_with("icons/") && !p_option.begins_with("icons/icon") && !p_option.begins_with("icons/app_store")) ||
+ p_option == "custom_template/debug" ||
+ p_option == "custom_template/release" ||
+ p_option == "application/additional_plist_content" ||
+ p_option == "application/delete_old_export_files_unconditionally" ||
+ p_option == "application/icon_interpolation" ||
+ p_option == "application/signature") {
+ return advanced_options_enabled;
+ }
+
+ return true;
+}
+
+void EditorExportPlatformAppleEmbedded::get_export_options(List *r_options) const {
+ r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "custom_template/debug", PROPERTY_HINT_GLOBAL_FILE, "*.zip"), ""));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "custom_template/release", PROPERTY_HINT_GLOBAL_FILE, "*.zip"), ""));
+
+ Vector architectures = _get_supported_architectures();
+ for (int i = 0; i < architectures.size(); ++i) {
+ r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, vformat("%s/%s", PNAME("architectures"), architectures[i].name)), architectures[i].is_default));
+ }
+
+ r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/app_store_team_id"), "", false, true));
+
+ r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "application/export_method_debug", PROPERTY_HINT_ENUM, "App Store,Development,Ad-Hoc,Enterprise"), 1));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/code_sign_identity_debug", PROPERTY_HINT_PLACEHOLDER_TEXT, "Apple Development"), ""));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/code_sign_identity_release", PROPERTY_HINT_PLACEHOLDER_TEXT, "Apple Distribution"), ""));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/provisioning_profile_uuid_debug", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SECRET), ""));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/provisioning_profile_uuid_release", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SECRET), ""));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/provisioning_profile_specifier_debug", PROPERTY_HINT_PLACEHOLDER_TEXT, ""), ""));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/provisioning_profile_specifier_release", PROPERTY_HINT_PLACEHOLDER_TEXT, ""), ""));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "application/export_method_release", PROPERTY_HINT_ENUM, "App Store,Development,Ad-Hoc,Enterprise"), 0));
+
+ r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/bundle_identifier", PROPERTY_HINT_PLACEHOLDER_TEXT, "com.example.game"), "", false, true));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/signature"), ""));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/short_version", PROPERTY_HINT_PLACEHOLDER_TEXT, "Leave empty to use project version"), ""));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/version", PROPERTY_HINT_PLACEHOLDER_TEXT, "Leave empty to use project version"), ""));
+
+ r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/additional_plist_content", PROPERTY_HINT_MULTILINE_TEXT), ""));
+
+ r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "application/icon_interpolation", PROPERTY_HINT_ENUM, "Nearest neighbor,Bilinear,Cubic,Trilinear,Lanczos"), 4));
+
+ r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "application/export_project_only"), false));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "application/delete_old_export_files_unconditionally"), false));
+
+ Vector found_plugins = get_plugins(get_platform_name());
+ for (int i = 0; i < found_plugins.size(); i++) {
+ r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, vformat("%s/%s", PNAME("plugins"), found_plugins[i].name)), false));
+ }
+
+ HashSet plist_keys;
+
+ for (int i = 0; i < found_plugins.size(); i++) {
+ // Editable plugin plist values
+ PluginConfigAppleEmbedded plugin = found_plugins[i];
+
+ for (const KeyValue &E : plugin.plist) {
+ switch (E.value.type) {
+ case PluginConfigAppleEmbedded::PlistItemType::STRING_INPUT: {
+ String preset_name = "plugins_plist/" + E.key;
+ if (!plist_keys.has(preset_name)) {
+ r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, preset_name), E.value.value));
+ plist_keys.insert(preset_name);
+ }
+ } break;
+ default:
+ continue;
+ }
+ }
+ }
+
+ plugins_changed.clear();
+ plugins = found_plugins;
+
+ r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "entitlements/increased_memory_limit"), false));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "entitlements/game_center"), false));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "entitlements/push_notifications", PROPERTY_HINT_ENUM, "Disabled,Production,Development"), "Disabled"));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "entitlements/additional", PROPERTY_HINT_MULTILINE_TEXT), ""));
+
+ r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "capabilities/access_wifi"), false));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "capabilities/performance_gaming_tier"), false));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "capabilities/performance_a12"), false));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::PACKED_STRING_ARRAY, "capabilities/additional"), PackedStringArray()));
+
+ r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "user_data/accessible_from_files_app"), false));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "user_data/accessible_from_itunes_sharing"), false));
+
+ r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "privacy/camera_usage_description", PROPERTY_HINT_PLACEHOLDER_TEXT, "Provide a message if you need to use the camera"), ""));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::DICTIONARY, "privacy/camera_usage_description_localized", PROPERTY_HINT_LOCALIZABLE_STRING), Dictionary()));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "privacy/microphone_usage_description", PROPERTY_HINT_PLACEHOLDER_TEXT, "Provide a message if you need to use the microphone"), ""));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::DICTIONARY, "privacy/microphone_usage_description_localized", PROPERTY_HINT_LOCALIZABLE_STRING), Dictionary()));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "privacy/photolibrary_usage_description", PROPERTY_HINT_PLACEHOLDER_TEXT, "Provide a message if you need access to the photo library"), ""));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::DICTIONARY, "privacy/photolibrary_usage_description_localized", PROPERTY_HINT_LOCALIZABLE_STRING), Dictionary()));
+
+ for (uint64_t i = 0; i < std::size(api_info); ++i) {
+ String prop_name = vformat("privacy/%s_access_reasons", api_info[i].prop_name);
+ String hint;
+ for (int j = 0; j < api_info[i].prop_flag_value.size(); j++) {
+ if (j != 0) {
+ hint += ",";
+ }
+ hint += vformat("%s - %s:%d", api_info[i].prop_flag_value[j], api_info[i].prop_flag_name[j], (1 << j));
+ }
+ r_options->push_back(ExportOption(PropertyInfo(Variant::INT, prop_name, PROPERTY_HINT_FLAGS, hint), api_info[i].default_value));
+ }
+
+ r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "privacy/tracking_enabled"), false));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::PACKED_STRING_ARRAY, "privacy/tracking_domains"), Vector()));
+
+ {
+ String hint;
+ for (uint64_t i = 0; i < std::size(data_collect_purpose_info); ++i) {
+ if (i != 0) {
+ hint += ",";
+ }
+ hint += vformat("%s:%d", data_collect_purpose_info[i].prop_name, (1 << i));
+ }
+ for (uint64_t i = 0; i < std::size(data_collect_type_info); ++i) {
+ r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, vformat("privacy/collected_data/%s/collected", data_collect_type_info[i].prop_name)), false));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, vformat("privacy/collected_data/%s/linked_to_user", data_collect_type_info[i].prop_name)), false));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, vformat("privacy/collected_data/%s/used_for_tracking", data_collect_type_info[i].prop_name)), false));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::INT, vformat("privacy/collected_data/%s/collection_purposes", data_collect_type_info[i].prop_name), PROPERTY_HINT_FLAGS, hint), 0));
+ }
+ }
+
+ r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "icons/icon_1024x1024", PROPERTY_HINT_FILE, "*.svg,*.png,*.webp,*.jpg,*.jpeg"), ""));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "icons/icon_1024x1024_dark", PROPERTY_HINT_FILE, "*.svg,*.png,*.webp,*.jpg,*.jpeg"), ""));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "icons/icon_1024x1024_tinted", PROPERTY_HINT_FILE, "*.svg,*.png,*.webp,*.jpg,*.jpeg"), ""));
+
+ HashSet used_names;
+
+ Vector icon_infos = get_icon_infos();
+ for (int i = 0; i < icon_infos.size(); ++i) {
+ if (!used_names.has(icon_infos[i].preset_key)) {
+ used_names.insert(icon_infos[i].preset_key);
+ r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, String(icon_infos[i].preset_key), PROPERTY_HINT_FILE, "*.png,*.jpg,*.jpeg"), ""));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, String(icon_infos[i].preset_key) + "_dark", PROPERTY_HINT_FILE, "*.png,*.jpg,*.jpeg"), ""));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, String(icon_infos[i].preset_key) + "_tinted", PROPERTY_HINT_FILE, "*.png,*.jpg,*.jpeg"), ""));
+ }
+ }
+}
+
+void EditorExportPlatformAppleEmbedded::_fix_config_file(const Ref &p_preset, Vector &pfile, const AppleEmbeddedConfigData &p_config, bool p_debug) {
+ String dbg_sign_id = p_preset->get("application/code_sign_identity_debug").operator String().is_empty() ? "Apple Development" : p_preset->get("application/code_sign_identity_debug");
+ String rel_sign_id = p_preset->get("application/code_sign_identity_release").operator String().is_empty() ? "Apple Distribution" : p_preset->get("application/code_sign_identity_release");
+ bool dbg_manual = !p_preset->get_or_env("application/provisioning_profile_uuid_debug", ENV_APPLE_PLATFORM_PROFILE_UUID_DEBUG).operator String().is_empty() || (dbg_sign_id != "Apple Development" && dbg_sign_id != "Apple Distribution");
+ bool rel_manual = !p_preset->get_or_env("application/provisioning_profile_uuid_release", ENV_APPLE_PLATFORM_PROFILE_UUID_RELEASE).operator String().is_empty() || (rel_sign_id != "Apple Development" && rel_sign_id != "Apple Distribution");
+
+ String provisioning_profile_specifier_dbg = p_preset->get_or_env("application/provisioning_profile_specifier_debug", ENV_APPLE_PLATFORM_PROFILE_SPECIFIER_DEBUG).operator String();
+ bool valid_dbg_specifier = !provisioning_profile_specifier_dbg.is_empty();
+ dbg_manual |= valid_dbg_specifier;
+
+ String provisioning_profile_specifier_rel = p_preset->get_or_env("application/provisioning_profile_specifier_release", ENV_APPLE_PLATFORM_PROFILE_SPECIFIER_RELEASE).operator String();
+ bool valid_rel_specifier = !provisioning_profile_specifier_rel.is_empty();
+ rel_manual |= valid_rel_specifier;
+
+ String str = String::utf8((const char *)pfile.ptr(), pfile.size());
+ String strnew;
+ Vector lines = str.split("\n");
+ for (int i = 0; i < lines.size(); i++) {
+ if (lines[i].contains("$binary")) {
+ strnew += lines[i].replace("$binary", p_config.binary_name) + "\n";
+ } else if (lines[i].contains("$modules_buildfile")) {
+ strnew += lines[i].replace("$modules_buildfile", p_config.modules_buildfile) + "\n";
+ } else if (lines[i].contains("$modules_fileref")) {
+ strnew += lines[i].replace("$modules_fileref", p_config.modules_fileref) + "\n";
+ } else if (lines[i].contains("$modules_buildphase")) {
+ strnew += lines[i].replace("$modules_buildphase", p_config.modules_buildphase) + "\n";
+ } else if (lines[i].contains("$modules_buildgrp")) {
+ strnew += lines[i].replace("$modules_buildgrp", p_config.modules_buildgrp) + "\n";
+ } else if (lines[i].contains("$name")) {
+ strnew += lines[i].replace("$name", p_config.pkg_name) + "\n";
+ } else if (lines[i].contains("$bundle_identifier")) {
+ strnew += lines[i].replace("$bundle_identifier", p_preset->get("application/bundle_identifier")) + "\n";
+ } else if (lines[i].contains("$short_version")) {
+ strnew += lines[i].replace("$short_version", p_preset->get_version("application/short_version")) + "\n";
+ } else if (lines[i].contains("$version")) {
+ strnew += lines[i].replace("$version", p_preset->get_version("application/version")) + "\n";
+ } else if (lines[i].contains("$min_version")) {
+ strnew += lines[i].replace("$min_version",
+ p_preset->get("application/min_" + get_platform_name() + "_version")) +
+ "\n";
+ } else if (lines[i].contains("$signature")) {
+ strnew += lines[i].replace("$signature", p_preset->get("application/signature")) + "\n";
+ } else if (lines[i].contains("$team_id")) {
+ strnew += lines[i].replace("$team_id", p_preset->get("application/app_store_team_id")) + "\n";
+ } else if (lines[i].contains("$default_build_config")) {
+ strnew += lines[i].replace("$default_build_config", p_debug ? "Debug" : "Release") + "\n";
+ } else if (lines[i].contains("$export_method")) {
+ int export_method = p_preset->get(p_debug ? "application/export_method_debug" : "application/export_method_release");
+ strnew += lines[i].replace("$export_method", export_method_string[export_method]) + "\n";
+ } else if (lines[i].contains("$provisioning_profile_specifier_debug")) {
+ strnew += lines[i].replace("$provisioning_profile_specifier_debug", provisioning_profile_specifier_dbg) + "\n";
+ } else if (lines[i].contains("$provisioning_profile_specifier_release")) {
+ strnew += lines[i].replace("$provisioning_profile_specifier_release", provisioning_profile_specifier_rel) + "\n";
+ } else if (lines[i].contains("$provisioning_profile_specifier")) {
+ String specifier = p_debug ? provisioning_profile_specifier_dbg : provisioning_profile_specifier_rel;
+ strnew += lines[i].replace("$provisioning_profile_specifier", specifier) + "\n";
+ } else if (lines[i].contains("$provisioning_profile_uuid_release")) {
+ strnew += lines[i].replace("$provisioning_profile_uuid_release", p_preset->get_or_env("application/provisioning_profile_uuid_release", ENV_APPLE_PLATFORM_PROFILE_UUID_RELEASE)) + "\n";
+ } else if (lines[i].contains("$provisioning_profile_uuid_debug")) {
+ strnew += lines[i].replace("$provisioning_profile_uuid_debug", p_preset->get_or_env("application/provisioning_profile_uuid_debug", ENV_APPLE_PLATFORM_PROFILE_UUID_DEBUG)) + "\n";
+ } else if (lines[i].contains("$code_sign_style_debug")) {
+ if (dbg_manual) {
+ strnew += lines[i].replace("$code_sign_style_debug", "Manual") + "\n";
+ } else {
+ strnew += lines[i].replace("$code_sign_style_debug", "Automatic") + "\n";
+ }
+ } else if (lines[i].contains("$code_sign_style_release")) {
+ if (rel_manual) {
+ strnew += lines[i].replace("$code_sign_style_release", "Manual") + "\n";
+ } else {
+ strnew += lines[i].replace("$code_sign_style_release", "Automatic") + "\n";
+ }
+ } else if (lines[i].contains("$provisioning_profile_uuid")) {
+ String uuid = p_debug ? p_preset->get_or_env("application/provisioning_profile_uuid_debug", ENV_APPLE_PLATFORM_PROFILE_UUID_DEBUG) : p_preset->get_or_env("application/provisioning_profile_uuid_release", ENV_APPLE_PLATFORM_PROFILE_UUID_RELEASE);
+ if (uuid.is_empty()) {
+ Variant variant = p_debug ? provisioning_profile_specifier_dbg : provisioning_profile_specifier_rel;
+ bool valid = p_debug ? valid_dbg_specifier : valid_rel_specifier;
+ uuid = valid ? variant : "";
+ }
+ strnew += lines[i].replace("$provisioning_profile_uuid", uuid) + "\n";
+ } else if (lines[i].contains("$code_sign_identity_debug")) {
+ strnew += lines[i].replace("$code_sign_identity_debug", dbg_sign_id) + "\n";
+ } else if (lines[i].contains("$code_sign_identity_release")) {
+ strnew += lines[i].replace("$code_sign_identity_release", rel_sign_id) + "\n";
+ } else if (lines[i].contains("$additional_plist_content")) {
+ strnew += lines[i].replace("$additional_plist_content", p_config.plist_content) + "\n";
+ } else if (lines[i].contains("$godot_archs")) {
+ strnew += lines[i].replace("$godot_archs", p_config.architectures) + "\n";
+ } else if (lines[i].contains("$linker_flags")) {
+ strnew += lines[i].replace("$linker_flags", p_config.linker_flags) + "\n";
+ } else if (lines[i].contains("$targeted_device_family")) {
+ String xcode_value;
+ switch ((int)p_preset->get("application/targeted_device_family")) {
+ case 0: // iPhone
+ xcode_value = "1";
+ break;
+ case 1: // iPad
+ xcode_value = "2";
+ break;
+ case 2: // iPhone & iPad
+ xcode_value = "1,2";
+ break;
+ }
+ strnew += lines[i].replace("$targeted_device_family", xcode_value) + "\n";
+ } else if (lines[i].contains("$cpp_code")) {
+ strnew += lines[i].replace("$cpp_code", p_config.cpp_code) + "\n";
+ } else if (lines[i].contains("$docs_in_place")) {
+ strnew += lines[i].replace("$docs_in_place", ((bool)p_preset->get("user_data/accessible_from_files_app")) ? "" : "") + "\n";
+ } else if (lines[i].contains("$docs_sharing")) {
+ strnew += lines[i].replace("$docs_sharing", ((bool)p_preset->get("user_data/accessible_from_itunes_sharing")) ? "" : "") + "\n";
+ } else if (lines[i].contains("$entitlements_full")) {
+ String entitlements;
+ if ((String)p_preset->get("entitlements/push_notifications") != "Disabled") {
+ entitlements += "aps-environment\n" + p_preset->get("entitlements/push_notifications").operator String().to_lower() + "" + "\n";
+ }
+ if ((bool)p_preset->get("entitlements/game_center")) {
+ entitlements += "com.apple.developer.game-center\n\n";
+ }
+ if ((bool)p_preset->get("entitlements/increased_memory_limit")) {
+ entitlements += "com.apple.developer.kernel.increased-memory-limit\n\n";
+ }
+ entitlements += p_preset->get("entitlements/additional").operator String() + "\n";
+
+ strnew += lines[i].replace("$entitlements_full", entitlements);
+ } else if (lines[i].contains("$required_device_capabilities")) {
+ String capabilities;
+
+ // I've removed armv7 as we can run on 64bit only devices
+ // Note that capabilities listed here are requirements for the app to be installed.
+ // They don't enable anything.
+ Vector capabilities_list = p_config.capabilities;
+
+ if ((bool)p_preset->get("capabilities/access_wifi") && !capabilities_list.has("wifi")) {
+ capabilities_list.push_back("wifi");
+ }
+ if ((bool)p_preset->get("capabilities/performance_gaming_tier") && !capabilities_list.has("iphone-performance-gaming-tier")) {
+ capabilities_list.push_back("iphone-performance-gaming-tier");
+ }
+ if ((bool)p_preset->get("capabilities/performance_a12") && !capabilities_list.has("iphone-ipad-minimum-performance-a12")) {
+ capabilities_list.push_back("iphone-ipad-minimum-performance-a12");
+ }
+ for (int idx = 0; idx < capabilities_list.size(); idx++) {
+ capabilities += "" + capabilities_list[idx] + "\n";
+ }
+ for (const String &cap : p_preset->get("capabilities/additional").operator PackedStringArray()) {
+ capabilities += "" + cap + "\n";
+ }
+
+ strnew += lines[i].replace("$required_device_capabilities", capabilities);
+ } else if (lines[i].contains("$interface_orientations")) {
+ String orientations;
+ const DisplayServer::ScreenOrientation screen_orientation =
+ DisplayServer::ScreenOrientation(int(get_project_setting(p_preset, "display/window/handheld/orientation")));
+
+ switch (screen_orientation) {
+ case DisplayServer::SCREEN_LANDSCAPE:
+ orientations += "UIInterfaceOrientationLandscapeLeft\n";
+ break;
+ case DisplayServer::SCREEN_PORTRAIT:
+ orientations += "UIInterfaceOrientationPortrait\n";
+ break;
+ case DisplayServer::SCREEN_REVERSE_LANDSCAPE:
+ orientations += "UIInterfaceOrientationLandscapeRight\n";
+ break;
+ case DisplayServer::SCREEN_REVERSE_PORTRAIT:
+ orientations += "UIInterfaceOrientationPortraitUpsideDown\n";
+ break;
+ case DisplayServer::SCREEN_SENSOR_LANDSCAPE:
+ // Allow both landscape orientations depending on sensor direction.
+ orientations += "UIInterfaceOrientationLandscapeLeft\n";
+ orientations += "UIInterfaceOrientationLandscapeRight\n";
+ break;
+ case DisplayServer::SCREEN_SENSOR_PORTRAIT:
+ // Allow both portrait orientations depending on sensor direction.
+ orientations += "UIInterfaceOrientationPortrait\n";
+ orientations += "UIInterfaceOrientationPortraitUpsideDown\n";
+ break;
+ case DisplayServer::SCREEN_SENSOR:
+ // Allow all screen orientations depending on sensor direction.
+ orientations += "UIInterfaceOrientationLandscapeLeft\n";
+ orientations += "UIInterfaceOrientationLandscapeRight\n";
+ orientations += "UIInterfaceOrientationPortrait\n";
+ orientations += "UIInterfaceOrientationPortraitUpsideDown\n";
+ break;
+ }
+
+ strnew += lines[i].replace("$interface_orientations", orientations);
+ } else if (lines[i].contains("$ipad_interface_orientations")) {
+ String orientations;
+ const DisplayServer::ScreenOrientation screen_orientation =
+ DisplayServer::ScreenOrientation(int(get_project_setting(p_preset, "display/window/handheld/orientation")));
+
+ switch (screen_orientation) {
+ case DisplayServer::SCREEN_LANDSCAPE:
+ orientations += "UIInterfaceOrientationLandscapeRight\n";
+ break;
+ case DisplayServer::SCREEN_PORTRAIT:
+ orientations += "UIInterfaceOrientationPortrait\n";
+ break;
+ case DisplayServer::SCREEN_REVERSE_LANDSCAPE:
+ orientations += "UIInterfaceOrientationLandscapeLeft\n";
+ break;
+ case DisplayServer::SCREEN_REVERSE_PORTRAIT:
+ orientations += "UIInterfaceOrientationPortraitUpsideDown\n";
+ break;
+ case DisplayServer::SCREEN_SENSOR_LANDSCAPE:
+ // Allow both landscape orientations depending on sensor direction.
+ orientations += "UIInterfaceOrientationLandscapeLeft\n";
+ orientations += "UIInterfaceOrientationLandscapeRight\n";
+ break;
+ case DisplayServer::SCREEN_SENSOR_PORTRAIT:
+ // Allow both portrait orientations depending on sensor direction.
+ orientations += "UIInterfaceOrientationPortrait\n";
+ orientations += "UIInterfaceOrientationPortraitUpsideDown\n";
+ break;
+ case DisplayServer::SCREEN_SENSOR:
+ // Allow all screen orientations depending on sensor direction.
+ orientations += "UIInterfaceOrientationLandscapeLeft\n";
+ orientations += "UIInterfaceOrientationLandscapeRight\n";
+ orientations += "UIInterfaceOrientationPortrait\n";
+ orientations += "UIInterfaceOrientationPortraitUpsideDown\n";
+ break;
+ }
+
+ strnew += lines[i].replace("$ipad_interface_orientations", orientations);
+ } else if (lines[i].contains("$camera_usage_description")) {
+ String description = p_preset->get("privacy/camera_usage_description");
+ strnew += lines[i].replace("$camera_usage_description", description) + "\n";
+ } else if (lines[i].contains("$microphone_usage_description")) {
+ String description = p_preset->get("privacy/microphone_usage_description");
+ strnew += lines[i].replace("$microphone_usage_description", description) + "\n";
+ } else if (lines[i].contains("$photolibrary_usage_description")) {
+ String description = p_preset->get("privacy/photolibrary_usage_description");
+ strnew += lines[i].replace("$photolibrary_usage_description", description) + "\n";
+ } else if (lines[i].contains("$plist_launch_screen_name")) {
+ String value = "UILaunchStoryboardName\nLaunch Screen";
+ strnew += lines[i].replace("$plist_launch_screen_name", value) + "\n";
+ } else if (lines[i].contains("$pbx_launch_screen_file_reference")) {
+ String value = "90DD2D9D24B36E8000717FE1 = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = \"Launch Screen.storyboard\"; sourceTree = \"\"; };";
+ strnew += lines[i].replace("$pbx_launch_screen_file_reference", value) + "\n";
+ } else if (lines[i].contains("$pbx_launch_screen_copy_files")) {
+ String value = "90DD2D9D24B36E8000717FE1 /* Launch Screen.storyboard */,";
+ strnew += lines[i].replace("$pbx_launch_screen_copy_files", value) + "\n";
+ } else if (lines[i].contains("$pbx_launch_screen_build_phase")) {
+ String value = "90DD2D9E24B36E8000717FE1 /* Launch Screen.storyboard in Resources */,";
+ strnew += lines[i].replace("$pbx_launch_screen_build_phase", value) + "\n";
+ } else if (lines[i].contains("$pbx_launch_screen_build_reference")) {
+ String value = "90DD2D9E24B36E8000717FE1 /* Launch Screen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 90DD2D9D24B36E8000717FE1 /* Launch Screen.storyboard */; };";
+ strnew += lines[i].replace("$pbx_launch_screen_build_reference", value) + "\n";
+#ifndef DISABLE_DEPRECATED
+ } else if (lines[i].contains("$pbx_launch_image_usage_setting")) {
+ strnew += lines[i].replace("$pbx_launch_image_usage_setting", "") + "\n";
+#endif
+ } else if (lines[i].contains("$launch_screen_image_mode")) {
+ int image_scale_mode = p_preset->get("storyboard/image_scale_mode");
+ String value;
+
+ switch (image_scale_mode) {
+ case 0: {
+ String logo_path = get_project_setting(p_preset, "application/boot_splash/image");
+ bool is_on = get_project_setting(p_preset, "application/boot_splash/fullsize");
+ // If custom logo is not specified, Godot does not scale default one, so we should do the same.
+ value = (is_on && logo_path.length() > 0) ? "scaleAspectFit" : "center";
+ } break;
+ default: {
+ value = storyboard_image_scale_mode[image_scale_mode - 1];
+ }
+ }
+
+ strnew += lines[i].replace("$launch_screen_image_mode", value) + "\n";
+ } else if (lines[i].contains("$launch_screen_background_color")) {
+ bool use_custom = p_preset->get("storyboard/use_custom_bg_color");
+ Color color = use_custom ? p_preset->get("storyboard/custom_bg_color") : get_project_setting(p_preset, "application/boot_splash/bg_color");
+ const String value_format = "red=\"$red\" green=\"$green\" blue=\"$blue\" alpha=\"$alpha\"";
+
+ Dictionary value_dictionary;
+ value_dictionary["red"] = color.r;
+ value_dictionary["green"] = color.g;
+ value_dictionary["blue"] = color.b;
+ value_dictionary["alpha"] = color.a;
+ String value = value_format.format(value_dictionary, "$_");
+
+ strnew += lines[i].replace("$launch_screen_background_color", value) + "\n";
+ } else if (lines[i].contains("$pbx_locale_file_reference")) {
+ String locale_files;
+ Vector translations = get_project_setting(p_preset, "internationalization/locale/translations");
+ if (translations.size() > 0) {
+ HashSet languages;
+ for (const String &E : translations) {
+ Ref tr = ResourceLoader::load(E);
+ if (tr.is_valid() && tr->get_locale() != "en") {
+ languages.insert(tr->get_locale());
+ }
+ }
+
+ int index = 0;
+ for (const String &lang : languages) {
+ locale_files += "D0BCFE4518AEBDA2004A" + itos(index).pad_zeros(4) + " /* " + lang + " */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = " + lang + "; path = " + lang + ".lproj/InfoPlist.strings; sourceTree = \"\"; };\n";
+ index++;
+ }
+ }
+ strnew += lines[i].replace("$pbx_locale_file_reference", locale_files);
+ } else if (lines[i].contains("$pbx_locale_build_reference")) {
+ String locale_files;
+ Vector translations = get_project_setting(p_preset, "internationalization/locale/translations");
+ if (translations.size() > 0) {
+ HashSet languages;
+ for (const String &E : translations) {
+ Ref tr = ResourceLoader::load(E);
+ if (tr.is_valid() && tr->get_locale() != "en") {
+ languages.insert(tr->get_locale());
+ }
+ }
+
+ int index = 0;
+ for (const String &lang : languages) {
+ locale_files += "D0BCFE4518AEBDA2004A" + itos(index).pad_zeros(4) + " /* " + lang + " */,\n";
+ index++;
+ }
+ }
+ strnew += lines[i].replace("$pbx_locale_build_reference", locale_files);
+ } else if (lines[i].contains("$swift_runtime_migration")) {
+ String value = !p_config.use_swift_runtime ? "" : "LastSwiftMigration = 1250;";
+ strnew += lines[i].replace("$swift_runtime_migration", value) + "\n";
+ } else if (lines[i].contains("$swift_runtime_build_settings")) {
+ String value = !p_config.use_swift_runtime ? "" : R"(
+ CLANG_ENABLE_MODULES = YES;
+ SWIFT_OBJC_BRIDGING_HEADER = "$binary/dummy.h";
+ SWIFT_VERSION = 5.0;
+ )";
+ value = value.replace("$binary", p_config.binary_name);
+ strnew += lines[i].replace("$swift_runtime_build_settings", value) + "\n";
+ } else if (lines[i].contains("$swift_runtime_fileref")) {
+ String value = !p_config.use_swift_runtime ? "" : R"(
+ 90B4C2AA2680BC560039117A /* dummy.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "dummy.h"; sourceTree = ""; };
+ 90B4C2B52680C7E90039117A /* dummy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "dummy.swift"; sourceTree = ""; };
+ )";
+ strnew += lines[i].replace("$swift_runtime_fileref", value) + "\n";
+ } else if (lines[i].contains("$swift_runtime_binary_files")) {
+ String value = !p_config.use_swift_runtime ? "" : R"(
+ 90B4C2AA2680BC560039117A /* dummy.h */,
+ 90B4C2B52680C7E90039117A /* dummy.swift */,
+ )";
+ strnew += lines[i].replace("$swift_runtime_binary_files", value) + "\n";
+ } else if (lines[i].contains("$swift_runtime_buildfile")) {
+ String value = !p_config.use_swift_runtime ? "" : "90B4C2B62680C7E90039117A /* dummy.swift in Sources */ = {isa = PBXBuildFile; fileRef = 90B4C2B52680C7E90039117A /* dummy.swift */; };";
+ strnew += lines[i].replace("$swift_runtime_buildfile", value) + "\n";
+ } else if (lines[i].contains("$swift_runtime_build_phase")) {
+ String value = !p_config.use_swift_runtime ? "" : "90B4C2B62680C7E90039117A /* dummy.swift */,";
+ strnew += lines[i].replace("$swift_runtime_build_phase", value) + "\n";
+ } else if (lines[i].contains("$priv_collection")) {
+ bool section_opened = false;
+ for (uint64_t j = 0; j < std::size(data_collect_type_info); ++j) {
+ bool data_collected = p_preset->get(vformat("privacy/collected_data/%s/collected", data_collect_type_info[j].prop_name));
+ bool linked = p_preset->get(vformat("privacy/collected_data/%s/linked_to_user", data_collect_type_info[j].prop_name));
+ bool tracking = p_preset->get(vformat("privacy/collected_data/%s/used_for_tracking", data_collect_type_info[j].prop_name));
+ int purposes = p_preset->get(vformat("privacy/collected_data/%s/collection_purposes", data_collect_type_info[j].prop_name));
+ if (data_collected) {
+ if (!section_opened) {
+ section_opened = true;
+ strnew += "\tNSPrivacyCollectedDataTypes\n";
+ strnew += "\t\n";
+ }
+ strnew += "\t\t\n";
+ strnew += "\t\t\tNSPrivacyCollectedDataType\n";
+ strnew += vformat("\t\t\t%s\n", data_collect_type_info[j].type_name);
+ strnew += "\t\t\t\tNSPrivacyCollectedDataTypeLinked\n";
+ if (linked) {
+ strnew += "\t\t\t\t\n";
+ } else {
+ strnew += "\t\t\t\t\n";
+ }
+ strnew += "\t\t\t\tNSPrivacyCollectedDataTypeTracking\n";
+ if (tracking) {
+ strnew += "\t\t\t\t\n";
+ } else {
+ strnew += "\t\t\t\t\n";
+ }
+ if (purposes != 0) {
+ strnew += "\t\t\t\tNSPrivacyCollectedDataTypePurposes\n";
+ strnew += "\t\t\t\t\n";
+ for (uint64_t k = 0; k < std::size(data_collect_purpose_info); ++k) {
+ if (purposes & (1 << k)) {
+ strnew += vformat("\t\t\t\t\t%s\n", data_collect_purpose_info[k].type_name);
+ }
+ }
+ strnew += "\t\t\t\t\n";
+ }
+ strnew += "\t\t\t\n";
+ }
+ }
+ if (section_opened) {
+ strnew += "\t\n";
+ }
+ } else if (lines[i].contains("$priv_tracking")) {
+ bool tracking = p_preset->get("privacy/tracking_enabled");
+ strnew += "\tNSPrivacyTracking\n";
+ if (tracking) {
+ strnew += "\t\n";
+ } else {
+ strnew += "\t\n";
+ }
+ Vector tracking_domains = p_preset->get("privacy/tracking_domains");
+ if (!tracking_domains.is_empty()) {
+ strnew += "\tNSPrivacyTrackingDomains\n";
+ strnew += "\t\n";
+ for (const String &E : tracking_domains) {
+ strnew += "\t\t" + E + "\n";
+ }
+ strnew += "\t\n";
+ }
+ } else if (lines[i].contains("$priv_api_types")) {
+ strnew += "\t\n";
+ for (uint64_t j = 0; j < std::size(api_info); ++j) {
+ int api_access = p_preset->get(vformat("privacy/%s_access_reasons", api_info[j].prop_name));
+ if (api_access != 0) {
+ strnew += "\t\t\n";
+ strnew += "\t\t\tNSPrivacyAccessedAPITypeReasons\n";
+ strnew += "\t\t\t\n";
+ for (int k = 0; k < api_info[j].prop_flag_value.size(); k++) {
+ if (api_access & (1 << k)) {
+ strnew += vformat("\t\t\t\t%s\n", api_info[j].prop_flag_value[k]);
+ }
+ }
+ strnew += "\t\t\t\n";
+ strnew += "\t\t\tNSPrivacyAccessedAPIType\n";
+ strnew += vformat("\t\t\t%s\n", api_info[j].type_name);
+ strnew += "\t\t\n";
+ }
+ }
+ strnew += "\t\n";
+ } else {
+ strnew += lines[i] + "\n";
+ }
+ }
+
+ // !BAS! I'm assuming the 9 in the original code was a typo. I've added -1 or else it seems to also be adding our terminating zero...
+ // should apply the same fix in our macOS export.
+ CharString cs = strnew.utf8();
+ pfile.resize(cs.size() - 1);
+ for (int i = 0; i < cs.size() - 1; i++) {
+ pfile.write[i] = cs[i];
+ }
+}
+
+String EditorExportPlatformAppleEmbedded::_get_additional_plist_content() {
+ Vector][> export_plugins = EditorExport::get_singleton()->get_export_plugins();
+ String result;
+ for (int i = 0; i < export_plugins.size(); ++i) {
+ result += export_plugins[i]->get_apple_embedded_platform_plist_content();
+ }
+ return result;
+}
+
+String EditorExportPlatformAppleEmbedded::_get_linker_flags() {
+ Vector][> export_plugins = EditorExport::get_singleton()->get_export_plugins();
+ String result;
+ for (int i = 0; i < export_plugins.size(); ++i) {
+ String flags = export_plugins[i]->get_apple_embedded_platform_linker_flags();
+ if (flags.length() == 0) {
+ continue;
+ }
+ if (result.length() > 0) {
+ result += ' ';
+ }
+ result += flags;
+ }
+ // the flags will be enclosed in quotes, so need to escape them
+ return result.replace("\"", "\\\"");
+}
+
+String EditorExportPlatformAppleEmbedded::_get_cpp_code() {
+ Vector][> export_plugins = EditorExport::get_singleton()->get_export_plugins();
+ String result;
+ for (int i = 0; i < export_plugins.size(); ++i) {
+ result += export_plugins[i]->get_apple_embedded_platform_cpp_code();
+ }
+ return result;
+}
+
+void EditorExportPlatformAppleEmbedded::_blend_and_rotate(Ref &p_dst, Ref &p_src, bool p_rot) {
+ ERR_FAIL_COND(p_dst.is_null());
+ ERR_FAIL_COND(p_src.is_null());
+
+ int sw = p_rot ? p_src->get_height() : p_src->get_width();
+ int sh = p_rot ? p_src->get_width() : p_src->get_height();
+
+ int x_pos = (p_dst->get_width() - sw) / 2;
+ int y_pos = (p_dst->get_height() - sh) / 2;
+
+ int xs = (x_pos >= 0) ? 0 : -x_pos;
+ int ys = (y_pos >= 0) ? 0 : -y_pos;
+
+ if (sw + x_pos > p_dst->get_width()) {
+ sw = p_dst->get_width() - x_pos;
+ }
+ if (sh + y_pos > p_dst->get_height()) {
+ sh = p_dst->get_height() - y_pos;
+ }
+
+ for (int y = ys; y < sh; y++) {
+ for (int x = xs; x < sw; x++) {
+ Color sc = p_rot ? p_src->get_pixel(p_src->get_width() - y - 1, x) : p_src->get_pixel(x, y);
+ Color dc = p_dst->get_pixel(x_pos + x, y_pos + y);
+ dc.r = (double)(sc.a * sc.r + dc.a * (1.0 - sc.a) * dc.r);
+ dc.g = (double)(sc.a * sc.g + dc.a * (1.0 - sc.a) * dc.g);
+ dc.b = (double)(sc.a * sc.b + dc.a * (1.0 - sc.a) * dc.b);
+ dc.a = (double)(sc.a + dc.a * (1.0 - sc.a));
+ p_dst->set_pixel(x_pos + x, y_pos + y, dc);
+ }
+ }
+}
+
+Error EditorExportPlatformAppleEmbedded::_walk_dir_recursive(Ref &p_da, FileHandler p_handler, void *p_userdata) {
+ Vector dirs;
+ String current_dir = p_da->get_current_dir();
+ p_da->list_dir_begin();
+ String path = p_da->get_next();
+ while (!path.is_empty()) {
+ if (p_da->current_is_dir()) {
+ if (path != "." && path != "..") {
+ dirs.push_back(path);
+ }
+ } else {
+ Error err = p_handler(current_dir.path_join(path), p_userdata);
+ if (err) {
+ p_da->list_dir_end();
+ return err;
+ }
+ }
+ path = p_da->get_next();
+ }
+ p_da->list_dir_end();
+
+ for (int i = 0; i < dirs.size(); ++i) {
+ p_da->change_dir(dirs[i]);
+ Error err = _walk_dir_recursive(p_da, p_handler, p_userdata);
+ p_da->change_dir("..");
+ if (err) {
+ return err;
+ }
+ }
+
+ return OK;
+}
+
+struct CodesignData {
+ const Ref &preset;
+ bool debug = false;
+
+ CodesignData(const Ref &p_preset, bool p_debug) :
+ preset(p_preset),
+ debug(p_debug) {
+ }
+};
+
+Error EditorExportPlatformAppleEmbedded::_codesign(String p_file, void *p_userdata) {
+ if (p_file.ends_with(".dylib")) {
+ CodesignData *data = static_cast(p_userdata);
+ print_line(String("Signing ") + p_file);
+
+ String sign_id;
+ if (data->debug) {
+ sign_id = data->preset->get("application/code_sign_identity_debug").operator String().is_empty() ? "Apple Development" : data->preset->get("application/code_sign_identity_debug");
+ } else {
+ sign_id = data->preset->get("application/code_sign_identity_release").operator String().is_empty() ? "Apple Distribution" : data->preset->get("application/code_sign_identity_release");
+ }
+
+ List codesign_args;
+ codesign_args.push_back("-f");
+ codesign_args.push_back("-s");
+ codesign_args.push_back(sign_id);
+ codesign_args.push_back(p_file);
+ String str;
+ Error err = OS::get_singleton()->execute("codesign", codesign_args, &str, nullptr, true);
+ print_verbose("codesign (" + p_file + "):\n" + str);
+
+ return err;
+ }
+ return OK;
+}
+
+struct PbxId {
+private:
+ static char _hex_char(uint8_t p_four_bits) {
+ if (p_four_bits < 10) {
+ return ('0' + p_four_bits);
+ }
+ return 'A' + (p_four_bits - 10);
+ }
+
+ static String _hex_pad(uint32_t p_num) {
+ Vector ret;
+ ret.resize(sizeof(p_num) * 2);
+ for (uint64_t i = 0; i < sizeof(p_num) * 2; ++i) {
+ uint8_t four_bits = (p_num >> (sizeof(p_num) * 8 - (i + 1) * 4)) & 0xF;
+ ret.write[i] = _hex_char(four_bits);
+ }
+ return String::utf8(ret.ptr(), ret.size());
+ }
+
+public:
+ uint32_t high_bits;
+ uint32_t mid_bits;
+ uint32_t low_bits;
+
+ String str() const {
+ return _hex_pad(high_bits) + _hex_pad(mid_bits) + _hex_pad(low_bits);
+ }
+
+ PbxId &operator++() {
+ low_bits++;
+ if (!low_bits) {
+ mid_bits++;
+ if (!mid_bits) {
+ high_bits++;
+ }
+ }
+
+ return *this;
+ }
+};
+
+struct ExportLibsData {
+ Vector lib_paths;
+ String dest_dir;
+};
+
+void EditorExportPlatformAppleEmbedded::_check_xcframework_content(const String &p_path, int &r_total_libs, int &r_static_libs, int &r_dylibs, int &r_frameworks) const {
+ Ref plist;
+ plist.instantiate();
+ plist->load_file(p_path.path_join("Info.plist"));
+ Ref root_node = plist->get_root();
+ if (root_node.is_null()) {
+ return;
+ }
+ Dictionary root = root_node->get_value();
+ if (!root.has("AvailableLibraries")) {
+ return;
+ }
+ Ref libs_node = root["AvailableLibraries"];
+ if (libs_node.is_null()) {
+ return;
+ }
+ Array libs = libs_node->get_value();
+ r_total_libs = libs.size();
+ for (int j = 0; j < libs.size(); j++) {
+ Ref lib_node = libs[j];
+ if (lib_node.is_null()) {
+ return;
+ }
+ Dictionary lib = lib_node->get_value();
+ if (lib.has("BinaryPath")) {
+ Ref path_node = lib["BinaryPath"];
+ if (path_node.is_valid()) {
+ String path = path_node->get_value();
+ if (path.ends_with(".a")) {
+ r_static_libs++;
+ }
+ if (path.ends_with(".dylib")) {
+ r_dylibs++;
+ }
+ if (path.ends_with(".framework")) {
+ r_frameworks++;
+ }
+ }
+ }
+ }
+}
+
+Error EditorExportPlatformAppleEmbedded::_convert_to_framework(const String &p_source, const String &p_destination, const String &p_id) const {
+ print_line("Converting to .framework", p_source, " -> ", p_destination);
+
+ Ref da = DirAccess::create_for_path(p_source);
+ if (da.is_null()) {
+ return ERR_CANT_OPEN;
+ }
+
+ Ref filesystem_da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
+ if (filesystem_da.is_null()) {
+ return ERR_CANT_OPEN;
+ }
+
+ if (!filesystem_da->dir_exists(p_destination)) {
+ Error make_dir_err = filesystem_da->make_dir_recursive(p_destination);
+ if (make_dir_err) {
+ return make_dir_err;
+ }
+ }
+
+ String asset = p_source.ends_with("/") ? p_source.left(-1) : p_source;
+ if (asset.ends_with(".xcframework")) {
+ Ref plist;
+ plist.instantiate();
+ plist->load_file(p_source.path_join("Info.plist"));
+ Ref root_node = plist->get_root();
+ if (root_node.is_null()) {
+ return ERR_CANT_OPEN;
+ }
+ Dictionary root = root_node->get_value();
+ if (!root.has("AvailableLibraries")) {
+ return ERR_CANT_OPEN;
+ }
+ Ref libs_node = root["AvailableLibraries"];
+ if (libs_node.is_null()) {
+ return ERR_CANT_OPEN;
+ }
+ Array libs = libs_node->get_value();
+ for (int j = 0; j < libs.size(); j++) {
+ Ref lib_node = libs[j];
+ if (lib_node.is_null()) {
+ return ERR_CANT_OPEN;
+ }
+ Dictionary lib = lib_node->get_value();
+ if (lib.has("BinaryPath") && lib.has("LibraryPath") && lib.has("LibraryIdentifier")) {
+ Ref bpath_node = lib["BinaryPath"];
+ Ref lpath_node = lib["LibraryPath"];
+ Ref lid_node = lib["LibraryIdentifier"];
+ if (bpath_node.is_valid() && lpath_node.is_valid() && lid_node.is_valid()) {
+ String binary_path = bpath_node->get_value();
+ String library_identifier = lid_node->get_value();
+
+ String file_name = binary_path.get_basename().get_file();
+ String framework_name = file_name + ".framework";
+
+ bpath_node->data_string = framework_name.utf8();
+ lpath_node->data_string = framework_name.utf8();
+ if (!filesystem_da->dir_exists(p_destination.path_join(library_identifier))) {
+ filesystem_da->make_dir_recursive(p_destination.path_join(library_identifier));
+ }
+ _convert_to_framework(p_source.path_join(library_identifier).path_join(binary_path), p_destination.path_join(library_identifier).path_join(framework_name), p_id);
+ if (lib.has("DebugSymbolsPath")) {
+ Ref dpath_node = lib["DebugSymbolsPath"];
+ if (dpath_node.is_valid()) {
+ String dpath = dpath_node->get_value();
+ if (da->dir_exists(p_source.path_join(library_identifier).path_join(dpath))) {
+ da->copy_dir(p_source.path_join(library_identifier).path_join(dpath), p_destination.path_join(library_identifier).path_join("dSYMs"));
+ }
+ }
+ }
+ }
+ }
+ }
+ String info_plist = plist->save_text();
+
+ Ref f = FileAccess::open(p_destination.path_join("Info.plist"), FileAccess::WRITE);
+ if (f.is_valid()) {
+ f->store_string(info_plist);
+ }
+ } else {
+ String file_name = p_destination.get_basename().get_file();
+ String framework_name = file_name + ".framework";
+
+ da->copy(p_source, p_destination.path_join(file_name));
+
+ // Performing `install_name_tool -id @rpath/{name}.framework/{name} ./{name}` on dylib
+ {
+ List install_name_args;
+ install_name_args.push_back("-id");
+ install_name_args.push_back(String("@rpath").path_join(framework_name).path_join(file_name));
+ install_name_args.push_back(p_destination.path_join(file_name));
+
+ OS::get_singleton()->execute("install_name_tool", install_name_args);
+ }
+
+ // Creating Info.plist
+ {
+ String lib_clean_name = file_name;
+ for (int i = 0; i < lib_clean_name.length(); i++) {
+ if (!is_ascii_alphanumeric_char(lib_clean_name[i]) && lib_clean_name[i] != '.' && lib_clean_name[i] != '-') {
+ lib_clean_name[i] = '-';
+ }
+ }
+ String info_plist_format =
+ "\n"
+ "\n"
+ "\n"
+ " \n"
+ " CFBundleShortVersionString\n"
+ " 1.0\n"
+ " CFBundleIdentifier\n"
+ " $id.framework.$cl_name\n"
+ " CFBundleName\n"
+ " $name\n"
+ " CFBundleExecutable\n"
+ " $name\n"
+ " DTPlatformName\n"
+ " " +
+ get_sdk_name() +
+ "\n"
+ " CFBundleInfoDictionaryVersion\n"
+ " 6.0\n"
+ " CFBundleVersion\n"
+ " 1\n"
+ " CFBundlePackageType\n"
+ " FMWK\n"
+ " MinimumOSVersion\n"
+ " " +
+ get_minimum_deployment_target() +
+ "\n"
+ " \n"
+ "";
+
+ String info_plist = info_plist_format.replace("$id", p_id).replace("$name", file_name).replace("$cl_name", lib_clean_name);
+
+ Ref f = FileAccess::open(p_destination.path_join("Info.plist"), FileAccess::WRITE);
+ if (f.is_valid()) {
+ f->store_string(info_plist);
+ }
+ }
+ }
+
+ return OK;
+}
+
+void EditorExportPlatformAppleEmbedded::_add_assets_to_project(const String &p_out_dir, const Ref &p_preset, Vector &p_project_data, const Vector &p_additional_assets) {
+ // that is just a random number, we just need Godot IDs not to clash with
+ // existing IDs in the project.
+ PbxId current_id = { 0x58938401, 0, 0 };
+ String pbx_files;
+ String pbx_frameworks_build;
+ String pbx_frameworks_refs;
+ String pbx_resources_build;
+ String pbx_resources_refs;
+ String pbx_embeded_frameworks;
+
+ const String file_info_format = String("$build_id = {isa = PBXBuildFile; fileRef = $ref_id; };\n") +
+ "$ref_id = {isa = PBXFileReference; lastKnownFileType = $file_type; name = \"$name\"; path = \"$file_path\"; sourceTree = \"\"; };\n";
+
+ for (int i = 0; i < p_additional_assets.size(); ++i) {
+ String additional_asset_info_format = file_info_format;
+
+ String build_id = (++current_id).str();
+ String ref_id = (++current_id).str();
+ String framework_id = "";
+
+ const AppleEmbeddedExportAsset &asset = p_additional_assets[i];
+
+ String type;
+ if (asset.exported_path.ends_with(".framework")) {
+ if (asset.should_embed) {
+ additional_asset_info_format += "$framework_id = {isa = PBXBuildFile; fileRef = $ref_id; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; };\n";
+ framework_id = (++current_id).str();
+ pbx_embeded_frameworks += framework_id + ",\n";
+ }
+
+ type = "wrapper.framework";
+ } else if (asset.exported_path.ends_with(".xcframework")) {
+ int total_libs = 0;
+ int static_libs = 0;
+ int dylibs = 0;
+ int frameworks = 0;
+ _check_xcframework_content(p_out_dir.path_join(asset.exported_path), total_libs, static_libs, dylibs, frameworks);
+ if (asset.should_embed && static_libs != total_libs) {
+ additional_asset_info_format += "$framework_id = {isa = PBXBuildFile; fileRef = $ref_id; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; };\n";
+ framework_id = (++current_id).str();
+ pbx_embeded_frameworks += framework_id + ",\n";
+ }
+
+ type = "wrapper.xcframework";
+ } else if (asset.exported_path.ends_with(".dylib")) {
+ type = "compiled.mach-o.dylib";
+ } else if (asset.exported_path.ends_with(".a")) {
+ type = "archive.ar";
+ } else {
+ type = "file";
+ }
+
+ String &pbx_build = asset.is_framework ? pbx_frameworks_build : pbx_resources_build;
+ String &pbx_refs = asset.is_framework ? pbx_frameworks_refs : pbx_resources_refs;
+
+ if (pbx_build.length() > 0) {
+ pbx_build += ",\n";
+ pbx_refs += ",\n";
+ }
+ pbx_build += build_id;
+ pbx_refs += ref_id;
+
+ Dictionary format_dict;
+ format_dict["build_id"] = build_id;
+ format_dict["ref_id"] = ref_id;
+ format_dict["name"] = asset.exported_path.get_file();
+ format_dict["file_path"] = asset.exported_path;
+ format_dict["file_type"] = type;
+ if (framework_id.length() > 0) {
+ format_dict["framework_id"] = framework_id;
+ }
+ pbx_files += additional_asset_info_format.format(format_dict, "$_");
+ }
+
+ // Note, frameworks like gamekit are always included in our project.pbxprof file
+ // even if turned off in capabilities.
+
+ String str = String::utf8((const char *)p_project_data.ptr(), p_project_data.size());
+ str = str.replace("$additional_pbx_files", pbx_files);
+ str = str.replace("$additional_pbx_frameworks_build", pbx_frameworks_build);
+ str = str.replace("$additional_pbx_frameworks_refs", pbx_frameworks_refs);
+ str = str.replace("$additional_pbx_resources_build", pbx_resources_build);
+ str = str.replace("$additional_pbx_resources_refs", pbx_resources_refs);
+ str = str.replace("$pbx_embeded_frameworks", pbx_embeded_frameworks);
+
+ CharString cs = str.utf8();
+ p_project_data.resize(cs.size() - 1);
+ for (int i = 0; i < cs.size() - 1; i++) {
+ p_project_data.write[i] = cs[i];
+ }
+}
+
+Error EditorExportPlatformAppleEmbedded::_copy_asset(const Ref &p_preset, const String &p_out_dir, const String &p_asset, const String *p_custom_file_name, bool p_is_framework, bool p_should_embed, Vector &r_exported_assets) {
+ String binary_name = p_out_dir.get_file().get_basename();
+
+ Ref da = DirAccess::create_for_path(p_asset);
+ if (da.is_null()) {
+ ERR_FAIL_V_MSG(ERR_CANT_CREATE, "Can't open directory: " + p_asset + ".");
+ }
+ bool file_exists = da->file_exists(p_asset);
+ bool dir_exists = da->dir_exists(p_asset);
+ if (!file_exists && !dir_exists) {
+ return ERR_FILE_NOT_FOUND;
+ }
+
+ String base_dir = p_asset.get_base_dir().replace("res://", "").replace(".godot/mono/temp/bin/", "");
+ String asset = p_asset.ends_with("/") ? p_asset.left(-1) : p_asset;
+ String destination_dir;
+ String destination;
+ String asset_path;
+
+ Ref filesystem_da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
+ ERR_FAIL_COND_V_MSG(filesystem_da.is_null(), ERR_CANT_CREATE, "Cannot create DirAccess for path '" + p_out_dir + "'.");
+
+ if (p_is_framework && asset.ends_with(".dylib")) {
+ // For Apple Embedded platforms we need to turn .dylib into .framework
+ // to be able to send application to AppStore
+ asset_path = String("dylibs").path_join(base_dir);
+
+ String file_name;
+
+ if (!p_custom_file_name) {
+ file_name = p_asset.get_basename().get_file();
+ } else {
+ file_name = *p_custom_file_name;
+ }
+
+ String framework_name = file_name + ".framework";
+
+ asset_path = asset_path.path_join(framework_name);
+ destination_dir = p_out_dir.path_join(asset_path);
+ destination = destination_dir;
+
+ // Convert to framework and copy.
+ Error err = _convert_to_framework(p_asset, destination, p_preset->get("application/bundle_identifier"));
+ if (err) {
+ return err;
+ }
+ } else if (p_is_framework && asset.ends_with(".xcframework")) {
+ // For Apple Embedded platforms we need to turn .dylib inside .xcframework
+ // into .framework to be able to send application to AppStore
+
+ int total_libs = 0;
+ int static_libs = 0;
+ int dylibs = 0;
+ int frameworks = 0;
+ _check_xcframework_content(p_asset, total_libs, static_libs, dylibs, frameworks);
+
+ asset_path = String("dylibs").path_join(base_dir);
+ String file_name;
+
+ if (!p_custom_file_name) {
+ file_name = p_asset.get_file();
+ } else {
+ file_name = *p_custom_file_name;
+ }
+
+ asset_path = asset_path.path_join(file_name);
+ destination_dir = p_out_dir.path_join(asset_path);
+ destination = destination_dir;
+
+ if (dylibs > 0) {
+ // Convert to framework and copy.
+ Error err = _convert_to_framework(p_asset, destination, p_preset->get("application/bundle_identifier"));
+ if (err) {
+ return err;
+ }
+ } else {
+ // Copy as is.
+ if (!filesystem_da->dir_exists(destination_dir)) {
+ Error make_dir_err = filesystem_da->make_dir_recursive(destination_dir);
+ if (make_dir_err) {
+ return make_dir_err;
+ }
+ }
+ Error err = dir_exists ? da->copy_dir(p_asset, destination) : da->copy(p_asset, destination);
+ if (err) {
+ return err;
+ }
+ }
+ } else if (p_is_framework && asset.ends_with(".framework")) {
+ // Framework.
+ asset_path = String("dylibs").path_join(base_dir);
+
+ String file_name;
+
+ if (!p_custom_file_name) {
+ file_name = p_asset.get_file();
+ } else {
+ file_name = *p_custom_file_name;
+ }
+
+ asset_path = asset_path.path_join(file_name);
+ destination_dir = p_out_dir.path_join(asset_path);
+ destination = destination_dir;
+
+ // Copy as is.
+ if (!filesystem_da->dir_exists(destination_dir)) {
+ Error make_dir_err = filesystem_da->make_dir_recursive(destination_dir);
+ if (make_dir_err) {
+ return make_dir_err;
+ }
+ }
+ Error err = dir_exists ? da->copy_dir(p_asset, destination) : da->copy(p_asset, destination);
+ if (err) {
+ return err;
+ }
+ } else {
+ // Unknown resource.
+ asset_path = base_dir;
+
+ String file_name;
+
+ if (!p_custom_file_name) {
+ file_name = p_asset.get_file();
+ } else {
+ file_name = *p_custom_file_name;
+ }
+
+ destination_dir = p_out_dir.path_join(asset_path);
+ asset_path = asset_path.path_join(file_name);
+ destination = p_out_dir.path_join(asset_path);
+
+ // Copy as is.
+ if (!filesystem_da->dir_exists(destination_dir)) {
+ Error make_dir_err = filesystem_da->make_dir_recursive(destination_dir);
+ if (make_dir_err) {
+ return make_dir_err;
+ }
+ }
+ Error err = dir_exists ? da->copy_dir(p_asset, destination) : da->copy(p_asset, destination);
+ if (err) {
+ return err;
+ }
+ }
+
+ if (asset_path.ends_with("/")) {
+ asset_path = asset_path.left(-1);
+ }
+ AppleEmbeddedExportAsset exported_asset = { binary_name.path_join(asset_path), p_is_framework, p_should_embed };
+ r_exported_assets.push_back(exported_asset);
+
+ return OK;
+}
+
+Error EditorExportPlatformAppleEmbedded::_export_additional_assets(const Ref &p_preset, const String &p_out_dir, const Vector &p_assets, bool p_is_framework, bool p_should_embed, Vector &r_exported_assets) {
+ for (int f_idx = 0; f_idx < p_assets.size(); ++f_idx) {
+ const String &asset = p_assets[f_idx];
+ if (asset.begins_with("res://")) {
+ Error err = _copy_asset(p_preset, p_out_dir, asset, nullptr, p_is_framework, p_should_embed, r_exported_assets);
+ ERR_FAIL_COND_V(err != OK, err);
+ } else if (asset.is_absolute_path() && ProjectSettings::get_singleton()->localize_path(asset).begins_with("res://")) {
+ Error err = _copy_asset(p_preset, p_out_dir, ProjectSettings::get_singleton()->localize_path(asset), nullptr, p_is_framework, p_should_embed, r_exported_assets);
+ ERR_FAIL_COND_V(err != OK, err);
+ } else {
+ // either SDK-builtin or already a part of the export template
+ AppleEmbeddedExportAsset exported_asset = { asset, p_is_framework, p_should_embed };
+ r_exported_assets.push_back(exported_asset);
+ }
+ }
+
+ return OK;
+}
+
+Error EditorExportPlatformAppleEmbedded::_export_additional_assets(const Ref &p_preset, const String &p_out_dir, const Vector &p_libraries, Vector &r_exported_assets) {
+ Vector][> export_plugins = EditorExport::get_singleton()->get_export_plugins();
+ for (int i = 0; i < export_plugins.size(); i++) {
+ Vector linked_frameworks = export_plugins[i]->get_apple_embedded_platform_frameworks();
+ Error err = _export_additional_assets(p_preset, p_out_dir, linked_frameworks, true, false, r_exported_assets);
+ ERR_FAIL_COND_V(err, err);
+
+ Vector embedded_frameworks = export_plugins[i]->get_apple_embedded_platform_embedded_frameworks();
+ err = _export_additional_assets(p_preset, p_out_dir, embedded_frameworks, true, true, r_exported_assets);
+ ERR_FAIL_COND_V(err, err);
+
+ Vector project_static_libs = export_plugins[i]->get_apple_embedded_platform_project_static_libs();
+ for (int j = 0; j < project_static_libs.size(); j++) {
+ project_static_libs.write[j] = project_static_libs[j].get_file(); // Only the file name as it's copied to the project
+ }
+ err = _export_additional_assets(p_preset, p_out_dir, project_static_libs, true, false, r_exported_assets);
+ ERR_FAIL_COND_V(err, err);
+
+ Vector apple_embedded_platform_bundle_files = export_plugins[i]->get_apple_embedded_platform_bundle_files();
+ err = _export_additional_assets(p_preset, p_out_dir, apple_embedded_platform_bundle_files, false, false, r_exported_assets);
+ ERR_FAIL_COND_V(err, err);
+ }
+
+ Vector library_paths;
+ for (int i = 0; i < p_libraries.size(); ++i) {
+ library_paths.push_back(p_libraries[i].path);
+ }
+ Error err = _export_additional_assets(p_preset, p_out_dir, library_paths, true, true, r_exported_assets);
+ ERR_FAIL_COND_V(err, err);
+
+ return OK;
+}
+
+Vector EditorExportPlatformAppleEmbedded::_get_preset_architectures(const Ref &p_preset) const {
+ Vector all_archs = _get_supported_architectures();
+ Vector enabled_archs;
+ for (int i = 0; i < all_archs.size(); ++i) {
+ bool is_enabled = p_preset->get("architectures/" + all_archs[i].name);
+ if (is_enabled) {
+ enabled_archs.push_back(all_archs[i].name);
+ }
+ }
+ return enabled_archs;
+}
+
+Error EditorExportPlatformAppleEmbedded::_export_apple_embedded_plugins(const Ref &p_preset, AppleEmbeddedConfigData &p_config_data, const String &dest_dir, Vector &r_exported_assets, bool p_debug) {
+ String plugin_definition_cpp_code;
+ String plugin_initialization_cpp_code;
+ String plugin_deinitialization_cpp_code;
+
+ Vector plugin_linked_dependencies;
+ Vector plugin_embedded_dependencies;
+ Vector plugin_files;
+
+ Vector enabled_plugins = get_enabled_plugins(get_platform_name(), p_preset);
+
+ Vector added_linked_dependenciy_names;
+ Vector added_embedded_dependenciy_names;
+ HashMap plist_values;
+
+ HashSet plugin_linker_flags;
+
+ Error err;
+
+ for (int i = 0; i < enabled_plugins.size(); i++) {
+ PluginConfigAppleEmbedded plugin = enabled_plugins[i];
+
+ // Export plugin binary.
+ String plugin_main_binary = PluginConfigAppleEmbedded::get_plugin_main_binary(plugin, p_debug);
+ String plugin_binary_result_file = plugin.binary.get_file();
+ // We shouldn't embed .xcframework that contains static libraries.
+ // Static libraries are not embedded anyway.
+ err = _copy_asset(p_preset, dest_dir, plugin_main_binary, &plugin_binary_result_file, true, false, r_exported_assets);
+ ERR_FAIL_COND_V(err != OK, err);
+
+ // Adding dependencies.
+ // Use separate container for names to check for duplicates.
+ for (int j = 0; j < plugin.linked_dependencies.size(); j++) {
+ String dependency = plugin.linked_dependencies[j];
+ String name = dependency.get_file();
+
+ if (added_linked_dependenciy_names.has(name)) {
+ continue;
+ }
+
+ added_linked_dependenciy_names.push_back(name);
+ plugin_linked_dependencies.push_back(dependency);
+ }
+
+ for (int j = 0; j < plugin.system_dependencies.size(); j++) {
+ String dependency = plugin.system_dependencies[j];
+ String name = dependency.get_file();
+
+ if (added_linked_dependenciy_names.has(name)) {
+ continue;
+ }
+
+ added_linked_dependenciy_names.push_back(name);
+ plugin_linked_dependencies.push_back(dependency);
+ }
+
+ for (int j = 0; j < plugin.embedded_dependencies.size(); j++) {
+ String dependency = plugin.embedded_dependencies[j];
+ String name = dependency.get_file();
+
+ if (added_embedded_dependenciy_names.has(name)) {
+ continue;
+ }
+
+ added_embedded_dependenciy_names.push_back(name);
+ plugin_embedded_dependencies.push_back(dependency);
+ }
+
+ plugin_files.append_array(plugin.files_to_copy);
+
+ // Capabilities
+ // Also checking for duplicates.
+ for (int j = 0; j < plugin.capabilities.size(); j++) {
+ String capability = plugin.capabilities[j];
+
+ if (p_config_data.capabilities.has(capability)) {
+ continue;
+ }
+
+ p_config_data.capabilities.push_back(capability);
+ }
+
+ // Linker flags
+ // Checking duplicates
+ for (int j = 0; j < plugin.linker_flags.size(); j++) {
+ String linker_flag = plugin.linker_flags[j];
+ plugin_linker_flags.insert(linker_flag);
+ }
+
+ // Plist
+ // Using hash map container to remove duplicates
+
+ for (const KeyValue &E : plugin.plist) {
+ String key = E.key;
+ const PluginConfigAppleEmbedded::PlistItem &item = E.value;
+
+ String value;
+
+ switch (item.type) {
+ case PluginConfigAppleEmbedded::PlistItemType::STRING_INPUT: {
+ String preset_name = "plugins_plist/" + key;
+ String input_value = p_preset->get(preset_name);
+ value = "" + input_value + "";
+ } break;
+ default:
+ value = item.value;
+ break;
+ }
+
+ if (key.is_empty() || value.is_empty()) {
+ continue;
+ }
+
+ String plist_key = "" + key + "";
+
+ plist_values[plist_key] = value;
+ }
+
+ // CPP Code
+ String definition_comment = "// Plugin: " + plugin.name + "\n";
+ String initialization_method = plugin.initialization_method + "();\n";
+ String deinitialization_method = plugin.deinitialization_method + "();\n";
+
+ plugin_definition_cpp_code += definition_comment +
+ "extern void " + initialization_method +
+ "extern void " + deinitialization_method + "\n";
+
+ plugin_initialization_cpp_code += "\t" + initialization_method;
+ plugin_deinitialization_cpp_code += "\t" + deinitialization_method;
+
+ if (plugin.use_swift_runtime) {
+ p_config_data.use_swift_runtime = true;
+ }
+ }
+
+ // Updating `Info.plist`
+ {
+ for (const KeyValue &E : plist_values) {
+ String key = E.key;
+ String value = E.value;
+
+ if (key.is_empty() || value.is_empty()) {
+ continue;
+ }
+
+ p_config_data.plist_content += key + value + "\n";
+ }
+ }
+
+ // Export files
+ {
+ // Export linked plugin dependency
+ err = _export_additional_assets(p_preset, dest_dir, plugin_linked_dependencies, true, false, r_exported_assets);
+ ERR_FAIL_COND_V(err != OK, err);
+
+ // Export embedded plugin dependency
+ err = _export_additional_assets(p_preset, dest_dir, plugin_embedded_dependencies, true, true, r_exported_assets);
+ ERR_FAIL_COND_V(err != OK, err);
+
+ // Export plugin files
+ err = _export_additional_assets(p_preset, dest_dir, plugin_files, false, false, r_exported_assets);
+ ERR_FAIL_COND_V(err != OK, err);
+ }
+
+ // Update CPP
+ {
+ Dictionary plugin_format;
+ plugin_format["definition"] = plugin_definition_cpp_code;
+ plugin_format["initialization"] = plugin_initialization_cpp_code;
+ plugin_format["deinitialization"] = plugin_deinitialization_cpp_code;
+
+ String plugin_cpp_code = "\n// Godot Plugins\n"
+ "void godot_apple_embedded_plugins_initialize();\n"
+ "void godot_apple_embedded_plugins_deinitialize();\n"
+ "// Exported Plugins\n\n"
+ "$definition"
+ "// Use Plugins\n"
+ "void godot_apple_embedded_plugins_initialize() {\n"
+ "$initialization"
+ "}\n\n"
+ "void godot_apple_embedded_plugins_deinitialize() {\n"
+ "$deinitialization"
+ "}\n";
+
+ p_config_data.cpp_code += plugin_cpp_code.format(plugin_format, "$_");
+ }
+
+ // Update Linker Flag Values
+ {
+ String result_linker_flags = " ";
+ for (const String &E : plugin_linker_flags) {
+ const String &flag = E;
+
+ if (flag.length() == 0) {
+ continue;
+ }
+
+ if (result_linker_flags.length() > 0) {
+ result_linker_flags += ' ';
+ }
+
+ result_linker_flags += flag;
+ }
+ result_linker_flags = result_linker_flags.replace("\"", "\\\"");
+ p_config_data.linker_flags += result_linker_flags;
+ }
+
+ return OK;
+}
+
+Error EditorExportPlatformAppleEmbedded::export_project(const Ref &p_preset, bool p_debug, const String &p_path, BitField p_flags) {
+ return _export_project_helper(p_preset, p_debug, p_path, p_flags, false);
+}
+
+Error EditorExportPlatformAppleEmbedded::_export_project_helper(const Ref &p_preset, bool p_debug, const String &p_path, BitField p_flags, bool p_oneclick) {
+ ExportNotifier notifier(*this, p_preset, p_debug, p_path, p_flags);
+
+ const String dest_dir = p_path.get_base_dir() + "/";
+ const String binary_name = p_path.get_file().get_basename();
+ const String binary_dir = dest_dir + binary_name;
+
+ if (!DirAccess::exists(dest_dir)) {
+ add_message(EXPORT_MESSAGE_ERROR, TTR("Export"), vformat(TTR("Target folder does not exist or is inaccessible: \"%s\""), dest_dir));
+ return ERR_FILE_BAD_PATH;
+ }
+
+ bool export_project_only = p_preset->get("application/export_project_only");
+ if (p_oneclick) {
+ export_project_only = false; // Skip for one-click deploy.
+ }
+
+ EditorProgress ep("export", export_project_only ? TTR("Exporting for " + get_name() + " (Project Files Only)") : TTR("Exporting for " + get_name() + ""), export_project_only ? 2 : 5, true);
+
+ String team_id = p_preset->get("application/app_store_team_id");
+ ERR_FAIL_COND_V_MSG(team_id.length() == 0, ERR_CANT_OPEN, "App Store Team ID not specified - cannot configure the project.");
+
+ String src_pkg_name;
+ if (p_debug) {
+ src_pkg_name = p_preset->get("custom_template/debug");
+ } else {
+ src_pkg_name = p_preset->get("custom_template/release");
+ }
+
+ if (src_pkg_name.is_empty()) {
+ String err;
+ src_pkg_name = find_export_template(get_platform_name() + ".zip", &err);
+ if (src_pkg_name.is_empty()) {
+ add_message(EXPORT_MESSAGE_ERROR, TTR("Prepare Templates"), TTR("Export template not found."));
+ return ERR_FILE_NOT_FOUND;
+ }
+ }
+
+ {
+ bool delete_old = p_preset->get("application/delete_old_export_files_unconditionally");
+ Ref da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
+ if (da.is_valid()) {
+ String current_dir = da->get_current_dir();
+
+ // Remove leftovers from last export so they don't interfere in case some files are no longer needed.
+ if (da->change_dir(binary_dir + ".xcodeproj") == OK) {
+ // Check directory content before deleting.
+ int expected_files = 0;
+ int total_files = 0;
+ if (!delete_old) {
+ da->list_dir_begin();
+ for (String n = da->get_next(); !n.is_empty(); n = da->get_next()) {
+ if (!n.begins_with(".")) { // Ignore ".", ".." and hidden files.
+ if (da->current_is_dir()) {
+ if (n == "xcshareddata" || n == "project.xcworkspace") {
+ expected_files++;
+ }
+ } else {
+ if (n == "project.pbxproj") {
+ expected_files++;
+ }
+ }
+ total_files++;
+ }
+ }
+ da->list_dir_end();
+ }
+ if ((total_files == 0) || (expected_files >= Math::floor(total_files * 0.8))) {
+ da->erase_contents_recursive();
+ } else {
+ add_message(EXPORT_MESSAGE_ERROR, TTR("Export"), vformat(TTR("Unexpected files found in the export destination directory \"%s.xcodeproj\", delete it manually or select another destination."), binary_dir));
+ return ERR_CANT_CREATE;
+ }
+ }
+ da->change_dir(current_dir);
+
+ if (da->change_dir(binary_dir) == OK) {
+ // Check directory content before deleting.
+ int expected_files = 0;
+ int total_files = 0;
+ if (!delete_old) {
+ da->list_dir_begin();
+ for (String n = da->get_next(); !n.is_empty(); n = da->get_next()) {
+ if (!n.begins_with(".")) { // Ignore ".", ".." and hidden files.
+ if (da->current_is_dir()) {
+ if (n == "dylibs" || n == "Images.xcassets" || n.ends_with(".lproj") || n == "godot-publish-dotnet" || n.ends_with(".xcframework") || n.ends_with(".framework")) {
+ expected_files++;
+ }
+ } else {
+ if (n == binary_name + "-Info.plist" || n == binary_name + ".entitlements" || n == "Launch Screen.storyboard" || n == "export_options.plist" || n.begins_with("dummy.") || n.ends_with(".gdip")) {
+ expected_files++;
+ }
+ }
+ total_files++;
+ }
+ }
+ da->list_dir_end();
+ }
+ if ((total_files == 0) || (expected_files >= Math::floor(total_files * 0.8))) {
+ da->erase_contents_recursive();
+ } else {
+ add_message(EXPORT_MESSAGE_ERROR, TTR("Export"), vformat(TTR("Unexpected files found in the export destination directory \"%s\", delete it manually or select another destination."), binary_dir));
+ return ERR_CANT_CREATE;
+ }
+ }
+ da->change_dir(current_dir);
+
+ if (!da->dir_exists(binary_dir)) {
+ Error err = da->make_dir(binary_dir);
+ if (err != OK) {
+ add_message(EXPORT_MESSAGE_ERROR, TTR("Export"), vformat(TTR("Failed to create the directory: \"%s\""), binary_dir));
+ return err;
+ }
+ }
+ }
+ }
+
+ if (ep.step("Making .pck", 0)) {
+ return ERR_SKIP;
+ }
+ String pack_path = binary_dir + ".pck";
+ Vector libraries;
+ Error err = save_pack(p_preset, p_debug, pack_path, &libraries);
+ if (err) {
+ // Message is supplied by the subroutine method.
+ return err;
+ }
+
+ if (ep.step("Extracting and configuring Xcode project", 1)) {
+ return ERR_SKIP;
+ }
+
+ String library_to_use = "libgodot." + get_platform_name() + "." + String(p_debug ? "debug" : "release") + ".xcframework";
+
+ print_line("Static framework: " + library_to_use);
+ String pkg_name;
+ if (String(get_project_setting(p_preset, "application/config/name")) != "") {
+ pkg_name = String(get_project_setting(p_preset, "application/config/name"));
+ } else {
+ pkg_name = "Unnamed";
+ }
+
+ bool found_library = false;
+
+ const String godot_platform = "godot_" + get_platform_name();
+ const String project_file = godot_platform + ".xcodeproj/project.pbxproj";
+ HashSet files_to_parse;
+ files_to_parse.insert(godot_platform + "/godot_" + get_platform_name() + "-Info.plist");
+ files_to_parse.insert(project_file);
+ files_to_parse.insert(godot_platform + "/export_options.plist");
+ files_to_parse.insert(godot_platform + "/dummy.cpp");
+ files_to_parse.insert(godot_platform + ".xcodeproj/project.xcworkspace/contents.xcworkspacedata");
+ files_to_parse.insert(godot_platform + ".xcodeproj/xcshareddata/xcschemes/godot_" + get_platform_name() + ".xcscheme");
+ files_to_parse.insert(godot_platform + "/godot_" + get_platform_name() + ".entitlements");
+ files_to_parse.insert(godot_platform + "/Launch Screen.storyboard");
+ files_to_parse.insert("PrivacyInfo.xcprivacy");
+
+ AppleEmbeddedConfigData config_data = {
+ pkg_name,
+ binary_name,
+ _get_additional_plist_content(),
+ String(" ").join(_get_preset_architectures(p_preset)),
+ _get_linker_flags(),
+ _get_cpp_code(),
+ "",
+ "",
+ "",
+ "",
+ Vector(),
+ false
+ };
+
+ config_data.plist_content += p_preset->get("application/additional_plist_content").operator String() + "\n";
+
+ Vector assets;
+
+ Ref tmp_app_path = DirAccess::create_for_path(dest_dir);
+ if (tmp_app_path.is_null()) {
+ add_message(EXPORT_MESSAGE_ERROR, TTR("Prepare Templates"), vformat(TTR("Could not create and open the directory: \"%s\""), dest_dir));
+ return ERR_CANT_CREATE;
+ }
+
+ print_line("Unzipping...");
+ Ref io_fa;
+ zlib_filefunc_def io = zipio_create_io(&io_fa);
+ unzFile src_pkg_zip = unzOpen2(src_pkg_name.utf8().get_data(), &io);
+ if (!src_pkg_zip) {
+ add_message(EXPORT_MESSAGE_ERROR, TTR("Prepare Templates"), TTR("Could not open export template (not a zip file?): \"%s\".", src_pkg_name));
+ return ERR_CANT_OPEN;
+ }
+
+ err = _export_apple_embedded_plugins(p_preset, config_data, binary_dir, assets, p_debug);
+ if (err != OK) {
+ // TODO: Improve error reporting by using `add_message` throughout all methods called via `_export_apple_embedded_plugins`.
+ // For now a generic top level message would be fine, but we're ought to use proper reporting here instead of
+ // just fail macros and non-descriptive error return values.
+ add_message(EXPORT_MESSAGE_ERROR, TTR("Apple Embedded Plugins"), vformat(TTR("Failed to export Apple Embedded plugins with code %d. Please check the output log."), err));
+ return err;
+ }
+
+ //export rest of the files
+ int ret = unzGoToFirstFile(src_pkg_zip);
+ Vector project_file_data;
+ while (ret == UNZ_OK) {
+#if defined(MACOS_ENABLED) || defined(LINUXBSD_ENABLED)
+ bool is_execute = false;
+#endif
+
+ //get filename
+ unz_file_info info;
+ char fname[16384];
+ ret = unzGetCurrentFileInfo(src_pkg_zip, &info, fname, 16384, nullptr, 0, nullptr, 0);
+ if (ret != UNZ_OK) {
+ break;
+ }
+
+ String file = String::utf8(fname);
+
+ print_line("READ: " + file);
+ Vector data;
+ data.resize(info.uncompressed_size);
+
+ //read
+ unzOpenCurrentFile(src_pkg_zip);
+ unzReadCurrentFile(src_pkg_zip, data.ptrw(), data.size());
+ unzCloseCurrentFile(src_pkg_zip);
+
+ //write
+
+ if (files_to_parse.has(file)) {
+ _fix_config_file(p_preset, data, config_data, p_debug);
+ } else if (file.begins_with("libgodot." + get_platform_name())) {
+ if (!file.begins_with(library_to_use) || file.ends_with(String("/empty"))) {
+ ret = unzGoToNextFile(src_pkg_zip);
+ continue; //ignore!
+ }
+ found_library = true;
+#if defined(MACOS_ENABLED) || defined(LINUXBSD_ENABLED)
+ is_execute = true;
+#endif
+ file = file.replace(library_to_use, binary_name + ".xcframework");
+ }
+
+ if (file == project_file) {
+ project_file_data = data;
+ }
+
+ ///@TODO need to parse logo files
+
+ if (data.size() > 0) {
+ file = file.replace("godot_" + get_platform_name(), binary_name);
+
+ print_line("ADDING: " + file + " size: " + itos(data.size()));
+
+ /* write it into our folder structure */
+ file = dest_dir + file;
+
+ /* make sure this folder exists */
+ String dir_name = file.get_base_dir();
+ if (!tmp_app_path->dir_exists(dir_name)) {
+ print_line("Creating " + dir_name);
+ Error dir_err = tmp_app_path->make_dir_recursive(dir_name);
+ if (dir_err) {
+ unzClose(src_pkg_zip);
+ add_message(EXPORT_MESSAGE_ERROR, TTR("Export"), vformat(TTR("Could not create a directory at path \"%s\"."), dir_name));
+ return ERR_CANT_CREATE;
+ }
+ }
+
+ /* write the file */
+ {
+ Ref f = FileAccess::open(file, FileAccess::WRITE);
+ if (f.is_null()) {
+ unzClose(src_pkg_zip);
+ add_message(EXPORT_MESSAGE_ERROR, TTR("Export"), vformat(TTR("Could not write to a file at path \"%s\"."), file));
+ return ERR_CANT_CREATE;
+ };
+ f->store_buffer(data.ptr(), data.size());
+ }
+
+#if defined(MACOS_ENABLED) || defined(LINUXBSD_ENABLED)
+ if (is_execute) {
+ // we need execute rights on this file
+ chmod(file.utf8().get_data(), 0755);
+ }
+#endif
+ }
+
+ ret = unzGoToNextFile(src_pkg_zip);
+ }
+
+ // We're done with our source zip.
+ unzClose(src_pkg_zip);
+
+ if (!found_library) {
+ add_message(EXPORT_MESSAGE_ERROR, TTR("Export"), vformat(TTR("Requested template library '%s' not found. It might be missing from your template archive."), library_to_use));
+ return ERR_FILE_NOT_FOUND;
+ }
+
+ Dictionary appnames = get_project_setting(p_preset, "application/config/name_localized");
+ Dictionary camera_usage_descriptions = p_preset->get("privacy/camera_usage_description_localized");
+ Dictionary microphone_usage_descriptions = p_preset->get("privacy/microphone_usage_description_localized");
+ Dictionary photolibrary_usage_descriptions = p_preset->get("privacy/photolibrary_usage_description_localized");
+
+ Vector translations = get_project_setting(p_preset, "internationalization/locale/translations");
+ if (translations.size() > 0) {
+ {
+ String fname = binary_dir + "/en.lproj";
+ tmp_app_path->make_dir_recursive(fname);
+ Ref f = FileAccess::open(fname + "/InfoPlist.strings", FileAccess::WRITE);
+ f->store_line("/* Localized versions of Info.plist keys */");
+ f->store_line("");
+ f->store_line("CFBundleDisplayName = \"" + get_project_setting(p_preset, "application/config/name").operator String() + "\";");
+ f->store_line("NSCameraUsageDescription = \"" + p_preset->get("privacy/camera_usage_description").operator String() + "\";");
+ f->store_line("NSMicrophoneUsageDescription = \"" + p_preset->get("privacy/microphone_usage_description").operator String() + "\";");
+ f->store_line("NSPhotoLibraryUsageDescription = \"" + p_preset->get("privacy/photolibrary_usage_description").operator String() + "\";");
+ }
+
+ HashSet languages;
+ for (const String &E : translations) {
+ Ref tr = ResourceLoader::load(E);
+ if (tr.is_valid() && tr->get_locale() != "en") {
+ languages.insert(tr->get_locale());
+ }
+ }
+
+ for (const String &lang : languages) {
+ String fname = binary_dir + "/" + lang + ".lproj";
+ tmp_app_path->make_dir_recursive(fname);
+ Ref f = FileAccess::open(fname + "/InfoPlist.strings", FileAccess::WRITE);
+ f->store_line("/* Localized versions of Info.plist keys */");
+ f->store_line("");
+ if (appnames.has(lang)) {
+ f->store_line("CFBundleDisplayName = \"" + appnames[lang].operator String() + "\";");
+ }
+ if (camera_usage_descriptions.has(lang)) {
+ f->store_line("NSCameraUsageDescription = \"" + camera_usage_descriptions[lang].operator String() + "\";");
+ }
+ if (microphone_usage_descriptions.has(lang)) {
+ f->store_line("NSMicrophoneUsageDescription = \"" + microphone_usage_descriptions[lang].operator String() + "\";");
+ }
+ if (photolibrary_usage_descriptions.has(lang)) {
+ f->store_line("NSPhotoLibraryUsageDescription = \"" + photolibrary_usage_descriptions[lang].operator String() + "\";");
+ }
+ }
+ }
+
+ // Copy project static libs to the project
+ Vector][> export_plugins = EditorExport::get_singleton()->get_export_plugins();
+ for (int i = 0; i < export_plugins.size(); i++) {
+ Vector project_static_libs = export_plugins[i]->get_apple_embedded_platform_project_static_libs();
+ for (int j = 0; j < project_static_libs.size(); j++) {
+ const String &static_lib_path = project_static_libs[j];
+ String dest_lib_file_path = dest_dir + static_lib_path.get_file();
+ Error lib_copy_err = tmp_app_path->copy(static_lib_path, dest_lib_file_path);
+ if (lib_copy_err != OK) {
+ add_message(EXPORT_MESSAGE_ERROR, TTR("Export"), vformat(TTR("Could not copy a file at path \"%s\" to \"%s\"."), static_lib_path, dest_lib_file_path));
+ return lib_copy_err;
+ }
+ }
+ }
+
+ String iconset_dir = binary_dir + "/Images.xcassets/AppIcon.appiconset/";
+ err = OK;
+ if (!tmp_app_path->dir_exists(iconset_dir)) {
+ err = tmp_app_path->make_dir_recursive(iconset_dir);
+ }
+ if (err != OK) {
+ add_message(EXPORT_MESSAGE_ERROR, TTR("Export"), vformat(TTR("Could not create a directory at path \"%s\"."), iconset_dir));
+ return err;
+ }
+
+ err = _export_icons(p_preset, iconset_dir);
+ if (err != OK) {
+ // Message is supplied by the subroutine method.
+ return err;
+ }
+
+ {
+ String splash_image_path = binary_dir + "/Images.xcassets/SplashImage.imageset/";
+
+ Ref]