diff options
Diffstat (limited to 'platform/ios')
-rw-r--r-- | platform/ios/README.md | 14 | ||||
-rw-r--r-- | platform/ios/app_delegate.mm | 9 | ||||
-rw-r--r-- | platform/ios/detect.py | 65 | ||||
-rw-r--r-- | platform/ios/display_layer.mm | 10 | ||||
-rw-r--r-- | platform/ios/display_server_ios.h | 12 | ||||
-rw-r--r-- | platform/ios/display_server_ios.mm | 60 | ||||
-rw-r--r-- | platform/ios/export/export.cpp | 1 | ||||
-rw-r--r-- | platform/ios/export/export_plugin.cpp | 356 | ||||
-rw-r--r-- | platform/ios/export/export_plugin.h | 26 | ||||
-rw-r--r-- | platform/ios/export/godot_plugin_config.cpp | 15 | ||||
-rw-r--r-- | platform/ios/export/godot_plugin_config.h | 3 | ||||
-rw-r--r-- | platform/ios/godot_ios.mm | 8 | ||||
-rw-r--r-- | platform/ios/godot_view.mm | 15 | ||||
-rw-r--r-- | platform/ios/keyboard_input_view.h | 2 | ||||
-rw-r--r-- | platform/ios/keyboard_input_view.mm | 2 | ||||
-rw-r--r-- | platform/ios/os_ios.h | 17 | ||||
-rw-r--r-- | platform/ios/os_ios.mm | 126 | ||||
-rw-r--r-- | platform/ios/platform_config.h | 2 | ||||
-rw-r--r-- | platform/ios/tts_ios.mm | 6 | ||||
-rw-r--r-- | platform/ios/view_controller.mm | 12 | ||||
-rw-r--r-- | platform/ios/vulkan_context_ios.h | 4 | ||||
-rw-r--r-- | platform/ios/vulkan_context_ios.mm | 4 |
22 files changed, 505 insertions, 264 deletions
diff --git a/platform/ios/README.md b/platform/ios/README.md new file mode 100644 index 0000000000..82c275ad31 --- /dev/null +++ b/platform/ios/README.md @@ -0,0 +1,14 @@ +# iOS platform port + +This folder contains the C++, Objective-C and Objective-C++ code for the iOS +platform port. + +See also [`misc/dist/ios_xcode`](/misc/dist/ios_xcode) folder for the Xcode +project template used for packaging the iOS export templates. + +## Documentation + +- [Compiling for iOS](https://docs.godotengine.org/en/latest/development/compiling/compiling_for_ios.html) + - Instructions on building this platform port from source. +- [Exporting for iOS](https://docs.godotengine.org/en/latest/tutorials/export/exporting_for_ios.html) + - Instructions on using the compiled export templates to export a project. diff --git a/platform/ios/app_delegate.mm b/platform/ios/app_delegate.mm index fb183d52d4..3ebd530585 100644 --- a/platform/ios/app_delegate.mm +++ b/platform/ios/app_delegate.mm @@ -45,7 +45,7 @@ extern int gargc; extern char **gargv; -extern int ios_main(int, char **, String, String); +extern int ios_main(int, char **); extern void ios_finish(); @implementation AppDelegate @@ -66,12 +66,7 @@ static ViewController *mainViewController = nil; // Create a full-screen window self.window = [[UIWindow alloc] initWithFrame:windowBounds]; - NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); - NSString *documentsDirectory = [paths objectAtIndex:0]; - paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES); - NSString *cacheDirectory = [paths objectAtIndex:0]; - - int err = ios_main(gargc, gargv, String::utf8([documentsDirectory UTF8String]), String::utf8([cacheDirectory UTF8String])); + int err = ios_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... diff --git a/platform/ios/detect.py b/platform/ios/detect.py index 67c90b10a0..38e62134b5 100644 --- a/platform/ios/detect.py +++ b/platform/ios/detect.py @@ -2,6 +2,11 @@ import os import sys from methods import detect_darwin_sdk_path +from typing import TYPE_CHECKING + +if TYPE_CHECKING: + from SCons import Environment + def is_active(): return True @@ -36,38 +41,34 @@ def get_opts(): def get_flags(): return [ - ("tools", False), + ("arch", "arm64"), # Default for convenience. + ("target", "template_debug"), ("use_volk", False), ] -def configure(env): - ## Build type - - if env["target"].startswith("release"): - env.Append(CPPDEFINES=["NDEBUG", ("NS_BLOCK_ASSERTIONS", 1)]) - if env["optimize"] == "speed": # optimize for speed (default) - # `-O2` is more friendly to debuggers than `-O3`, leading to better crash backtraces - # when using `target=release_debug`. - opt = "-O3" if env["target"] == "release" else "-O2" - env.Append(CCFLAGS=[opt, "-ftree-vectorize", "-fomit-frame-pointer"]) - env.Append(LINKFLAGS=[opt]) - elif env["optimize"] == "size": # optimize for size - env.Append(CCFLAGS=["-Os", "-ftree-vectorize"]) - env.Append(LINKFLAGS=["-Os"]) +def configure(env: "Environment"): + # Validate arch. + supported_arches = ["x86_64", "arm64"] + if env["arch"] not in supported_arches: + print( + 'Unsupported CPU architecture "%s" for iOS. Supported architectures are: %s.' + % (env["arch"], ", ".join(supported_arches)) + ) + sys.exit() - elif env["target"] == "debug": - env.Append(CCFLAGS=["-gdwarf-2", "-O0"]) - env.Append(CPPDEFINES=["_DEBUG", ("DEBUG", 1)]) + ## LTO - if env["use_lto"]: - env.Append(CCFLAGS=["-flto"]) - env.Append(LINKFLAGS=["-flto"]) + if env["lto"] == "auto": # Disable by default as it makes linking in Xcode very slow. + env["lto"] = "none" - ## Architecture - env["bits"] = "64" - if env["arch"] != "x86_64": - env["arch"] = "arm64" + if env["lto"] != "none": + if env["lto"] == "thin": + env.Append(CCFLAGS=["-flto=thin"]) + env.Append(LINKFLAGS=["-flto=thin"]) + else: + env.Append(CCFLAGS=["-flto"]) + env.Append(LINKFLAGS=["-flto"]) ## Compiler configuration @@ -107,6 +108,10 @@ def configure(env): env.Append(CCFLAGS=["-miphoneos-version-min=11.0"]) if env["arch"] == "x86_64": + if not env["ios_simulator"]: + print("ERROR: Building for iOS with 'arch=x86_64' requires 'ios_simulator=yes'.") + sys.exit(255) + env["ENV"]["MACOSX_DEPLOYMENT_TARGET"] = "10.9" env.Append( CCFLAGS=( @@ -128,12 +133,10 @@ def configure(env): env.Append(ASFLAGS=["-arch", "arm64"]) env.Append(CPPDEFINES=["NEED_LONG_INT"]) - # Disable exceptions on non-tools (template) builds - if not env["tools"]: - if env["ios_exceptions"]: - env.Append(CCFLAGS=["-fexceptions"]) - else: - env.Append(CCFLAGS=["-fno-exceptions"]) + if env["ios_exceptions"]: + env.Append(CCFLAGS=["-fexceptions"]) + else: + env.Append(CCFLAGS=["-fno-exceptions"]) # Temp fix for ABS/MAX/MIN macros in iOS SDK blocking compilation env.Append(CCFLAGS=["-Wno-ambiguous-macro"]) diff --git a/platform/ios/display_layer.mm b/platform/ios/display_layer.mm index 7c83494768..74c760ae9a 100644 --- a/platform/ios/display_layer.mm +++ b/platform/ios/display_layer.mm @@ -89,12 +89,12 @@ // FIXME: Add Vulkan support via MoltenVK. Add fallback code back? - // Create GL ES 2 context - if (GLOBAL_GET("rendering/driver/driver_name") == "opengl3") { - context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2]; - NSLog(@"Setting up an OpenGL ES 2.0 context."); + // Create GL ES 3 context + if (GLOBAL_GET("rendering/renderer/rendering_method") == "gl_compatibility") { + context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES3]; + NSLog(@"Setting up an OpenGL ES 3.0 context."); if (!context) { - NSLog(@"Failed to create OpenGL ES 2.0 context!"); + NSLog(@"Failed to create OpenGL ES 3.0 context!"); return; } } diff --git a/platform/ios/display_server_ios.h b/platform/ios/display_server_ios.h index 5dd4d177ea..447f919139 100644 --- a/platform/ios/display_server_ios.h +++ b/platform/ios/display_server_ios.h @@ -40,7 +40,6 @@ #include "vulkan_context_ios.h" -#import <QuartzCore/CAMetalLayer.h> #ifdef USE_VOLK #include <volk.h> #else @@ -48,6 +47,9 @@ #endif #endif +#import <Foundation/Foundation.h> +#import <QuartzCore/CAMetalLayer.h> + class DisplayServerIOS : public DisplayServer { GDCLASS(DisplayServerIOS, DisplayServer) @@ -73,7 +75,7 @@ class DisplayServerIOS : public DisplayServer { void perform_event(const Ref<InputEvent> &p_event); - DisplayServerIOS(const String &p_rendering_driver, DisplayServer::WindowMode p_mode, DisplayServer::VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i &p_resolution, Error &r_error); + DisplayServerIOS(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, Error &r_error); ~DisplayServerIOS(); public: @@ -82,7 +84,7 @@ public: static DisplayServerIOS *get_singleton(); static void register_ios_driver(); - static DisplayServer *create_func(const String &p_rendering_driver, WindowMode p_mode, DisplayServer::VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i &p_resolution, Error &r_error); + static DisplayServer *create_func(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, Error &r_error); static Vector<String> get_rendering_drivers_func(); // MARK: - Events @@ -127,7 +129,7 @@ public: virtual bool tts_is_speaking() const override; virtual bool tts_is_paused() const override; - virtual Array tts_get_voices() const override; + virtual TypedArray<Dictionary> 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; @@ -199,7 +201,7 @@ public: virtual bool screen_is_touchscreen(int p_screen) const override; - virtual void virtual_keyboard_show(const String &p_existing_text, const Rect2 &p_screen_rect, bool p_multiline, int p_max_length, int p_cursor_start, int p_cursor_end) 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); diff --git a/platform/ios/display_server_ios.mm b/platform/ios/display_server_ios.mm index 73d4a2a427..6793b40dd4 100644 --- a/platform/ios/display_server_ios.mm +++ b/platform/ios/display_server_ios.mm @@ -41,7 +41,6 @@ #include "tts_ios.h" #import "view_controller.h" -#import <Foundation/Foundation.h> #import <sys/utsname.h> static const float kDisplayServerIOSAcceleration = 1.f; @@ -50,7 +49,7 @@ DisplayServerIOS *DisplayServerIOS::get_singleton() { return (DisplayServerIOS *)DisplayServer::get_singleton(); } -DisplayServerIOS::DisplayServerIOS(const String &p_rendering_driver, WindowMode p_mode, DisplayServer::VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i &p_resolution, Error &r_error) { +DisplayServerIOS::DisplayServerIOS(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, Error &r_error) { rendering_driver = p_rendering_driver; // Init TTS @@ -62,7 +61,7 @@ DisplayServerIOS::DisplayServerIOS(const String &p_rendering_driver, WindowMode // Note that we should be checking "opengl3" as the driver, might never enable this seeing OpenGL is deprecated on iOS // We are hardcoding the rendering_driver to "vulkan" down below - if (rendering_driver == "opengl_es") { + if (rendering_driver == "opengl3") { bool gl_initialization_error = false; // FIXME: Add Vulkan support via MoltenVK. Add fallback code back? @@ -128,7 +127,7 @@ DisplayServerIOS::DisplayServerIOS(const String &p_rendering_driver, WindowMode } #endif - bool keep_screen_on = bool(GLOBAL_DEF("display/window/energy_saving/keep_screen_on", true)); + 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); @@ -152,8 +151,8 @@ DisplayServerIOS::~DisplayServerIOS() { #endif } -DisplayServer *DisplayServerIOS::create_func(const String &p_rendering_driver, WindowMode p_mode, DisplayServer::VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i &p_resolution, Error &r_error) { - return memnew(DisplayServerIOS(p_rendering_driver, p_mode, p_vsync_mode, p_flags, p_resolution, r_error)); +DisplayServer *DisplayServerIOS::create_func(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, Error &r_error) { + return memnew(DisplayServerIOS(p_rendering_driver, p_mode, p_vsync_mode, p_flags, p_position, p_resolution, r_error)); } Vector<String> DisplayServerIOS::get_rendering_drivers_func() { @@ -163,7 +162,7 @@ Vector<String> DisplayServerIOS::get_rendering_drivers_func() { drivers.push_back("vulkan"); #endif #if defined(GLES3_ENABLED) - drivers.push_back("opengl_es"); + drivers.push_back("opengl3"); #endif return drivers; @@ -219,7 +218,7 @@ void DisplayServerIOS::_window_callback(const Callable &p_callable, const Varian const Variant *argp = &p_arg; Variant ret; Callable::CallError ce; - p_callable.call((const Variant **)&argp, 1, ret, ce); + p_callable.callp((const Variant **)&argp, 1, ret, ce); } } @@ -228,19 +227,20 @@ void DisplayServerIOS::_window_callback(const Callable &p_callable, const Varian // MARK: Touches void DisplayServerIOS::touch_press(int p_idx, int p_x, int p_y, bool p_pressed, bool p_double_click) { - if (!GLOBAL_DEF("debug/disable_touch", false)) { + if (!GLOBAL_GET("debug/disable_touch")) { Ref<InputEventScreenTouch> 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 DisplayServerIOS::touch_drag(int p_idx, int p_prev_x, int p_prev_y, int p_x, int p_y) { - if (!GLOBAL_DEF("debug/disable_touch", false)) { + if (!GLOBAL_GET("debug/disable_touch")) { Ref<InputEventScreenDrag> ev; ev.instantiate(); ev->set_index(p_idx); @@ -336,8 +336,8 @@ bool DisplayServerIOS::tts_is_paused() const { return [tts isPaused]; } -Array DisplayServerIOS::tts_get_voices() const { - ERR_FAIL_COND_V(!tts, Array()); +TypedArray<Dictionary> DisplayServerIOS::tts_get_voices() const { + ERR_FAIL_COND_V(!tts, TypedArray<Dictionary>()); return [tts getVoices]; } @@ -585,12 +585,44 @@ bool DisplayServerIOS::screen_is_touchscreen(int p_screen) const { return true; } -void DisplayServerIOS::virtual_keyboard_show(const String &p_existing_text, const Rect2 &p_screen_rect, bool p_multiline, int p_max_length, int p_cursor_start, int p_cursor_end) { +void DisplayServerIOS::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()]; + AppDelegate.viewController.keyboardView.keyboardType = UIKeyboardTypeDefault; + AppDelegate.viewController.keyboardView.textContentType = nil; + switch (p_type) { + case KEYBOARD_TYPE_DEFAULT: { + AppDelegate.viewController.keyboardView.keyboardType = UIKeyboardTypeDefault; + } break; + case KEYBOARD_TYPE_MULTILINE: { + AppDelegate.viewController.keyboardView.keyboardType = UIKeyboardTypeDefault; + } break; + case KEYBOARD_TYPE_NUMBER: { + AppDelegate.viewController.keyboardView.keyboardType = UIKeyboardTypeNumberPad; + } break; + case KEYBOARD_TYPE_NUMBER_DECIMAL: { + AppDelegate.viewController.keyboardView.keyboardType = UIKeyboardTypeDecimalPad; + } break; + case KEYBOARD_TYPE_PHONE: { + AppDelegate.viewController.keyboardView.keyboardType = UIKeyboardTypePhonePad; + AppDelegate.viewController.keyboardView.textContentType = UITextContentTypeTelephoneNumber; + } break; + case KEYBOARD_TYPE_EMAIL_ADDRESS: { + AppDelegate.viewController.keyboardView.keyboardType = UIKeyboardTypeEmailAddress; + AppDelegate.viewController.keyboardView.textContentType = UITextContentTypeEmailAddress; + } break; + case KEYBOARD_TYPE_PASSWORD: { + AppDelegate.viewController.keyboardView.keyboardType = UIKeyboardTypeDefault; + AppDelegate.viewController.keyboardView.textContentType = UITextContentTypePassword; + } break; + case KEYBOARD_TYPE_URL: { + AppDelegate.viewController.keyboardView.keyboardType = UIKeyboardTypeWebSearch; + AppDelegate.viewController.keyboardView.textContentType = UITextContentTypeURL; + } break; + } + [AppDelegate.viewController.keyboardView becomeFirstResponderWithString:existingString - multiline:p_multiline cursorStart:p_cursor_start cursorEnd:p_cursor_end]; } diff --git a/platform/ios/export/export.cpp b/platform/ios/export/export.cpp index 1531c2bde5..6024db2f2c 100644 --- a/platform/ios/export/export.cpp +++ b/platform/ios/export/export.cpp @@ -30,6 +30,7 @@ #include "export.h" +#include "editor/export/editor_export.h" #include "export_plugin.h" void register_ios_exporter() { diff --git a/platform/ios/export/export_plugin.cpp b/platform/ios/export/export_plugin.cpp index a2e80d33fd..33f1071077 100644 --- a/platform/ios/export/export_plugin.cpp +++ b/platform/ios/export/export_plugin.cpp @@ -30,10 +30,10 @@ #include "export_plugin.h" +#include "core/string/translation.h" #include "editor/editor_node.h" -void EditorExportPlatformIOS::get_preset_features(const Ref<EditorExportPreset> &p_preset, List<String> *r_features) { - String driver = ProjectSettings::get_singleton()->get("rendering/driver/driver_name"); +void EditorExportPlatformIOS::get_preset_features(const Ref<EditorExportPreset> &p_preset, List<String> *r_features) const { // Vulkan and OpenGL ES 3.0 both mandate ETC2 support. r_features->push_back("etc2"); @@ -43,12 +43,52 @@ void EditorExportPlatformIOS::get_preset_features(const Ref<EditorExportPreset> } } -Vector<EditorExportPlatformIOS::ExportArchitecture> EditorExportPlatformIOS::_get_supported_architectures() { +Vector<EditorExportPlatformIOS::ExportArchitecture> EditorExportPlatformIOS::_get_supported_architectures() const { Vector<ExportArchitecture> archs; archs.push_back(ExportArchitecture("arm64", true)); return archs; } +struct IconInfo { + const char *preset_key; + const char *idiom; + const char *export_name; + const char *actual_size_side; + const char *scale; + const char *unscaled_size; + const bool force_opaque; +}; + +static const IconInfo icon_infos[] = { + // Home screen on iPhone + { PNAME("icons/iphone_120x120"), "iphone", "Icon-120.png", "120", "2x", "60x60", false }, + { PNAME("icons/iphone_120x120"), "iphone", "Icon-120.png", "120", "3x", "40x40", false }, + { PNAME("icons/iphone_180x180"), "iphone", "Icon-180.png", "180", "3x", "60x60", false }, + + // Home screen on iPad + { PNAME("icons/ipad_76x76"), "ipad", "Icon-76.png", "76", "1x", "76x76", false }, + { PNAME("icons/ipad_152x152"), "ipad", "Icon-152.png", "152", "2x", "76x76", false }, + { PNAME("icons/ipad_167x167"), "ipad", "Icon-167.png", "167", "2x", "83.5x83.5", false }, + + // App Store + { PNAME("icons/app_store_1024x1024"), "ios-marketing", "Icon-1024.png", "1024", "1x", "1024x1024", true }, + + // Spotlight + { PNAME("icons/spotlight_40x40"), "ipad", "Icon-40.png", "40", "1x", "40x40", false }, + { PNAME("icons/spotlight_80x80"), "iphone", "Icon-80.png", "80", "2x", "40x40", false }, + { PNAME("icons/spotlight_80x80"), "ipad", "Icon-80.png", "80", "2x", "40x40", false }, + + // Settings + { PNAME("icons/settings_58x58"), "iphone", "Icon-58.png", "58", "2x", "29x29", false }, + { PNAME("icons/settings_58x58"), "ipad", "Icon-58.png", "58", "2x", "29x29", false }, + { PNAME("icons/settings_87x87"), "iphone", "Icon-87.png", "87", "3x", "29x29", false }, + + // Notification + { PNAME("icons/notification_40x40"), "iphone", "Icon-40.png", "40", "2x", "20x20", false }, + { PNAME("icons/notification_40x40"), "ipad", "Icon-40.png", "40", "2x", "20x20", false }, + { PNAME("icons/notification_60x60"), "iphone", "Icon-60.png", "60", "3x", "20x20", false } +}; + struct LoadingScreenInfo { const char *preset_key; const char *export_name; @@ -97,6 +137,9 @@ void EditorExportPlatformIOS::get_export_options(List<ExportOption> *r_options) r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/short_version"), "1.0")); r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/version"), "1.0")); + 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::INT, "application/launch_screens_interpolation", PROPERTY_HINT_ENUM, "Nearest neighbor,Bilinear,Cubic,Trilinear,Lanczos"), 4)); + Vector<PluginConfigIOS> found_plugins = get_plugins(); 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)); @@ -139,27 +182,22 @@ void EditorExportPlatformIOS::get_export_options(List<ExportOption> *r_options) 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())); - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "icons/iphone_120x120", PROPERTY_HINT_FILE, "*.png"), "")); // Home screen on iPhone/iPod Touch with Retina display - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "icons/iphone_180x180", PROPERTY_HINT_FILE, "*.png"), "")); // Home screen on iPhone with Retina HD display - - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "icons/ipad_76x76", PROPERTY_HINT_FILE, "*.png"), "")); // Home screen on iPad - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "icons/ipad_152x152", PROPERTY_HINT_FILE, "*.png"), "")); // Home screen on iPad with Retina display - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "icons/ipad_167x167", PROPERTY_HINT_FILE, "*.png"), "")); // Home screen on iPad Pro - - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "icons/app_store_1024x1024", PROPERTY_HINT_FILE, "*.png"), "")); // App Store - - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "icons/spotlight_40x40", PROPERTY_HINT_FILE, "*.png"), "")); // Spotlight - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "icons/spotlight_80x80", PROPERTY_HINT_FILE, "*.png"), "")); // Spotlight on devices with Retina display - + HashSet<String> used_names; + for (uint64_t i = 0; i < sizeof(icon_infos) / sizeof(icon_infos[0]); ++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, icon_infos[i].preset_key, PROPERTY_HINT_FILE, "*.png,*.jpg,*.jpeg"), "")); + } + } r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "storyboard/use_launch_screen_storyboard"), false)); r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "storyboard/image_scale_mode", PROPERTY_HINT_ENUM, "Same as Logo,Center,Scale to Fit,Scale to Fill,Scale"), 0)); - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "storyboard/custom_image@2x", PROPERTY_HINT_FILE, "*.png"), "")); - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "storyboard/custom_image@3x", PROPERTY_HINT_FILE, "*.png"), "")); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "storyboard/custom_image@2x", PROPERTY_HINT_FILE, "*.png,*.jpg,*.jpeg"), "")); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "storyboard/custom_image@3x", PROPERTY_HINT_FILE, "*.png,*.jpg,*.jpeg"), "")); r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "storyboard/use_custom_bg_color"), false)); r_options->push_back(ExportOption(PropertyInfo(Variant::COLOR, "storyboard/custom_bg_color"), Color())); for (uint64_t i = 0; i < sizeof(loading_screen_infos) / sizeof(loading_screen_infos[0]); ++i) { - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, loading_screen_infos[i].preset_key, PROPERTY_HINT_FILE, "*.png"), "")); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, loading_screen_infos[i].preset_key, PROPERTY_HINT_FILE, "*.png,*.jpg,*.jpeg"), "")); } } @@ -358,8 +396,8 @@ void EditorExportPlatformIOS::_fix_config_file(const Ref<EditorExportPreset> &p_ switch (image_scale_mode) { case 0: { - String logo_path = ProjectSettings::get_singleton()->get("application/boot_splash/image"); - bool is_on = ProjectSettings::get_singleton()->get("application/boot_splash/fullsize"); + String logo_path = GLOBAL_GET("application/boot_splash/image"); + bool is_on = GLOBAL_GET("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; @@ -371,7 +409,7 @@ void EditorExportPlatformIOS::_fix_config_file(const Ref<EditorExportPreset> &p_ strnew += lines[i].replace("$launch_screen_image_mode", value) + "\n"; } else if (lines[i].find("$launch_screen_background_color") != -1) { bool use_custom = p_preset->get("storyboard/use_custom_bg_color"); - Color color = use_custom ? p_preset->get("storyboard/custom_bg_color") : ProjectSettings::get_singleton()->get("application/boot_splash/bg_color"); + Color color = use_custom ? p_preset->get("storyboard/custom_bg_color") : GLOBAL_GET("application/boot_splash/bg_color"); const String value_format = "red=\"$red\" green=\"$green\" blue=\"$blue\" alpha=\"$alpha\""; Dictionary value_dictionary; @@ -384,34 +422,71 @@ void EditorExportPlatformIOS::_fix_config_file(const Ref<EditorExportPreset> &p_ strnew += lines[i].replace("$launch_screen_background_color", value) + "\n"; } else if (lines[i].find("$pbx_locale_file_reference") != -1) { String locale_files; - Vector<String> translations = ProjectSettings::get_singleton()->get("internationalization/locale/translations"); + Vector<String> translations = GLOBAL_GET("internationalization/locale/translations"); if (translations.size() > 0) { - int index = 0; + HashSet<String> languages; for (const String &E : translations) { Ref<Translation> tr = ResourceLoader::load(E); - if (tr.is_valid()) { - String lang = tr->get_locale(); - locale_files += "D0BCFE4518AEBDA2004A" + itos(index).pad_zeros(4) + " /* " + lang + " */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = " + lang + "; path = " + lang + ".lproj/InfoPlist.strings; sourceTree = \"<group>\"; };"; + 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 = \"<group>\"; };\n"; index++; } } strnew += lines[i].replace("$pbx_locale_file_reference", locale_files); } else if (lines[i].find("$pbx_locale_build_reference") != -1) { String locale_files; - Vector<String> translations = ProjectSettings::get_singleton()->get("internationalization/locale/translations"); + Vector<String> translations = GLOBAL_GET("internationalization/locale/translations"); if (translations.size() > 0) { - int index = 0; + HashSet<String> languages; for (const String &E : translations) { Ref<Translation> tr = ResourceLoader::load(E); - if (tr.is_valid()) { - String lang = tr->get_locale(); - locale_files += "D0BCFE4518AEBDA2004A" + itos(index).pad_zeros(4) + " /* " + lang + " */,"; + 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].find("$swift_runtime_migration") != -1) { + String value = !p_config.use_swift_runtime ? "" : "LastSwiftMigration = 1250;"; + strnew += lines[i].replace("$swift_runtime_migration", value) + "\n"; + } else if (lines[i].find("$swift_runtime_build_settings") != -1) { + 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].find("$swift_runtime_fileref") != -1) { + String value = !p_config.use_swift_runtime ? "" : R"( + 90B4C2AA2680BC560039117A /* dummy.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "dummy.h"; sourceTree = "<group>"; }; + 90B4C2B52680C7E90039117A /* dummy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "dummy.swift"; sourceTree = "<group>"; }; + )"; + strnew += lines[i].replace("$swift_runtime_fileref", value) + "\n"; + } else if (lines[i].find("$swift_runtime_binary_files") != -1) { + 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].find("$swift_runtime_buildfile") != -1) { + 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].find("$swift_runtime_build_phase") != -1) { + String value = !p_config.use_swift_runtime ? "" : "90B4C2B62680C7E90039117A /* dummy.swift */,"; + strnew += lines[i].replace("$swift_runtime_build_phase", value) + "\n"; } else { strnew += lines[i] + "\n"; } @@ -494,35 +569,6 @@ void EditorExportPlatformIOS::_blend_and_rotate(Ref<Image> &p_dst, Ref<Image> &p } } -struct IconInfo { - const char *preset_key; - const char *idiom; - const char *export_name; - const char *actual_size_side; - const char *scale; - const char *unscaled_size; -}; - -static const IconInfo icon_infos[] = { - // Home screen on iPhone - { "icons/iphone_120x120", "iphone", "Icon-120.png", "120", "2x", "60x60" }, - { "icons/iphone_120x120", "iphone", "Icon-120.png", "120", "3x", "40x40" }, - { "icons/iphone_180x180", "iphone", "Icon-180.png", "180", "3x", "60x60" }, - - // Home screen on iPad - { "icons/ipad_76x76", "ipad", "Icon-76.png", "76", "1x", "76x76" }, - { "icons/ipad_152x152", "ipad", "Icon-152.png", "152", "2x", "76x76" }, - { "icons/ipad_167x167", "ipad", "Icon-167.png", "167", "2x", "83.5x83.5" }, - - // App Store - { "icons/app_store_1024x1024", "ios-marketing", "Icon-1024.png", "1024", "1x", "1024x1024" }, - - // Spotlight - { "icons/spotlight_40x40", "ipad", "Icon-40.png", "40", "1x", "40x40" }, - { "icons/spotlight_80x80", "iphone", "Icon-80.png", "80", "2x", "40x40" }, - { "icons/spotlight_80x80", "ipad", "Icon-80.png", "80", "2x", "40x40" } -}; - Error EditorExportPlatformIOS::_export_icons(const Ref<EditorExportPreset> &p_preset, const String &p_iconset_dir) { String json_description = "{\"images\":["; String sizes; @@ -530,24 +576,33 @@ Error EditorExportPlatformIOS::_export_icons(const Ref<EditorExportPreset> &p_pr Ref<DirAccess> da = DirAccess::open(p_iconset_dir); ERR_FAIL_COND_V_MSG(da.is_null(), ERR_CANT_OPEN, "Cannot open directory '" + p_iconset_dir + "'."); + Color boot_bg_color = GLOBAL_GET("application/boot_splash/bg_color"); + for (uint64_t i = 0; i < (sizeof(icon_infos) / sizeof(icon_infos[0])); ++i) { IconInfo info = icon_infos[i]; int side_size = String(info.actual_size_side).to_int(); String icon_path = p_preset->get(info.preset_key); if (icon_path.length() == 0) { // Resize main app icon - icon_path = ProjectSettings::get_singleton()->get("application/config/icon"); + icon_path = GLOBAL_GET("application/config/icon"); Ref<Image> img = memnew(Image); Error err = ImageLoader::load_image(icon_path, img); if (err != OK) { - ERR_PRINT("Invalid icon (" + String(info.preset_key) + "): '" + icon_path + "'."); + add_message(EXPORT_MESSAGE_ERROR, TTR("Export Icons"), vformat("Invalid icon (%s): '%s'.", info.preset_key, icon_path)); return ERR_UNCONFIGURED; + } else if (info.force_opaque && img->detect_alpha() != Image::ALPHA_NONE) { + add_message(EXPORT_MESSAGE_WARNING, TTR("Export Icons"), vformat("Icon (%s) must be opaque.", info.preset_key)); + img->resize(side_size, side_size, (Image::Interpolation)(p_preset->get("application/icon_interpolation").operator int())); + Ref<Image> new_img = Image::create_empty(side_size, side_size, false, Image::FORMAT_RGBA8); + new_img->fill(boot_bg_color); + _blend_and_rotate(new_img, img, false); + err = new_img->save_png(p_iconset_dir + info.export_name); + } else { + img->resize(side_size, side_size, (Image::Interpolation)(p_preset->get("application/icon_interpolation").operator int())); + err = img->save_png(p_iconset_dir + info.export_name); } - img->resize(side_size, side_size); - err = img->save_png(p_iconset_dir + info.export_name); if (err) { - String err_str = String("Failed to export icon(" + String(info.preset_key) + "): '" + icon_path + "'."); - ERR_PRINT(err_str.utf8().get_data()); + add_message(EXPORT_MESSAGE_ERROR, TTR("Export Icons"), vformat("Failed to export icon (%s): '%s'.", info.preset_key, icon_path)); return err; } } else { @@ -555,20 +610,25 @@ Error EditorExportPlatformIOS::_export_icons(const Ref<EditorExportPreset> &p_pr Ref<Image> img = memnew(Image); Error err = ImageLoader::load_image(icon_path, img); if (err != OK) { - ERR_PRINT("Invalid icon (" + String(info.preset_key) + "): '" + icon_path + "'."); + add_message(EXPORT_MESSAGE_ERROR, TTR("Export Icons"), vformat("Invalid icon (%s): '%s'.", info.preset_key, icon_path)); return ERR_UNCONFIGURED; - } - if (img->get_width() != side_size || img->get_height() != side_size) { - WARN_PRINT("Icon (" + String(info.preset_key) + "): '" + icon_path + "' has incorrect size (" + String::num_int64(img->get_width()) + "x" + String::num_int64(img->get_height()) + ") and was automatically resized to " + String::num_int64(side_size) + "x" + String::num_int64(side_size) + "."); - img->resize(side_size, side_size); + } else if (info.force_opaque && img->detect_alpha() != Image::ALPHA_NONE) { + add_message(EXPORT_MESSAGE_WARNING, TTR("Export Icons"), vformat("Icon (%s) must be opaque.", info.preset_key)); + img->resize(side_size, side_size, (Image::Interpolation)(p_preset->get("application/icon_interpolation").operator int())); + Ref<Image> new_img = Image::create_empty(side_size, side_size, false, Image::FORMAT_RGBA8); + new_img->fill(boot_bg_color); + _blend_and_rotate(new_img, img, false); + err = new_img->save_png(p_iconset_dir + info.export_name); + } else if (img->get_width() != side_size || img->get_height() != side_size) { + add_message(EXPORT_MESSAGE_WARNING, TTR("Export Icons"), vformat("Icon (%s): '%s' has incorrect size %s and was automatically resized to %s.", info.preset_key, icon_path, img->get_size(), Vector2i(side_size, side_size))); + img->resize(side_size, side_size, (Image::Interpolation)(p_preset->get("application/icon_interpolation").operator int())); err = img->save_png(p_iconset_dir + info.export_name); } else { err = da->copy(icon_path, p_iconset_dir + info.export_name); } if (err) { - String err_str = String("Failed to export icon(" + String(info.preset_key) + "): '" + icon_path + "'."); - ERR_PRINT(err_str.utf8().get_data()); + add_message(EXPORT_MESSAGE_ERROR, TTR("Export Icons"), vformat("Failed to export icon (%s): '%s'.", info.preset_key, icon_path)); return err; } } @@ -604,9 +664,9 @@ Error EditorExportPlatformIOS::_export_loading_screen_file(const Ref<EditorExpor if (custom_launch_image_2x.length() > 0 && custom_launch_image_3x.length() > 0) { Ref<Image> image; - String image_path = p_dest_dir.plus_file("splash@2x.png"); + String image_path = p_dest_dir.path_join("splash@2x.png"); image.instantiate(); - Error err = image->load(custom_launch_image_2x); + Error err = ImageLoader::load_image(custom_launch_image_2x, image); if (err) { image.unref(); @@ -618,9 +678,9 @@ Error EditorExportPlatformIOS::_export_loading_screen_file(const Ref<EditorExpor } image.unref(); - image_path = p_dest_dir.plus_file("splash@3x.png"); + image_path = p_dest_dir.path_join("splash@3x.png"); image.instantiate(); - err = image->load(custom_launch_image_3x); + err = ImageLoader::load_image(custom_launch_image_3x, image); if (err) { image.unref(); @@ -633,11 +693,11 @@ Error EditorExportPlatformIOS::_export_loading_screen_file(const Ref<EditorExpor } else { Ref<Image> splash; - const String splash_path = ProjectSettings::get_singleton()->get("application/boot_splash/image"); + const String splash_path = GLOBAL_GET("application/boot_splash/image"); if (!splash_path.is_empty()) { splash.instantiate(); - const Error err = splash->load(splash_path); + const Error err = ImageLoader::load_image(splash_path, splash); if (err) { splash.unref(); } @@ -651,8 +711,8 @@ Error EditorExportPlatformIOS::_export_loading_screen_file(const Ref<EditorExpor // because Godot's own boot logo uses single image for all resolutions. // Also not using @1x image, because devices using this image variant // are not supported by iOS 9, which is minimal target. - const String splash_png_path_2x = p_dest_dir.plus_file("splash@2x.png"); - const String splash_png_path_3x = p_dest_dir.plus_file("splash@3x.png"); + const String splash_png_path_2x = p_dest_dir.path_join("splash@2x.png"); + const String splash_png_path_3x = p_dest_dir.path_join("splash@3x.png"); if (splash->save_png(splash_png_path_2x) != OK) { return ERR_FILE_CANT_WRITE; @@ -674,9 +734,9 @@ Error EditorExportPlatformIOS::_export_loading_screen_images(const Ref<EditorExp LoadingScreenInfo info = loading_screen_infos[i]; String loading_screen_file = p_preset->get(info.preset_key); - Color boot_bg_color = ProjectSettings::get_singleton()->get("application/boot_splash/bg_color"); - String boot_logo_path = ProjectSettings::get_singleton()->get("application/boot_splash/image"); - bool boot_logo_scale = ProjectSettings::get_singleton()->get("application/boot_splash/fullsize"); + Color boot_bg_color = GLOBAL_GET("application/boot_splash/bg_color"); + String boot_logo_path = GLOBAL_GET("application/boot_splash/image"); + bool boot_logo_scale = GLOBAL_GET("application/boot_splash/fullsize"); if (loading_screen_file.size() > 0) { // Load custom loading screens, and resize if required. @@ -691,13 +751,12 @@ Error EditorExportPlatformIOS::_export_loading_screen_images(const Ref<EditorExp float aspect_ratio = (float)img->get_width() / (float)img->get_height(); if (boot_logo_scale) { if (info.height * aspect_ratio <= info.width) { - img->resize(info.height * aspect_ratio, info.height); + img->resize(info.height * aspect_ratio, info.height, (Image::Interpolation)(p_preset->get("application/launch_screens_interpolation").operator int())); } else { - img->resize(info.width, info.width / aspect_ratio); + img->resize(info.width, info.width / aspect_ratio, (Image::Interpolation)(p_preset->get("application/launch_screens_interpolation").operator int())); } } - Ref<Image> new_img = memnew(Image); - new_img->create(info.width, info.height, false, Image::FORMAT_RGBA8); + Ref<Image> new_img = Image::create_empty(info.width, info.height, false, Image::FORMAT_RGBA8); new_img->fill(boot_bg_color); _blend_and_rotate(new_img, img, false); err = new_img->save_png(p_dest_dir + info.export_name); @@ -711,8 +770,7 @@ Error EditorExportPlatformIOS::_export_loading_screen_images(const Ref<EditorExp } } else { // Generate loading screen from the splash screen - Ref<Image> img = memnew(Image); - img->create(info.width, info.height, false, Image::FORMAT_RGBA8); + Ref<Image> img = Image::create_empty(info.width, info.height, false, Image::FORMAT_RGBA8); img->fill(boot_bg_color); Ref<Image> img_bs; @@ -729,17 +787,17 @@ Error EditorExportPlatformIOS::_export_loading_screen_images(const Ref<EditorExp if (info.rotate) { if (boot_logo_scale) { if (info.width * aspect_ratio <= info.height) { - img_bs->resize(info.width * aspect_ratio, info.width); + img_bs->resize(info.width * aspect_ratio, info.width, (Image::Interpolation)(p_preset->get("application/launch_screens_interpolation").operator int())); } else { - img_bs->resize(info.height, info.height / aspect_ratio); + img_bs->resize(info.height, info.height / aspect_ratio, (Image::Interpolation)(p_preset->get("application/launch_screens_interpolation").operator int())); } } } else { if (boot_logo_scale) { if (info.height * aspect_ratio <= info.width) { - img_bs->resize(info.height * aspect_ratio, info.height); + img_bs->resize(info.height * aspect_ratio, info.height, (Image::Interpolation)(p_preset->get("application/launch_screens_interpolation").operator int())); } else { - img_bs->resize(info.width, info.width / aspect_ratio); + img_bs->resize(info.width, info.width / aspect_ratio, (Image::Interpolation)(p_preset->get("application/launch_screens_interpolation").operator int())); } } } @@ -767,7 +825,7 @@ Error EditorExportPlatformIOS::_walk_dir_recursive(Ref<DirAccess> &p_da, FileHan dirs.push_back(path); } } else { - Error err = p_handler(current_dir.plus_file(path), p_userdata); + Error err = p_handler(current_dir.path_join(path), p_userdata); if (err) { p_da->list_dir_end(); return err; @@ -983,7 +1041,7 @@ Error EditorExportPlatformIOS::_copy_asset(const String &p_out_dir, const String if (p_is_framework && p_asset.ends_with(".dylib")) { // For iOS we need to turn .dylib into .framework // to be able to send application to AppStore - asset_path = String("dylibs").plus_file(base_dir); + asset_path = String("dylibs").path_join(base_dir); String file_name; @@ -995,12 +1053,12 @@ Error EditorExportPlatformIOS::_copy_asset(const String &p_out_dir, const String String framework_name = file_name + ".framework"; - asset_path = asset_path.plus_file(framework_name); - destination_dir = p_out_dir.plus_file(asset_path); - destination = destination_dir.plus_file(file_name); + asset_path = asset_path.path_join(framework_name); + destination_dir = p_out_dir.path_join(asset_path); + destination = destination_dir.path_join(file_name); create_framework = true; } else if (p_is_framework && (p_asset.ends_with(".framework") || p_asset.ends_with(".xcframework"))) { - asset_path = String("dylibs").plus_file(base_dir); + asset_path = String("dylibs").path_join(base_dir); String file_name; @@ -1010,8 +1068,8 @@ Error EditorExportPlatformIOS::_copy_asset(const String &p_out_dir, const String file_name = *p_custom_file_name; } - asset_path = asset_path.plus_file(file_name); - destination_dir = p_out_dir.plus_file(asset_path); + asset_path = asset_path.path_join(file_name); + destination_dir = p_out_dir.path_join(asset_path); destination = destination_dir; } else { asset_path = base_dir; @@ -1024,9 +1082,9 @@ Error EditorExportPlatformIOS::_copy_asset(const String &p_out_dir, const String file_name = *p_custom_file_name; } - destination_dir = p_out_dir.plus_file(asset_path); - asset_path = asset_path.plus_file(file_name); - destination = p_out_dir.plus_file(asset_path); + 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); } Ref<DirAccess> filesystem_da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM); @@ -1043,7 +1101,7 @@ Error EditorExportPlatformIOS::_copy_asset(const String &p_out_dir, const String if (err) { return err; } - IOSExportAsset exported_asset = { binary_name.plus_file(asset_path), p_is_framework, p_should_embed }; + IOSExportAsset exported_asset = { binary_name.path_join(asset_path), p_is_framework, p_should_embed }; r_exported_assets.push_back(exported_asset); if (create_framework) { @@ -1061,7 +1119,7 @@ Error EditorExportPlatformIOS::_copy_asset(const String &p_out_dir, const String { List<String> install_name_args; install_name_args.push_back("-id"); - install_name_args.push_back(String("@rpath").plus_file(framework_name).plus_file(file_name)); + install_name_args.push_back(String("@rpath").path_join(framework_name).path_join(file_name)); install_name_args.push_back(destination); OS::get_singleton()->execute("install_name_tool", install_name_args); @@ -1096,7 +1154,7 @@ Error EditorExportPlatformIOS::_copy_asset(const String &p_out_dir, const String String info_plist = info_plist_format.replace("$name", file_name); - Ref<FileAccess> f = FileAccess::open(destination_dir.plus_file("Info.plist"), FileAccess::WRITE); + Ref<FileAccess> f = FileAccess::open(destination_dir.path_join("Info.plist"), FileAccess::WRITE); if (f.is_valid()) { f->store_string(info_plist); } @@ -1155,7 +1213,7 @@ Error EditorExportPlatformIOS::_export_additional_assets(const String &p_out_dir return OK; } -Vector<String> EditorExportPlatformIOS::_get_preset_architectures(const Ref<EditorExportPreset> &p_preset) { +Vector<String> EditorExportPlatformIOS::_get_preset_architectures(const Ref<EditorExportPreset> &p_preset) const { Vector<ExportArchitecture> all_archs = _get_supported_architectures(); Vector<String> enabled_archs; for (int i = 0; i < all_archs.size(); ++i) { @@ -1297,6 +1355,10 @@ Error EditorExportPlatformIOS::_export_ios_plugins(const Ref<EditorExportPreset> 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` @@ -1448,8 +1510,8 @@ Error EditorExportPlatformIOS::export_project(const Ref<EditorExportPreset> &p_p print_line("Static framework: " + library_to_use); String pkg_name; - if (String(ProjectSettings::get_singleton()->get("application/config/name")) != "") { - pkg_name = String(ProjectSettings::get_singleton()->get("application/config/name")); + if (String(GLOBAL_GET("application/config/name")) != "") { + pkg_name = String(GLOBAL_GET("application/config/name")); } else { pkg_name = "Unnamed"; } @@ -1478,7 +1540,8 @@ Error EditorExportPlatformIOS::export_project(const Ref<EditorExportPreset> &p_p "", "", "", - Vector<String>() + Vector<String>(), + false }; Vector<IOSExportAsset> assets; @@ -1527,8 +1590,6 @@ Error EditorExportPlatformIOS::export_project(const Ref<EditorExportPreset> &p_p //write - file = file.replace_first("ios/", ""); - if (files_to_parse.has(file)) { _fix_config_file(p_preset, data, config_data, p_debug); } else if (file.begins_with("libgodot.ios")) { @@ -1599,12 +1660,12 @@ Error EditorExportPlatformIOS::export_project(const Ref<EditorExportPreset> &p_p return ERR_FILE_NOT_FOUND; } - Dictionary appnames = ProjectSettings::get_singleton()->get("application/config/name_localized"); + Dictionary appnames = GLOBAL_GET("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<String> translations = ProjectSettings::get_singleton()->get("internationalization/locale/translations"); + Vector<String> translations = GLOBAL_GET("internationalization/locale/translations"); if (translations.size() > 0) { { String fname = dest_dir + binary_name + "/en.lproj"; @@ -1612,33 +1673,37 @@ Error EditorExportPlatformIOS::export_project(const Ref<EditorExportPreset> &p_p Ref<FileAccess> f = FileAccess::open(fname + "/InfoPlist.strings", FileAccess::WRITE); f->store_line("/* Localized versions of Info.plist keys */"); f->store_line(""); - f->store_line("CFBundleDisplayName = \"" + ProjectSettings::get_singleton()->get("application/config/name").operator String() + "\";"); + f->store_line("CFBundleDisplayName = \"" + GLOBAL_GET("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<String> languages; for (const String &E : translations) { Ref<Translation> tr = ResourceLoader::load(E); - if (tr.is_valid()) { - String lang = tr->get_locale(); - String fname = dest_dir + binary_name + "/" + lang + ".lproj"; - tmp_app_path->make_dir_recursive(fname); - Ref<FileAccess> 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() + "\";"); - } + if (tr.is_valid() && tr->get_locale() != "en") { + languages.insert(tr->get_locale()); + } + } + + for (const String &lang : languages) { + String fname = dest_dir + binary_name + "/" + lang + ".lproj"; + tmp_app_path->make_dir_recursive(fname); + Ref<FileAccess> 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() + "\";"); } } } @@ -1784,7 +1849,7 @@ Error EditorExportPlatformIOS::export_project(const Ref<EditorExportPreset> &p_p return OK; } -bool EditorExportPlatformIOS::can_export(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates) const { +bool EditorExportPlatformIOS::has_valid_export_configuration(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates) const { String err; bool valid = false; @@ -1809,7 +1874,18 @@ bool EditorExportPlatformIOS::can_export(const Ref<EditorExportPreset> &p_preset valid = dvalid || rvalid; r_missing_templates = !valid; - // Validate the rest of the configuration. + if (!err.is_empty()) { + r_error = err; + } + + return valid; +} + +bool EditorExportPlatformIOS::has_valid_project_configuration(const Ref<EditorExportPreset> &p_preset, String &r_error) const { + String err; + bool valid = true; + + // Validate the project configuration. String team_id = p_preset->get("application/app_store_team_id"); if (team_id.length() == 0) { @@ -1840,10 +1916,14 @@ bool EditorExportPlatformIOS::can_export(const Ref<EditorExportPreset> &p_preset EditorExportPlatformIOS::EditorExportPlatformIOS() { logo = ImageTexture::create_from_image(memnew(Image(_ios_logo))); plugins_changed.set(); +#ifndef ANDROID_ENABLED check_for_changes_thread.start(_check_for_changes_poll_thread, this); +#endif } EditorExportPlatformIOS::~EditorExportPlatformIOS() { +#ifndef ANDROID_ENABLED quit_request.set(); check_for_changes_thread.wait_to_finish(); +#endif } diff --git a/platform/ios/export/export_plugin.h b/platform/ios/export/export_plugin.h index 91b13419f1..639f2416a5 100644 --- a/platform/ios/export/export_plugin.h +++ b/platform/ios/export/export_plugin.h @@ -40,8 +40,8 @@ #include "core/os/os.h" #include "core/templates/safe_refcount.h" #include "core/version.h" -#include "editor/editor_export.h" #include "editor/editor_settings.h" +#include "editor/export/editor_export_platform.h" #include "main/splash.gen.h" #include "platform/ios/logo.gen.h" #include "string.h" @@ -57,8 +57,10 @@ class EditorExportPlatformIOS : public EditorExportPlatform { // Plugins SafeFlag plugins_changed; +#ifndef ANDROID_ENABLED Thread check_for_changes_thread; SafeFlag quit_request; +#endif Mutex plugins_lock; Vector<PluginConfigIOS> plugins; @@ -79,6 +81,7 @@ class EditorExportPlatformIOS : public EditorExportPlatform { String modules_buildphase; String modules_buildgrp; Vector<String> capabilities; + bool use_swift_runtime; }; struct ExportArchitecture { String name; @@ -106,8 +109,8 @@ class EditorExportPlatformIOS : public EditorExportPlatform { Error _export_loading_screen_file(const Ref<EditorExportPreset> &p_preset, const String &p_dest_dir); Error _export_icons(const Ref<EditorExportPreset> &p_preset, const String &p_iconset_dir); - Vector<ExportArchitecture> _get_supported_architectures(); - Vector<String> _get_preset_architectures(const Ref<EditorExportPreset> &p_preset); + Vector<ExportArchitecture> _get_supported_architectures() const; + Vector<String> _get_preset_architectures(const Ref<EditorExportPreset> &p_preset) const; void _add_assets_to_project(const Ref<EditorExportPreset> &p_preset, Vector<uint8_t> &p_project_data, const Vector<IOSExportAsset> &p_additional_assets); Error _export_additional_assets(const String &p_out_dir, const Vector<String> &p_assets, bool p_is_framework, bool p_should_embed, Vector<IOSExportAsset> &r_exported_assets); @@ -138,6 +141,7 @@ class EditorExportPlatformIOS : public EditorExportPlatform { return true; } +#ifndef ANDROID_ENABLED static void _check_for_changes_poll_thread(void *ud) { EditorExportPlatformIOS *ea = static_cast<EditorExportPlatformIOS *>(ud); @@ -171,9 +175,10 @@ class EditorExportPlatformIOS : public EditorExportPlatform { } } } +#endif protected: - virtual void get_preset_features(const Ref<EditorExportPreset> &p_preset, List<String> *r_features) override; + virtual void get_preset_features(const Ref<EditorExportPreset> &p_preset, List<String> *r_features) const override; virtual void get_export_options(List<ExportOption> *r_options) override; public: @@ -197,9 +202,10 @@ public: } virtual Error export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags = 0) override; - virtual bool can_export(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates) const override; + virtual bool has_valid_export_configuration(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates) const override; + virtual bool has_valid_project_configuration(const Ref<EditorExportPreset> &p_preset, String &r_error) const override; - virtual void get_platform_features(List<String> *r_features) override { + virtual void get_platform_features(List<String> *r_features) const override { r_features->push_back("mobile"); r_features->push_back("ios"); } @@ -232,9 +238,9 @@ public: if (da->current_is_dir()) { if (p_check_directories) { - Vector<String> directory_files = list_plugin_config_files(p_path.plus_file(file), false); + Vector<String> directory_files = list_plugin_config_files(p_path.path_join(file), false); for (int i = 0; i < directory_files.size(); ++i) { - dir_files.push_back(file.plus_file(directory_files[i])); + dir_files.push_back(file.path_join(directory_files[i])); } } @@ -254,7 +260,7 @@ public: static Vector<PluginConfigIOS> get_plugins() { Vector<PluginConfigIOS> loaded_plugins; - String plugins_dir = ProjectSettings::get_singleton()->get_resource_path().plus_file("ios/plugins"); + String plugins_dir = ProjectSettings::get_singleton()->get_resource_path().path_join("ios/plugins"); if (DirAccess::exists(plugins_dir)) { Vector<String> plugins_filenames = list_plugin_config_files(plugins_dir, true); @@ -262,7 +268,7 @@ public: if (!plugins_filenames.is_empty()) { Ref<ConfigFile> config_file = memnew(ConfigFile); for (int i = 0; i < plugins_filenames.size(); i++) { - PluginConfigIOS config = PluginConfigIOS::load_plugin_config(config_file, plugins_dir.plus_file(plugins_filenames[i])); + PluginConfigIOS config = PluginConfigIOS::load_plugin_config(config_file, plugins_dir.path_join(plugins_filenames[i])); if (config.valid_config) { loaded_plugins.push_back(config); } else { diff --git a/platform/ios/export/godot_plugin_config.cpp b/platform/ios/export/godot_plugin_config.cpp index 9118b95337..42797d449b 100644 --- a/platform/ios/export/godot_plugin_config.cpp +++ b/platform/ios/export/godot_plugin_config.cpp @@ -46,7 +46,7 @@ String PluginConfigIOS::resolve_local_dependency_path(String plugin_config_dir, } String res_path = ProjectSettings::get_singleton()->globalize_path("res://"); - absolute_path = plugin_config_dir.plus_file(dependency_path); + absolute_path = plugin_config_dir.path_join(dependency_path); return absolute_path.replace(res_path, "res://"); } @@ -64,7 +64,7 @@ String PluginConfigIOS::resolve_system_dependency_path(String dependency_path) { String system_path = "/System/Library/Frameworks"; - return system_path.plus_file(dependency_path); + return system_path.path_join(dependency_path); } Vector<String> PluginConfigIOS::resolve_local_dependencies(String plugin_config_dir, Vector<String> p_paths) { @@ -121,8 +121,8 @@ bool PluginConfigIOS::validate_plugin(PluginConfigIOS &plugin_config) { String file_path = plugin_config.binary.get_base_dir(); String file_name = plugin_config.binary.get_basename().get_file(); String file_extension = plugin_config.binary.get_extension(); - String release_file_name = file_path.plus_file(file_name + ".release." + file_extension); - String debug_file_name = file_path.plus_file(file_name + ".debug." + file_extension); + String release_file_name = file_path.path_join(file_name + ".release." + file_extension); + String debug_file_name = file_path.path_join(file_name + ".debug." + file_extension); if ((plugin_extension == "a" && FileAccess::exists(release_file_name) && FileAccess::exists(debug_file_name)) || (plugin_extension == "xcframework" && DirAccess::exists(release_file_name) && DirAccess::exists(debug_file_name))) { @@ -144,7 +144,7 @@ String PluginConfigIOS::get_plugin_main_binary(PluginConfigIOS &plugin_config, b String plugin_extension = plugin_config.binary.get_extension(); String plugin_file = plugin_name_prefix + "." + (p_debug ? "debug" : "release") + "." + plugin_extension; - return plugin_binary_dir.plus_file(plugin_file); + return plugin_binary_dir.path_join(plugin_file); } uint64_t PluginConfigIOS::get_plugin_modification_time(const PluginConfigIOS &plugin_config, const String &config_path) { @@ -156,8 +156,8 @@ uint64_t PluginConfigIOS::get_plugin_modification_time(const PluginConfigIOS &pl String file_path = plugin_config.binary.get_base_dir(); String file_name = plugin_config.binary.get_basename().get_file(); String plugin_extension = plugin_config.binary.get_extension(); - String release_file_name = file_path.plus_file(file_name + ".release." + plugin_extension); - String debug_file_name = file_path.plus_file(file_name + ".debug." + plugin_extension); + String release_file_name = file_path.path_join(file_name + ".release." + plugin_extension); + String debug_file_name = file_path.path_join(file_name + ".debug." + plugin_extension); last_updated = MAX(last_updated, FileAccess::get_modified_time(release_file_name)); last_updated = MAX(last_updated, FileAccess::get_modified_time(debug_file_name)); @@ -184,6 +184,7 @@ PluginConfigIOS PluginConfigIOS::load_plugin_config(Ref<ConfigFile> config_file, String config_base_dir = path.get_base_dir(); plugin_config.name = config_file->get_value(PluginConfigIOS::CONFIG_SECTION, PluginConfigIOS::CONFIG_NAME_KEY, String()); + plugin_config.use_swift_runtime = config_file->get_value(PluginConfigIOS::CONFIG_SECTION, PluginConfigIOS::CONFIG_USE_SWIFT_KEY, false); plugin_config.initialization_method = config_file->get_value(PluginConfigIOS::CONFIG_SECTION, PluginConfigIOS::CONFIG_INITIALIZE_KEY, String()); plugin_config.deinitialization_method = config_file->get_value(PluginConfigIOS::CONFIG_SECTION, PluginConfigIOS::CONFIG_DEINITIALIZE_KEY, String()); diff --git a/platform/ios/export/godot_plugin_config.h b/platform/ios/export/godot_plugin_config.h index 5ca8b05b42..98e8456ed5 100644 --- a/platform/ios/export/godot_plugin_config.h +++ b/platform/ios/export/godot_plugin_config.h @@ -39,6 +39,7 @@ The `config` section and fields are required and defined as follow: - **name**: name of the plugin - **binary**: path to static `.a` library +- **use_swift_runtime**: optional boolean field used to determine if Swift runtime is used The `dependencies` and fields are optional. - **linked**: dependencies that should only be linked. @@ -57,6 +58,7 @@ struct PluginConfigIOS { inline static const char *CONFIG_SECTION = "config"; inline static const char *CONFIG_NAME_KEY = "name"; inline static const char *CONFIG_BINARY_KEY = "binary"; + inline static const char *CONFIG_USE_SWIFT_KEY = "use_swift_runtime"; inline static const char *CONFIG_INITIALIZE_KEY = "initialization"; inline static const char *CONFIG_DEINITIALIZE_KEY = "deinitialization"; @@ -93,6 +95,7 @@ struct PluginConfigIOS { // Required config section String name; String binary; + bool use_swift_runtime; String initialization_method; String deinitialization_method; diff --git a/platform/ios/godot_ios.mm b/platform/ios/godot_ios.mm index 5f3e786b8a..abe7c59ce2 100644 --- a/platform/ios/godot_ios.mm +++ b/platform/ios/godot_ios.mm @@ -38,10 +38,6 @@ static OS_IOS *os = nullptr; -int add_path(int, char **); -int add_cmdline(int, char **); -int ios_main(int, char **, String); - int add_path(int p_argc, char **p_args) { NSString *str = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"godot_path"]; if (!str) { @@ -74,7 +70,7 @@ int add_cmdline(int p_argc, char **p_args) { return p_argc; } -int ios_main(int argc, char **argv, String data_dir, String cache_dir) { +int ios_main(int argc, char **argv) { size_t len = strlen(argv[0]); while (len--) { @@ -95,7 +91,7 @@ int ios_main(int argc, char **argv, String data_dir, String cache_dir) { char cwd[512]; getcwd(cwd, sizeof(cwd)); printf("cwd %s\n", cwd); - os = new OS_IOS(data_dir, cache_dir); + os = new OS_IOS(); // We must override main when testing is enabled TEST_MAIN_OVERRIDE diff --git a/platform/ios/godot_view.mm b/platform/ios/godot_view.mm index 9ed219508c..4537dc2985 100644 --- a/platform/ios/godot_view.mm +++ b/platform/ios/godot_view.mm @@ -30,6 +30,7 @@ #import "godot_view.h" +#include "core/config/project_settings.h" #include "core/os/keyboard.h" #include "core/string/ustring.h" #import "display_layer.h" @@ -74,7 +75,7 @@ static const float earth_gravity = 9.80665; if ([driverName isEqualToString:@"vulkan"]) { layer = [GodotMetalLayer layer]; - } else if ([driverName isEqualToString:@"opengl_es"]) { + } else if ([driverName isEqualToString:@"opengl3"]) { if (@available(iOS 13, *)) { NSLog(@"OpenGL ES is deprecated on iOS 13"); } @@ -205,16 +206,16 @@ static const float earth_gravity = 9.80665; if (self.useCADisplayLink) { self.displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(drawView)]; - // Approximate frame rate - // assumes device refreshes at 60 fps - int displayFPS = (NSInteger)(1.0 / self.renderingInterval); - - self.displayLink.preferredFramesPerSecond = displayFPS; + if (GLOBAL_GET("display/window/ios/allow_high_refresh_rate")) { + self.displayLink.preferredFramesPerSecond = 120; + } else { + self.displayLink.preferredFramesPerSecond = 60; + } // Setup DisplayLink in main thread [self.displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes]; } else { - self.animationTimer = [NSTimer scheduledTimerWithTimeInterval:self.renderingInterval target:self selector:@selector(drawView) userInfo:nil repeats:YES]; + self.animationTimer = [NSTimer scheduledTimerWithTimeInterval:(1.0 / 60) target:self selector:@selector(drawView) userInfo:nil repeats:YES]; } } diff --git a/platform/ios/keyboard_input_view.h b/platform/ios/keyboard_input_view.h index 33fa5d571a..fc34e6a11b 100644 --- a/platform/ios/keyboard_input_view.h +++ b/platform/ios/keyboard_input_view.h @@ -32,6 +32,6 @@ @interface GodotKeyboardInputView : UITextView -- (BOOL)becomeFirstResponderWithString:(NSString *)existingString multiline:(BOOL)flag cursorStart:(NSInteger)start cursorEnd:(NSInteger)end; +- (BOOL)becomeFirstResponderWithString:(NSString *)existingString cursorStart:(NSInteger)start cursorEnd:(NSInteger)end; @end diff --git a/platform/ios/keyboard_input_view.mm b/platform/ios/keyboard_input_view.mm index 76e3f23c9d..f031a1de22 100644 --- a/platform/ios/keyboard_input_view.mm +++ b/platform/ios/keyboard_input_view.mm @@ -84,7 +84,7 @@ return YES; } -- (BOOL)becomeFirstResponderWithString:(NSString *)existingString multiline:(BOOL)flag cursorStart:(NSInteger)start cursorEnd:(NSInteger)end { +- (BOOL)becomeFirstResponderWithString:(NSString *)existingString cursorStart:(NSInteger)start cursorEnd:(NSInteger)end { self.text = existingString; self.previousText = existingString; diff --git a/platform/ios/os_ios.h b/platform/ios/os_ios.h index cfd1771653..3560de7486 100644 --- a/platform/ios/os_ios.h +++ b/platform/ios/os_ios.h @@ -28,11 +28,11 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifdef IOS_ENABLED - #ifndef OS_IOS_H #define OS_IOS_H +#ifdef IOS_ENABLED + #include "drivers/coreaudio/audio_driver_coreaudio.h" #include "drivers/unix/os_unix.h" #include "ios.h" @@ -71,9 +71,6 @@ private: virtual void finalize() override; - String user_data_dir; - String cache_dir; - bool is_focused = false; void deinitialize_modules(); @@ -81,7 +78,7 @@ private: public: static OS_IOS *get_singleton(); - OS_IOS(String p_data_dir, String p_cache_dir); + OS_IOS(); ~OS_IOS(); void initialize_modules(); @@ -92,16 +89,20 @@ public: virtual void alert(const String &p_alert, const String &p_title = "ALERT!") override; + virtual Vector<String> get_system_fonts() const override; + virtual String get_system_font_path(const String &p_font_name, bool p_bold = false, bool p_italic = false) const override; + virtual Error open_dynamic_library(const String p_path, void *&p_library_handle, bool p_also_set_library_path = false, String *r_resolved_path = 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_name() const 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(String p_uri) override; - void set_user_data_dir(String p_dir); virtual String get_user_data_dir() const override; virtual String get_cache_path() const override; @@ -119,6 +120,6 @@ public: void on_focus_in(); }; -#endif // OS_IOS_H +#endif // IOS_ENABLED #endif // OS_IOS_H diff --git a/platform/ios/os_ios.mm b/platform/ios/os_ios.mm index 880315209e..9a8cfc2593 100644 --- a/platform/ios/os_ios.mm +++ b/platform/ios/os_ios.mm @@ -44,6 +44,7 @@ #import "view_controller.h" #import <AudioToolbox/AudioServices.h> +#import <CoreText/CoreText.h> #import <UIKit/UIKit.h> #import <dlfcn.h> #include <sys/sysctl.h> @@ -89,7 +90,7 @@ OS_IOS *OS_IOS::get_singleton() { return (OS_IOS *)OS::get_singleton(); } -OS_IOS::OS_IOS(String p_data_dir, String p_cache_dir) { +OS_IOS::OS_IOS() { for (int i = 0; i < ios_init_callbacks_count; ++i) { ios_init_callbacks[i](); } @@ -100,11 +101,6 @@ OS_IOS::OS_IOS(String p_data_dir, String p_cache_dir) { main_loop = nullptr; - // can't call set_data_dir from here, since it requires DirAccess - // which is initialized in initialize_core - user_data_dir = p_data_dir; - cache_dir = p_cache_dir; - Vector<Logger *> loggers; loggers.push_back(memnew(SyslogLogger)); #ifdef DEBUG_ENABLED @@ -129,8 +125,6 @@ void OS_IOS::alert(const String &p_alert, const String &p_title) { void OS_IOS::initialize_core() { OS_Unix::initialize_core(); - - set_user_data_dir(user_data_dir); } void OS_IOS::initialize() { @@ -239,6 +233,15 @@ String OS_IOS::get_name() const { return "iOS"; } +String OS_IOS::get_distribution_name() const { + return get_name(); +} + +String OS_IOS::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_IOS::get_model_name() const { String model = ios->get_model(); if (model != "") { @@ -263,18 +266,26 @@ Error OS_IOS::shell_open(String p_uri) { return OK; } -void OS_IOS::set_user_data_dir(String p_dir) { - Ref<DirAccess> da = DirAccess::open(p_dir); - user_data_dir = da->get_current_dir(); - printf("setting data dir to %s from %s\n", user_data_dir.utf8().get_data(), p_dir.utf8().get_data()); -} - String OS_IOS::get_user_data_dir() const { - return user_data_dir; + static String ret; + if (ret.is_empty()) { + NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); + if (paths && [paths count] >= 1) { + ret.parse_utf8([[paths firstObject] UTF8String]); + } + } + return ret; } String OS_IOS::get_cache_path() const { - return cache_dir; + static String ret; + if (ret.is_empty()) { + NSArray *paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES); + if (paths && [paths count] >= 1) { + ret.parse_utf8([[paths firstObject] UTF8String]); + } + } + return ret; } String OS_IOS::get_locale() const { @@ -302,6 +313,80 @@ String OS_IOS::get_processor_name() const { ERR_FAIL_V_MSG("", String("Couldn't get the CPU model name. Returning an empty string.")); } +Vector<String> OS_IOS::get_system_fonts() const { + HashSet<String> 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<String> ret; + for (const String &E : font_names) { + ret.push_back(E); + } + return ret; +} + +String OS_IOS::get_system_font_path(const String &p_font_name, bool p_bold, bool p_italic) const { + String ret; + + 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"; + }; + + CFStringRef name = CFStringCreateWithCString(kCFAllocatorDefault, font_name.utf8().get_data(), kCFStringEncodingUTF8); + + CTFontSymbolicTraits traits = 0; + if (p_bold) { + traits |= kCTFontBoldTrait; + } + if (p_italic) { + traits |= kCTFontItalicTrait; + } + + CFNumberRef sym_traits = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &traits); + CFMutableDictionaryRef traits_dict = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, nullptr, nullptr); + CFDictionaryAddValue(traits_dict, kCTFontSymbolicTrait, sym_traits); + + 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(name); + + return ret; +} + void OS_IOS::vibrate_handheld(int p_duration_ms) { if (ios->supports_haptic_engine()) { ios->vibrate_haptic_engine((float)p_duration_ms / 1000.f); @@ -312,7 +397,14 @@ void OS_IOS::vibrate_handheld(int p_duration_ms) { } bool OS_IOS::_check_internal_feature_support(const String &p_feature) { - return p_feature == "mobile"; + if (p_feature == "system_fonts") { + return true; + } + if (p_feature == "mobile") { + return true; + } + + return false; } void OS_IOS::on_focus_out() { diff --git a/platform/ios/platform_config.h b/platform/ios/platform_config.h index fed77d8932..3af08b0d65 100644 --- a/platform/ios/platform_config.h +++ b/platform/ios/platform_config.h @@ -32,8 +32,6 @@ #define OPENGL_INCLUDE_H <ES3/gl.h> -#define PLATFORM_REFCOUNT - #define PTHREAD_RENAME_SELF #define _weakify(var) __weak typeof(var) GDWeak_##var = var; diff --git a/platform/ios/tts_ios.mm b/platform/ios/tts_ios.mm index a079d02add..8319cad117 100644 --- a/platform/ios/tts_ios.mm +++ b/platform/ios/tts_ios.mm @@ -78,12 +78,12 @@ AVSpeechUtterance *new_utterance = [[AVSpeechUtterance alloc] initWithString:[NSString stringWithUTF8String:message.text.utf8().get_data()]]; [new_utterance setVoice:[AVSpeechSynthesisVoice voiceWithIdentifier:[NSString stringWithUTF8String:message.voice.utf8().get_data()]]]; if (message.rate > 1.f) { - [new_utterance setRate:Math::range_lerp(message.rate, 1.f, 10.f, AVSpeechUtteranceDefaultSpeechRate, AVSpeechUtteranceMaximumSpeechRate)]; + [new_utterance setRate:Math::remap(message.rate, 1.f, 10.f, AVSpeechUtteranceDefaultSpeechRate, AVSpeechUtteranceMaximumSpeechRate)]; } else if (message.rate < 1.f) { - [new_utterance setRate:Math::range_lerp(message.rate, 0.1f, 1.f, AVSpeechUtteranceMinimumSpeechRate, AVSpeechUtteranceDefaultSpeechRate)]; + [new_utterance setRate:Math::remap(message.rate, 0.1f, 1.f, AVSpeechUtteranceMinimumSpeechRate, AVSpeechUtteranceDefaultSpeechRate)]; } [new_utterance setPitchMultiplier:message.pitch]; - [new_utterance setVolume:(Math::range_lerp(message.volume, 0.f, 100.f, 0.f, 1.f))]; + [new_utterance setVolume:(Math::remap(message.volume, 0.f, 100.f, 0.f, 1.f))]; ids[new_utterance] = message.id; [av_synth speakUtterance:new_utterance]; diff --git a/platform/ios/view_controller.mm b/platform/ios/view_controller.mm index 43669d3f94..53c5d3d0b4 100644 --- a/platform/ios/view_controller.mm +++ b/platform/ios/view_controller.mm @@ -164,7 +164,11 @@ // MARK: Orientation - (UIRectEdge)preferredScreenEdgesDeferringSystemGestures { - return UIRectEdgeAll; + if (GLOBAL_GET("display/window/ios/suppress_ui_gesture")) { + return UIRectEdgeAll; + } else { + return UIRectEdgeNone; + } } - (BOOL)shouldAutorotate { @@ -206,7 +210,11 @@ } - (BOOL)prefersStatusBarHidden { - return YES; + if (GLOBAL_GET("display/window/ios/hide_status_bar")) { + return YES; + } else { + return NO; + } } - (BOOL)prefersHomeIndicatorAutoHidden { diff --git a/platform/ios/vulkan_context_ios.h b/platform/ios/vulkan_context_ios.h index e9c09e087a..3849c8ba8a 100644 --- a/platform/ios/vulkan_context_ios.h +++ b/platform/ios/vulkan_context_ios.h @@ -31,6 +31,8 @@ #ifndef VULKAN_CONTEXT_IOS_H #define VULKAN_CONTEXT_IOS_H +#ifdef VULKAN_ENABLED + #include "drivers/vulkan/vulkan_context.h" #import <UIKit/UIKit.h> @@ -45,4 +47,6 @@ public: ~VulkanContextIOS(); }; +#endif // VULKAN_ENABLED + #endif // VULKAN_CONTEXT_IOS_H diff --git a/platform/ios/vulkan_context_ios.mm b/platform/ios/vulkan_context_ios.mm index 09cd369aa5..81b021e758 100644 --- a/platform/ios/vulkan_context_ios.mm +++ b/platform/ios/vulkan_context_ios.mm @@ -28,6 +28,8 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ +#ifdef VULKAN_ENABLED + #include "vulkan_context_ios.h" #ifdef USE_VOLK #include <volk.h> @@ -57,3 +59,5 @@ Error VulkanContextIOS::window_create(DisplayServer::WindowID p_window_id, Displ VulkanContextIOS::VulkanContextIOS() {} VulkanContextIOS::~VulkanContextIOS() {} + +#endif // VULKAN_ENABLED |