summaryrefslogtreecommitdiffstats
path: root/platform
diff options
context:
space:
mode:
Diffstat (limited to 'platform')
-rw-r--r--platform/android/detect.py2
-rw-r--r--platform/android/export/export.cpp1
-rw-r--r--platform/android/export/export_plugin.cpp67
-rw-r--r--platform/android/export/export_plugin.h1
-rw-r--r--platform/android/file_access_android.cpp81
-rw-r--r--platform/android/file_access_android.h6
-rw-r--r--platform/android/file_access_filesystem_jandroid.cpp51
-rw-r--r--platform/android/file_access_filesystem_jandroid.h6
-rw-r--r--platform/android/java/editor/src/main/java/org/godotengine/editor/GodotEditor.kt4
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/Godot.kt16
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/GodotActivity.kt8
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/GodotFragment.java26
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/GodotGLRenderView.java44
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/GodotIO.java8
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/GodotRenderView.java5
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/GodotVulkanRenderView.java44
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/gl/GLSurfaceView.java16
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/utils/PermissionsUtil.java14
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/vulkan/VkSurfaceView.kt4
-rw-r--r--platform/android/java_godot_lib_jni.cpp2
-rw-r--r--platform/android/java_godot_wrapper.cpp2
-rw-r--r--platform/android/java_godot_wrapper.h2
-rw-r--r--platform/ios/SCsub1
-rw-r--r--platform/ios/detect.py2
-rw-r--r--platform/ios/doc_classes/EditorExportPlatformIOS.xml55
-rw-r--r--platform/ios/export/export_plugin.cpp582
-rw-r--r--platform/ios/export/export_plugin.h12
-rw-r--r--platform/ios/ios_terminal_logger.h45
-rw-r--r--platform/ios/ios_terminal_logger.mm74
-rw-r--r--platform/ios/os_ios.mm10
-rw-r--r--platform/linuxbsd/detect.py2
-rw-r--r--platform/linuxbsd/export/export_plugin.cpp2
-rw-r--r--platform/linuxbsd/freedesktop_portal_desktop.cpp2
-rw-r--r--platform/linuxbsd/os_linuxbsd.cpp2
-rw-r--r--platform/linuxbsd/x11/display_server_x11.cpp7
-rw-r--r--platform/linuxbsd/x11/display_server_x11.h2
-rw-r--r--platform/linuxbsd/x11/key_mapping_x11.cpp2
-rw-r--r--platform/macos/detect.py37
-rw-r--r--platform/macos/display_server_macos.h2
-rw-r--r--platform/macos/display_server_macos.mm4
-rw-r--r--platform/macos/doc_classes/EditorExportPlatformMacOS.xml7
-rw-r--r--platform/macos/export/codesign.cpp2
-rw-r--r--platform/macos/export/codesign.h3
-rw-r--r--platform/macos/export/export_plugin.cpp35
-rw-r--r--platform/macos/export/plist.cpp848
-rw-r--r--platform/macos/export/plist.h128
-rw-r--r--platform/macos/os_macos.mm2
-rw-r--r--platform/web/.eslintrc.html.js2
-rw-r--r--platform/web/SCsub4
-rw-r--r--platform/web/audio_driver_web.cpp52
-rw-r--r--platform/web/audio_driver_web.h51
-rw-r--r--platform/web/detect.py35
-rw-r--r--platform/web/doc_classes/EditorExportPlatformWeb.xml4
-rw-r--r--platform/web/emscripten_helpers.py41
-rw-r--r--platform/web/export/export_plugin.cpp30
-rw-r--r--platform/web/export/export_plugin.h7
-rw-r--r--platform/web/js/engine/features.js22
-rw-r--r--platform/web/js/libs/audio.worklet.js4
-rw-r--r--platform/windows/SCsub16
-rw-r--r--platform/windows/detect.py12
-rw-r--r--platform/windows/display_server_windows.cpp48
-rw-r--r--platform/windows/display_server_windows.h2
-rw-r--r--platform/windows/export/export_plugin.cpp2
-rw-r--r--platform/windows/godot.natvis141
-rw-r--r--platform/windows/os_windows.cpp6
-rw-r--r--platform/windows/vulkan_context_win.h2
66 files changed, 1277 insertions, 1482 deletions
diff --git a/platform/android/detect.py b/platform/android/detect.py
index a417ef454b..b396e5eb2d 100644
--- a/platform/android/detect.py
+++ b/platform/android/detect.py
@@ -200,7 +200,7 @@ def configure(env: "Environment"):
env.Append(LIBS=["OpenSLES", "EGL", "android", "log", "z", "dl"])
if env["vulkan"]:
- env.Append(CPPDEFINES=["VULKAN_ENABLED"])
+ env.Append(CPPDEFINES=["VULKAN_ENABLED", "RD_ENABLED"])
if not env["use_volk"]:
env.Append(LIBS=["vulkan"])
diff --git a/platform/android/export/export.cpp b/platform/android/export/export.cpp
index ee1ec2790d..e276048d40 100644
--- a/platform/android/export/export.cpp
+++ b/platform/android/export/export.cpp
@@ -48,6 +48,7 @@ void register_android_exporter() {
EditorSettings::get_singleton()->add_property_hint(PropertyInfo(Variant::STRING, "export/android/debug_keystore", PROPERTY_HINT_GLOBAL_FILE, "*.keystore,*.jks"));
EDITOR_DEF("export/android/debug_keystore_user", "androiddebugkey");
EDITOR_DEF("export/android/debug_keystore_pass", "android");
+ EditorSettings::get_singleton()->add_property_hint(PropertyInfo(Variant::STRING, "export/android/debug_keystore_pass", PROPERTY_HINT_PASSWORD));
EDITOR_DEF("export/android/force_system_user", false);
EDITOR_DEF("export/android/shutdown_adb_on_exit", true);
diff --git a/platform/android/export/export_plugin.cpp b/platform/android/export/export_plugin.cpp
index 10d54e8d97..653e7bfe6f 100644
--- a/platform/android/export/export_plugin.cpp
+++ b/platform/android/export/export_plugin.cpp
@@ -45,9 +45,9 @@
#include "editor/editor_log.h"
#include "editor/editor_node.h"
#include "editor/editor_paths.h"
-#include "editor/editor_scale.h"
#include "editor/editor_settings.h"
#include "editor/import/resource_importer_texture_settings.h"
+#include "editor/themes/editor_scale.h"
#include "main/splash.gen.h"
#include "scene/resources/image_texture.h"
@@ -1111,7 +1111,7 @@ void EditorExportPlatformAndroid::_fix_manifest(const Ref<EditorExportPreset> &p
}
for (int i = 0; i < feature_names.size(); i++) {
- String feature_name = feature_names[i];
+ const String &feature_name = feature_names[i];
bool feature_required = feature_required_list[i];
int feature_version = feature_versions[i];
bool has_version_attribute = feature_version != -1;
@@ -1605,7 +1605,11 @@ void EditorExportPlatformAndroid::load_icon_refs(const Ref<EditorExportPreset> &
print_verbose("Loading regular icon from " + path);
if (path.is_empty() || ImageLoader::load_image(path, icon) != OK) {
print_verbose("- falling back to project icon: " + project_icon_path);
- ImageLoader::load_image(project_icon_path, icon);
+ if (!project_icon_path.is_empty()) {
+ ImageLoader::load_image(project_icon_path, icon);
+ } else {
+ ERR_PRINT("No project icon specified. Please specify one in the Project Settings under Application -> Config -> Icon");
+ }
}
// Adaptive foreground: user selection -> regular icon (user selection -> project icon -> default).
@@ -1823,10 +1827,10 @@ void EditorExportPlatformAndroid::get_export_options(List<ExportOption> *r_optio
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "keystore/debug", PROPERTY_HINT_GLOBAL_FILE, "*.keystore,*.jks", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SECRET), ""));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "keystore/debug_user", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SECRET), ""));
- r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "keystore/debug_password", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SECRET), ""));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "keystore/debug_password", PROPERTY_HINT_PASSWORD, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SECRET), ""));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "keystore/release", PROPERTY_HINT_GLOBAL_FILE, "*.keystore,*.jks", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SECRET), ""));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "keystore/release_user", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SECRET), ""));
- r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "keystore/release_password", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SECRET), ""));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "keystore/release_password", PROPERTY_HINT_PASSWORD, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SECRET), ""));
r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "version/code", PROPERTY_HINT_RANGE, "1,4096,1,or_greater"), 1));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "version/name", PROPERTY_HINT_PLACEHOLDER_TEXT, "Leave empty to use project version"), ""));
@@ -2229,6 +2233,54 @@ String EditorExportPlatformAndroid::get_apksigner_path(int p_target_sdk, bool p_
return apksigner_path;
}
+static bool has_valid_keystore_credentials(String &r_error_str, const String &p_keystore, const String &p_username, const String &p_password, const String &p_type) {
+ String output;
+ List<String> args;
+ args.push_back("-list");
+ args.push_back("-keystore");
+ args.push_back(p_keystore);
+ args.push_back("-storepass");
+ args.push_back(p_password);
+ args.push_back("-alias");
+ args.push_back(p_username);
+ Error error = OS::get_singleton()->execute("keytool", args, &output, nullptr, true);
+ String keytool_error = "keytool error:";
+ bool valid = output.substr(0, keytool_error.length()) != keytool_error;
+
+ if (error != OK) {
+ r_error_str = TTR("Error: There was a problem validating the keystore username and password");
+ return false;
+ }
+ if (!valid) {
+ r_error_str = TTR(p_type + " Username and/or Password is invalid for the given " + p_type + " Keystore");
+ return false;
+ }
+ r_error_str = "";
+ return true;
+}
+
+bool EditorExportPlatformAndroid::has_valid_username_and_password(const Ref<EditorExportPreset> &p_preset, String &r_error) {
+ String dk = p_preset->get_or_env("keystore/debug", ENV_ANDROID_KEYSTORE_DEBUG_PATH);
+ String dk_user = p_preset->get_or_env("keystore/debug_user", ENV_ANDROID_KEYSTORE_DEBUG_USER);
+ String dk_password = p_preset->get_or_env("keystore/debug_password", ENV_ANDROID_KEYSTORE_DEBUG_PASS);
+ String rk = p_preset->get_or_env("keystore/release", ENV_ANDROID_KEYSTORE_RELEASE_PATH);
+ String rk_user = p_preset->get_or_env("keystore/release_user", ENV_ANDROID_KEYSTORE_RELEASE_USER);
+ String rk_password = p_preset->get_or_env("keystore/release_password", ENV_ANDROID_KEYSTORE_RELEASE_PASS);
+
+ bool valid = true;
+ if (!dk.is_empty() && !dk_user.is_empty() && !dk_password.is_empty()) {
+ String err = "";
+ valid = has_valid_keystore_credentials(err, dk, dk_user, dk_password, "Debug");
+ r_error += err;
+ }
+ if (!rk.is_empty() && !rk_user.is_empty() && !rk_password.is_empty()) {
+ String err = "";
+ valid = has_valid_keystore_credentials(err, rk, rk_user, rk_password, "Release");
+ r_error += err;
+ }
+ return valid;
+}
+
bool EditorExportPlatformAndroid::has_valid_export_configuration(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates, bool p_debug) const {
String err;
bool valid = false;
@@ -2842,6 +2894,11 @@ Error EditorExportPlatformAndroid::export_project_helper(const Ref<EditorExportP
add_message(EXPORT_MESSAGE_ERROR, TTR("Export"), TTR("Unsupported export format!"));
return ERR_UNCONFIGURED;
}
+ String err_string;
+ if (!has_valid_username_and_password(p_preset, err_string)) {
+ add_message(EXPORT_MESSAGE_ERROR, TTR("Export"), TTR(err_string));
+ return ERR_UNCONFIGURED;
+ }
if (use_gradle_build) {
print_verbose("Starting gradle build...");
diff --git a/platform/android/export/export_plugin.h b/platform/android/export/export_plugin.h
index a2d0417c5d..5b585581b0 100644
--- a/platform/android/export/export_plugin.h
+++ b/platform/android/export/export_plugin.h
@@ -228,6 +228,7 @@ public:
virtual bool has_valid_export_configuration(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates, bool p_debug = false) const override;
virtual bool has_valid_project_configuration(const Ref<EditorExportPreset> &p_preset, String &r_error) const override;
+ static bool has_valid_username_and_password(const Ref<EditorExportPreset> &p_preset, String &r_error);
virtual List<String> get_binary_extensions(const Ref<EditorExportPreset> &p_preset) const override;
diff --git a/platform/android/file_access_android.cpp b/platform/android/file_access_android.cpp
index 1249f2219f..f56eda4694 100644
--- a/platform/android/file_access_android.cpp
+++ b/platform/android/file_access_android.cpp
@@ -121,6 +121,75 @@ uint8_t FileAccessAndroid::get_8() const {
return byte;
}
+uint16_t FileAccessAndroid::get_16() const {
+ if (pos >= len) {
+ eof = true;
+ return 0;
+ }
+
+ uint16_t bytes = 0;
+ int r = AAsset_read(asset, &bytes, 2);
+
+ if (r >= 0) {
+ pos += r;
+ if (pos >= len) {
+ eof = true;
+ }
+ }
+
+ if (big_endian) {
+ bytes = BSWAP16(bytes);
+ }
+
+ return bytes;
+}
+
+uint32_t FileAccessAndroid::get_32() const {
+ if (pos >= len) {
+ eof = true;
+ return 0;
+ }
+
+ uint32_t bytes = 0;
+ int r = AAsset_read(asset, &bytes, 4);
+
+ if (r >= 0) {
+ pos += r;
+ if (pos >= len) {
+ eof = true;
+ }
+ }
+
+ if (big_endian) {
+ bytes = BSWAP32(bytes);
+ }
+
+ return bytes;
+}
+
+uint64_t FileAccessAndroid::get_64() const {
+ if (pos >= len) {
+ eof = true;
+ return 0;
+ }
+
+ uint64_t bytes = 0;
+ int r = AAsset_read(asset, &bytes, 8);
+
+ if (r >= 0) {
+ pos += r;
+ if (pos >= len) {
+ eof = true;
+ }
+ }
+
+ if (big_endian) {
+ bytes = BSWAP64(bytes);
+ }
+
+ return bytes;
+}
+
uint64_t FileAccessAndroid::get_buffer(uint8_t *p_dst, uint64_t p_length) const {
ERR_FAIL_COND_V(!p_dst && p_length > 0, -1);
@@ -151,6 +220,18 @@ void FileAccessAndroid::store_8(uint8_t p_dest) {
ERR_FAIL();
}
+void FileAccessAndroid::store_16(uint16_t p_dest) {
+ ERR_FAIL();
+}
+
+void FileAccessAndroid::store_32(uint32_t p_dest) {
+ ERR_FAIL();
+}
+
+void FileAccessAndroid::store_64(uint64_t p_dest) {
+ ERR_FAIL();
+}
+
bool FileAccessAndroid::file_exists(const String &p_path) {
String path = fix_path(p_path).simplify_path();
if (path.begins_with("/")) {
diff --git a/platform/android/file_access_android.h b/platform/android/file_access_android.h
index 3aa4ca98fc..ec613b6687 100644
--- a/platform/android/file_access_android.h
+++ b/platform/android/file_access_android.h
@@ -66,12 +66,18 @@ public:
virtual bool eof_reached() const override; // reading passed EOF
virtual uint8_t get_8() const override; // get a byte
+ virtual uint16_t get_16() const override;
+ virtual uint32_t get_32() const override;
+ virtual uint64_t get_64() const override;
virtual uint64_t get_buffer(uint8_t *p_dst, uint64_t p_length) const override;
virtual Error get_error() const override; // get last error
virtual void flush() override;
virtual void store_8(uint8_t p_dest) override; // store a byte
+ virtual void store_16(uint16_t p_dest) override;
+ virtual void store_32(uint32_t p_dest) override;
+ virtual void store_64(uint64_t p_dest) override;
virtual bool file_exists(const String &p_path) override; // return true if a file exists
diff --git a/platform/android/file_access_filesystem_jandroid.cpp b/platform/android/file_access_filesystem_jandroid.cpp
index beea73fd61..46d9728632 100644
--- a/platform/android/file_access_filesystem_jandroid.cpp
+++ b/platform/android/file_access_filesystem_jandroid.cpp
@@ -181,6 +181,36 @@ uint8_t FileAccessFilesystemJAndroid::get_8() const {
return byte;
}
+uint16_t FileAccessFilesystemJAndroid::get_16() const {
+ ERR_FAIL_COND_V_MSG(!is_open(), 0, "File must be opened before use.");
+ uint16_t bytes = 0;
+ get_buffer(reinterpret_cast<uint8_t *>(&bytes), 2);
+ if (big_endian) {
+ bytes = BSWAP16(bytes);
+ }
+ return bytes;
+}
+
+uint32_t FileAccessFilesystemJAndroid::get_32() const {
+ ERR_FAIL_COND_V_MSG(!is_open(), 0, "File must be opened before use.");
+ uint32_t bytes = 0;
+ get_buffer(reinterpret_cast<uint8_t *>(&bytes), 4);
+ if (big_endian) {
+ bytes = BSWAP32(bytes);
+ }
+ return bytes;
+}
+
+uint64_t FileAccessFilesystemJAndroid::get_64() const {
+ ERR_FAIL_COND_V_MSG(!is_open(), 0, "File must be opened before use.");
+ uint64_t bytes = 0;
+ get_buffer(reinterpret_cast<uint8_t *>(&bytes), 8);
+ if (big_endian) {
+ bytes = BSWAP64(bytes);
+ }
+ return bytes;
+}
+
String FileAccessFilesystemJAndroid::get_line() const {
ERR_FAIL_COND_V_MSG(!is_open(), String(), "File must be opened before use.");
@@ -250,6 +280,27 @@ void FileAccessFilesystemJAndroid::store_8(uint8_t p_dest) {
store_buffer(&p_dest, 1);
}
+void FileAccessFilesystemJAndroid::store_16(uint16_t p_dest) {
+ if (big_endian) {
+ p_dest = BSWAP16(p_dest);
+ }
+ store_buffer(reinterpret_cast<uint8_t *>(&p_dest), 2);
+}
+
+void FileAccessFilesystemJAndroid::store_32(uint32_t p_dest) {
+ if (big_endian) {
+ p_dest = BSWAP32(p_dest);
+ }
+ store_buffer(reinterpret_cast<uint8_t *>(&p_dest), 4);
+}
+
+void FileAccessFilesystemJAndroid::store_64(uint64_t p_dest) {
+ if (big_endian) {
+ p_dest = BSWAP64(p_dest);
+ }
+ store_buffer(reinterpret_cast<uint8_t *>(&p_dest), 8);
+}
+
void FileAccessFilesystemJAndroid::store_buffer(const uint8_t *p_src, uint64_t p_length) {
if (_file_write) {
ERR_FAIL_COND_MSG(!is_open(), "File must be opened before use.");
diff --git a/platform/android/file_access_filesystem_jandroid.h b/platform/android/file_access_filesystem_jandroid.h
index 0c3f8d7259..f33aa64ebe 100644
--- a/platform/android/file_access_filesystem_jandroid.h
+++ b/platform/android/file_access_filesystem_jandroid.h
@@ -77,6 +77,9 @@ public:
virtual bool eof_reached() const override; ///< reading passed EOF
virtual uint8_t get_8() const override; ///< get a byte
+ virtual uint16_t get_16() const override;
+ virtual uint32_t get_32() const override;
+ virtual uint64_t get_64() const override;
virtual String get_line() const override; ///< get a line
virtual uint64_t get_buffer(uint8_t *p_dst, uint64_t p_length) const override;
@@ -84,6 +87,9 @@ public:
virtual void flush() override;
virtual void store_8(uint8_t p_dest) override; ///< store a byte
+ virtual void store_16(uint16_t p_dest) override;
+ virtual void store_32(uint32_t p_dest) override;
+ virtual void store_64(uint64_t p_dest) override;
virtual void store_buffer(const uint8_t *p_src, uint64_t p_length) override;
virtual bool file_exists(const String &p_path) override; ///< return true if a file exists
diff --git a/platform/android/java/editor/src/main/java/org/godotengine/editor/GodotEditor.kt b/platform/android/java/editor/src/main/java/org/godotengine/editor/GodotEditor.kt
index 0d7017ae71..caf64bc933 100644
--- a/platform/android/java/editor/src/main/java/org/godotengine/editor/GodotEditor.kt
+++ b/platform/android/java/editor/src/main/java/org/godotengine/editor/GodotEditor.kt
@@ -91,6 +91,10 @@ open class GodotEditor : GodotActivity() {
private val commandLineParams = ArrayList<String>()
override fun onCreate(savedInstanceState: Bundle?) {
+ // We exclude certain permissions from the set we request at startup, as they'll be
+ // requested on demand based on use-cases.
+ PermissionsUtil.requestManifestPermissions(this, setOf(Manifest.permission.RECORD_AUDIO))
+
val params = intent.getStringArrayExtra(EXTRA_COMMAND_LINE_PARAMS)
Log.d(TAG, "Received parameters ${params.contentToString()}")
updateCommandLineParams(params)
diff --git a/platform/android/java/lib/src/org/godotengine/godot/Godot.kt b/platform/android/java/lib/src/org/godotengine/godot/Godot.kt
index 217e7a2b60..da86e67c7d 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/Godot.kt
+++ b/platform/android/java/lib/src/org/godotengine/godot/Godot.kt
@@ -484,6 +484,14 @@ class Godot(private val context: Context) : SensorEventListener {
return containerLayout
}
+ fun onStart(host: GodotHost) {
+ if (host != primaryHost) {
+ return
+ }
+
+ renderView!!.onActivityStarted()
+ }
+
fun onResume(host: GodotHost) {
if (host != primaryHost) {
return
@@ -528,6 +536,14 @@ class Godot(private val context: Context) : SensorEventListener {
}
}
+ fun onStop(host: GodotHost) {
+ if (host != primaryHost) {
+ return
+ }
+
+ renderView!!.onActivityStopped()
+ }
+
fun onDestroy(primaryHost: GodotHost) {
if (this.primaryHost != primaryHost) {
return
diff --git a/platform/android/java/lib/src/org/godotengine/godot/GodotActivity.kt b/platform/android/java/lib/src/org/godotengine/godot/GodotActivity.kt
index a60f6e997e..e01c5481d5 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/GodotActivity.kt
+++ b/platform/android/java/lib/src/org/godotengine/godot/GodotActivity.kt
@@ -30,7 +30,6 @@
package org.godotengine.godot
-import android.Manifest
import android.app.Activity
import android.content.Intent
import android.content.pm.PackageManager
@@ -65,10 +64,6 @@ abstract class GodotActivity : FragmentActivity(), GodotHost {
private set
override fun onCreate(savedInstanceState: Bundle?) {
- // We exclude certain permissions from the set we request at startup, as they'll be
- // requested on demand based on use-cases.
- PermissionsUtil.requestManifestPermissions(this, setOf(Manifest.permission.RECORD_AUDIO))
-
super.onCreate(savedInstanceState)
setContentView(R.layout.godot_app_layout)
@@ -156,7 +151,8 @@ abstract class GodotActivity : FragmentActivity(), GodotHost {
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
godotFragment?.onRequestPermissionsResult(requestCode, permissions, grantResults)
- if (requestCode == PermissionsUtil.REQUEST_ALL_PERMISSION_REQ_CODE) {
+ // Logging the result of permission requests
+ if (requestCode == PermissionsUtil.REQUEST_ALL_PERMISSION_REQ_CODE || requestCode == PermissionsUtil.REQUEST_SINGLE_PERMISSION_REQ_CODE) {
Log.d(TAG, "Received permissions request result..")
for (i in permissions.indices) {
val permissionGranted = grantResults[i] == PackageManager.PERMISSION_GRANTED
diff --git a/platform/android/java/lib/src/org/godotengine/godot/GodotFragment.java b/platform/android/java/lib/src/org/godotengine/godot/GodotFragment.java
index f1c029e7a1..643c9a658e 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/GodotFragment.java
+++ b/platform/android/java/lib/src/org/godotengine/godot/GodotFragment.java
@@ -271,6 +271,32 @@ public class GodotFragment extends Fragment implements IDownloaderClient, GodotH
}
@Override
+ public void onStop() {
+ super.onStop();
+ if (!godot.isInitialized()) {
+ if (null != mDownloaderClientStub) {
+ mDownloaderClientStub.disconnect(getActivity());
+ }
+ return;
+ }
+
+ godot.onStop(this);
+ }
+
+ @Override
+ public void onStart() {
+ super.onStart();
+ if (!godot.isInitialized()) {
+ if (null != mDownloaderClientStub) {
+ mDownloaderClientStub.connect(getActivity());
+ }
+ return;
+ }
+
+ godot.onStart(this);
+ }
+
+ @Override
public void onResume() {
super.onResume();
if (!godot.isInitialized()) {
diff --git a/platform/android/java/lib/src/org/godotengine/godot/GodotGLRenderView.java b/platform/android/java/lib/src/org/godotengine/godot/GodotGLRenderView.java
index 52350c12a6..81043ce782 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/GodotGLRenderView.java
+++ b/platform/android/java/lib/src/org/godotengine/godot/GodotGLRenderView.java
@@ -114,12 +114,30 @@ public class GodotGLRenderView extends GLSurfaceView implements GodotRenderView
@Override
public void onActivityPaused() {
- onPause();
+ queueEvent(() -> {
+ GodotLib.focusout();
+ // Pause the renderer
+ godotRenderer.onActivityPaused();
+ });
+ }
+
+ @Override
+ public void onActivityStopped() {
+ pauseGLThread();
}
@Override
public void onActivityResumed() {
- onResume();
+ queueEvent(() -> {
+ // Resume the renderer
+ godotRenderer.onActivityResumed();
+ GodotLib.focusin();
+ });
+ }
+
+ @Override
+ public void onActivityStarted() {
+ resumeGLThread();
}
@Override
@@ -283,26 +301,4 @@ public class GodotGLRenderView extends GLSurfaceView implements GodotRenderView
/* Set the renderer responsible for frame rendering */
setRenderer(godotRenderer);
}
-
- @Override
- public void onResume() {
- super.onResume();
-
- queueEvent(() -> {
- // Resume the renderer
- godotRenderer.onActivityResumed();
- GodotLib.focusin();
- });
- }
-
- @Override
- public void onPause() {
- super.onPause();
-
- queueEvent(() -> {
- GodotLib.focusout();
- // Pause the renderer
- godotRenderer.onActivityPaused();
- });
- }
}
diff --git a/platform/android/java/lib/src/org/godotengine/godot/GodotIO.java b/platform/android/java/lib/src/org/godotengine/godot/GodotIO.java
index edcd9c4d1f..4b51bd778d 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/GodotIO.java
+++ b/platform/android/java/lib/src/org/godotengine/godot/GodotIO.java
@@ -178,12 +178,10 @@ public class GodotIO {
}
public int[] getDisplaySafeArea() {
- DisplayMetrics metrics = activity.getResources().getDisplayMetrics();
- Display display = activity.getWindowManager().getDefaultDisplay();
- Point size = new Point();
- display.getRealSize(size);
+ Rect rect = new Rect();
+ activity.getWindow().getDecorView().getWindowVisibleDisplayFrame(rect);
- int[] result = { 0, 0, size.x, size.y };
+ int[] result = { rect.left, rect.top, rect.right, rect.bottom };
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
WindowInsets insets = activity.getWindow().getDecorView().getRootWindowInsets();
DisplayCutout cutout = insets.getDisplayCutout();
diff --git a/platform/android/java/lib/src/org/godotengine/godot/GodotRenderView.java b/platform/android/java/lib/src/org/godotengine/godot/GodotRenderView.java
index ebf3a6b2fb..5b2f9f57c7 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/GodotRenderView.java
+++ b/platform/android/java/lib/src/org/godotengine/godot/GodotRenderView.java
@@ -47,8 +47,13 @@ public interface GodotRenderView {
void queueOnRenderThread(Runnable event);
void onActivityPaused();
+
+ void onActivityStopped();
+
void onActivityResumed();
+ void onActivityStarted();
+
void onBackPressed();
GodotInputHandler getInputHandler();
diff --git a/platform/android/java/lib/src/org/godotengine/godot/GodotVulkanRenderView.java b/platform/android/java/lib/src/org/godotengine/godot/GodotVulkanRenderView.java
index 48708152be..a1ee9bd6b4 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/GodotVulkanRenderView.java
+++ b/platform/android/java/lib/src/org/godotengine/godot/GodotVulkanRenderView.java
@@ -92,12 +92,30 @@ public class GodotVulkanRenderView extends VkSurfaceView implements GodotRenderV
@Override
public void onActivityPaused() {
- onPause();
+ queueOnVkThread(() -> {
+ GodotLib.focusout();
+ // Pause the renderer
+ mRenderer.onVkPause();
+ });
+ }
+
+ @Override
+ public void onActivityStopped() {
+ pauseRenderThread();
+ }
+
+ @Override
+ public void onActivityStarted() {
+ resumeRenderThread();
}
@Override
public void onActivityResumed() {
- onResume();
+ queueOnVkThread(() -> {
+ // Resume the renderer
+ mRenderer.onVkResume();
+ GodotLib.focusin();
+ });
}
@Override
@@ -211,26 +229,4 @@ public class GodotVulkanRenderView extends VkSurfaceView implements GodotRenderV
}
return super.onResolvePointerIcon(me, pointerIndex);
}
-
- @Override
- public void onResume() {
- super.onResume();
-
- queueOnVkThread(() -> {
- // Resume the renderer
- mRenderer.onVkResume();
- GodotLib.focusin();
- });
- }
-
- @Override
- public void onPause() {
- super.onPause();
-
- queueOnVkThread(() -> {
- GodotLib.focusout();
- // Pause the renderer
- mRenderer.onVkPause();
- });
- }
}
diff --git a/platform/android/java/lib/src/org/godotengine/godot/gl/GLSurfaceView.java b/platform/android/java/lib/src/org/godotengine/godot/gl/GLSurfaceView.java
index 56397bb2c2..ef97aaeab9 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/gl/GLSurfaceView.java
+++ b/platform/android/java/lib/src/org/godotengine/godot/gl/GLSurfaceView.java
@@ -122,8 +122,8 @@ import javax.microedition.khronos.opengles.GL10;
* <p>
* <h3>Activity Life-cycle</h3>
* A GLSurfaceView must be notified when to pause and resume rendering. GLSurfaceView clients
- * are required to call {@link #onPause()} when the activity stops and
- * {@link #onResume()} when the activity starts. These calls allow GLSurfaceView to
+ * are required to call {@link #pauseGLThread()} when the activity stops and
+ * {@link #resumeGLThread()} when the activity starts. These calls allow GLSurfaceView to
* pause and resume the rendering thread, and also allow GLSurfaceView to release and recreate
* the OpenGL display.
* <p>
@@ -339,8 +339,8 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback
* setRenderer is called:
* <ul>
* <li>{@link #getRenderMode()}
- * <li>{@link #onPause()}
- * <li>{@link #onResume()}
+ * <li>{@link #pauseGLThread()}
+ * <li>{@link #resumeGLThread()}
* <li>{@link #queueEvent(Runnable)}
* <li>{@link #requestRender()}
* <li>{@link #setRenderMode(int)}
@@ -568,6 +568,7 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback
}
+ // -- GODOT start --
/**
* Pause the rendering thread, optionally tearing down the EGL context
* depending upon the value of {@link #setPreserveEGLContextOnPause(boolean)}.
@@ -578,22 +579,23 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback
*
* Must not be called before a renderer has been set.
*/
- public void onPause() {
+ protected final void pauseGLThread() {
mGLThread.onPause();
}
/**
* Resumes the rendering thread, re-creating the OpenGL context if necessary. It
- * is the counterpart to {@link #onPause()}.
+ * is the counterpart to {@link #pauseGLThread()}.
*
* This method should typically be called in
* {@link android.app.Activity#onStart Activity.onStart}.
*
* Must not be called before a renderer has been set.
*/
- public void onResume() {
+ protected final void resumeGLThread() {
mGLThread.onResume();
}
+ // -- GODOT end --
/**
* Queue a runnable to be run on the GL rendering thread. This can be used
diff --git a/platform/android/java/lib/src/org/godotengine/godot/utils/PermissionsUtil.java b/platform/android/java/lib/src/org/godotengine/godot/utils/PermissionsUtil.java
index 9a82204467..737b4ac20b 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/utils/PermissionsUtil.java
+++ b/platform/android/java/lib/src/org/godotengine/godot/utils/PermissionsUtil.java
@@ -56,9 +56,9 @@ import java.util.Set;
public final class PermissionsUtil {
private static final String TAG = PermissionsUtil.class.getSimpleName();
- static final int REQUEST_RECORD_AUDIO_PERMISSION = 1;
- static final int REQUEST_CAMERA_PERMISSION = 2;
- static final int REQUEST_VIBRATE_PERMISSION = 3;
+ public static final int REQUEST_RECORD_AUDIO_PERMISSION = 1;
+ public static final int REQUEST_CAMERA_PERMISSION = 2;
+ public static final int REQUEST_VIBRATE_PERMISSION = 3;
public static final int REQUEST_ALL_PERMISSION_REQ_CODE = 1001;
public static final int REQUEST_SINGLE_PERMISSION_REQ_CODE = 1002;
public static final int REQUEST_MANAGE_EXTERNAL_STORAGE_REQ_CODE = 2002;
@@ -70,7 +70,7 @@ public final class PermissionsUtil {
* Request a dangerous permission. name must be specified in <a href="https://github.com/aosp-mirror/platform_frameworks_base/blob/master/core/res/AndroidManifest.xml">this</a>
* @param permissionName the name of the requested permission.
* @param activity the caller activity for this method.
- * @return true/false. "true" if permission was granted otherwise returns "false".
+ * @return true/false. "true" if permission is already granted, "false" if a permission request was dispatched.
*/
public static boolean requestPermission(String permissionName, Activity activity) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
@@ -124,7 +124,7 @@ public final class PermissionsUtil {
/**
* Request dangerous permissions which are defined in the Android manifest file from the user.
* @param activity the caller activity for this method.
- * @return true/false. "true" if all permissions were granted otherwise returns "false".
+ * @return true/false. "true" if all permissions were already granted, returns "false" if permissions requests were dispatched.
*/
public static boolean requestManifestPermissions(Activity activity) {
return requestManifestPermissions(activity, null);
@@ -134,7 +134,7 @@ public final class PermissionsUtil {
* Request dangerous permissions which are defined in the Android manifest file from the user.
* @param activity the caller activity for this method.
* @param excludes Set of permissions to exclude from the request
- * @return true/false. "true" if all permissions were granted otherwise returns "false".
+ * @return true/false. "true" if all permissions were already granted, returns "false" if permissions requests were dispatched.
*/
public static boolean requestManifestPermissions(Activity activity, @Nullable Set<String> excludes) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
@@ -235,7 +235,7 @@ public final class PermissionsUtil {
/**
* Check if the given permission is in the AndroidManifest.xml file.
* @param context the caller context for this method.
- * @param permission the permession to look for in the manifest file.
+ * @param permission the permission to look for in the manifest file.
* @return "true" if the permission is in the manifest file of the activity, "false" otherwise.
*/
public static boolean hasManifestPermission(Context context, String permission) {
diff --git a/platform/android/java/lib/src/org/godotengine/godot/vulkan/VkSurfaceView.kt b/platform/android/java/lib/src/org/godotengine/godot/vulkan/VkSurfaceView.kt
index 3828004198..791b425444 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/vulkan/VkSurfaceView.kt
+++ b/platform/android/java/lib/src/org/godotengine/godot/vulkan/VkSurfaceView.kt
@@ -99,7 +99,7 @@ open internal class VkSurfaceView(context: Context) : SurfaceView(context), Surf
*
* Must not be called before a [VkRenderer] has been set.
*/
- open fun onResume() {
+ protected fun resumeRenderThread() {
vkThread.onResume()
}
@@ -108,7 +108,7 @@ open internal class VkSurfaceView(context: Context) : SurfaceView(context), Surf
*
* Must not be called before a [VkRenderer] has been set.
*/
- open fun onPause() {
+ protected fun pauseRenderThread() {
vkThread.onPause()
}
diff --git a/platform/android/java_godot_lib_jni.cpp b/platform/android/java_godot_lib_jni.cpp
index 50075ed3f5..08e792cc04 100644
--- a/platform/android/java_godot_lib_jni.cpp
+++ b/platform/android/java_godot_lib_jni.cpp
@@ -484,7 +484,7 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_calldeferred(JNIEnv *
env->DeleteLocalRef(jobj);
}
- MessageQueue::get_singleton()->push_callp(obj, str_method, argptrs, count);
+ Callable(obj, str_method).call_deferredp(argptrs, count);
}
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_requestPermissionResult(JNIEnv *env, jclass clazz, jstring p_permission, jboolean p_result) {
diff --git a/platform/android/java_godot_wrapper.cpp b/platform/android/java_godot_wrapper.cpp
index 4e401e633e..3c950bb1b1 100644
--- a/platform/android/java_godot_wrapper.cpp
+++ b/platform/android/java_godot_wrapper.cpp
@@ -334,7 +334,7 @@ void GodotJavaWrapper::vibrate(int p_duration_ms) {
}
}
-int GodotJavaWrapper::create_new_godot_instance(List<String> args) {
+int GodotJavaWrapper::create_new_godot_instance(const List<String> &args) {
if (_create_new_godot_instance) {
JNIEnv *env = get_jni_env();
ERR_FAIL_NULL_V(env, 0);
diff --git a/platform/android/java_godot_wrapper.h b/platform/android/java_godot_wrapper.h
index 52043c6027..93998021a9 100644
--- a/platform/android/java_godot_wrapper.h
+++ b/platform/android/java_godot_wrapper.h
@@ -104,7 +104,7 @@ public:
void init_input_devices();
void vibrate(int p_duration_ms);
String get_input_fallback_mapping();
- int create_new_godot_instance(List<String> args);
+ int create_new_godot_instance(const List<String> &args);
void begin_benchmark_measure(const String &p_context, const String &p_label);
void end_benchmark_measure(const String &p_context, const String &p_label);
void dump_benchmark(const String &benchmark_file);
diff --git a/platform/ios/SCsub b/platform/ios/SCsub
index 18ba6617af..30d4216464 100644
--- a/platform/ios/SCsub
+++ b/platform/ios/SCsub
@@ -20,6 +20,7 @@ ios_lib = [
"device_metrics.m",
"keyboard_input_view.mm",
"key_mapping_ios.mm",
+ "ios_terminal_logger.mm",
]
env_ios = env.Clone()
diff --git a/platform/ios/detect.py b/platform/ios/detect.py
index 26d81c8ed6..23f688501b 100644
--- a/platform/ios/detect.py
+++ b/platform/ios/detect.py
@@ -152,7 +152,7 @@ def configure(env: "Environment"):
env.Append(CPPDEFINES=["IOS_ENABLED", "UNIX_ENABLED", "COREAUDIO_ENABLED"])
if env["vulkan"]:
- env.Append(CPPDEFINES=["VULKAN_ENABLED"])
+ env.Append(CPPDEFINES=["VULKAN_ENABLED", "RD_ENABLED"])
if env["opengl3"]:
env.Append(CPPDEFINES=["GLES3_ENABLED", "GLES_SILENCE_DEPRECATION"])
diff --git a/platform/ios/doc_classes/EditorExportPlatformIOS.xml b/platform/ios/doc_classes/EditorExportPlatformIOS.xml
index ecae6d721e..35ef6d6a78 100644
--- a/platform/ios/doc_classes/EditorExportPlatformIOS.xml
+++ b/platform/ios/doc_classes/EditorExportPlatformIOS.xml
@@ -10,6 +10,13 @@
<link title="iOS plugins documentation index">$DOCS_URL/tutorials/platform/ios/index.html</link>
</tutorials>
<members>
+ <member name="application/additional_plist_content" type="String" setter="" getter="">
+ Additional data added to the root [code]&lt;dict&gt;[/code] section of the [url=https://developer.apple.com/documentation/bundleresources/information_property_list]Info.plist[/url] file. The value should be an XML section with pairs of key-value elements, e.g.:
+ [codeblock]
+ &lt;key&gt;key_name&lt;/key&gt;
+ &lt;string&gt;value&lt;/string&gt;
+ [/codeblock]
+ </member>
<member name="application/app_store_team_id" type="String" setter="" getter="">
Apple Team ID, unique 10-character string. To locate your Team ID check "Membership details" section in your Apple developer account dashboard, or "Organizational Unit" of your code signing certificate. See [url=https://developer.apple.com/help/account/manage-your-team/locate-your-team-id]Locate your Team ID[/url].
</member>
@@ -34,8 +41,8 @@
<member name="application/icon_interpolation" type="int" setter="" getter="">
Interpolation method used to resize application icon.
</member>
- <member name="application/launch_screens_interpolation" type="int" setter="" getter="">
- Interpolation method used to resize launch screen images.
+ <member name="application/min_ios_version" type="String" setter="" getter="">
+ Minimum version of iOS required for this application to run in the [code]major.minor.patch[/code] or [code]major.minor[/code] format, can only contain numeric characters ([code]0-9[/code]) and periods ([code].[/code]).
</member>
<member name="application/provisioning_profile_uuid_debug" type="String" setter="" getter="">
UUID of the provisioning profile. If left empty, Xcode will download or create a provisioning profile automatically. See [url=https://developer.apple.com/help/account/manage-profiles/edit-download-or-delete-profiles]Edit, download, or delete provisioning profiles[/url].
@@ -63,6 +70,14 @@
<member name="capabilities/access_wifi" type="bool" setter="" getter="">
If [code]true[/code], networking features related to Wi-Fi access are enabled. See [url=https://developer.apple.com/support/required-device-capabilities/]Required Device Capabilities[/url].
</member>
+ <member name="capabilities/performance_a12" type="bool" setter="" getter="">
+ Requires the graphics performance and features of the A12 Bionic and later chips (devices supporting all Vulkan renderer features).
+ Enabling this option limits supported devices to: iPhone XS, iPhone XR, iPad Mini (5th gen.), iPad Air (3rd gen.), iPad (8th gen) and newer.
+ </member>
+ <member name="capabilities/performance_gaming_tier" type="bool" setter="" getter="">
+ Requires the graphics performance and features of the A17 Pro and later chips.
+ Enabling this option limits supported devices to: iPhone 15 Pro and newer.
+ </member>
<member name="capabilities/push_notifications" type="bool" setter="" getter="">
If [code]true[/code], push notifications are enabled. See [url=https://developer.apple.com/support/required-device-capabilities/]Required Device Capabilities[/url].
</member>
@@ -108,39 +123,6 @@
<member name="icons/spotlight_80x80" type="String" setter="" getter="">
Spotlight icon file on iPad and iPhone (2x DPI). If left empty, it will fallback to [member ProjectSettings.application/config/icon]. See [url=https://developer.apple.com/design/human-interface-guidelines/foundations/app-icons]App icons[/url].
</member>
- <member name="landscape_launch_screens/ipad_1024x768" type="String" setter="" getter="">
- Application launch screen image file. If left empty, it will fallback to [member ProjectSettings.application/boot_splash/image].
- </member>
- <member name="landscape_launch_screens/ipad_2048x1536" type="String" setter="" getter="">
- Application launch screen image file. If left empty, it will fallback to [member ProjectSettings.application/boot_splash/image].
- </member>
- <member name="landscape_launch_screens/iphone_2208x1242" type="String" setter="" getter="">
- Application launch screen image file. If left empty, it will fallback to [member ProjectSettings.application/boot_splash/image].
- </member>
- <member name="landscape_launch_screens/iphone_2436x1125" type="String" setter="" getter="">
- Application launch screen image file. If left empty, it will fallback to [member ProjectSettings.application/boot_splash/image].
- </member>
- <member name="portrait_launch_screens/ipad_768x1024" type="String" setter="" getter="">
- Application launch screen image file. If left empty, it will fallback to [member ProjectSettings.application/boot_splash/image].
- </member>
- <member name="portrait_launch_screens/ipad_1536x2048" type="String" setter="" getter="">
- Application launch screen image file. If left empty, it will fallback to [member ProjectSettings.application/boot_splash/image].
- </member>
- <member name="portrait_launch_screens/iphone_640x960" type="String" setter="" getter="">
- Application launch screen image file. If left empty, it will fallback to [member ProjectSettings.application/boot_splash/image].
- </member>
- <member name="portrait_launch_screens/iphone_640x1136" type="String" setter="" getter="">
- Application launch screen image file. If left empty, it will fallback to [member ProjectSettings.application/boot_splash/image].
- </member>
- <member name="portrait_launch_screens/iphone_750x1334" type="String" setter="" getter="">
- Application launch screen image file. If left empty, it will fallback to [member ProjectSettings.application/boot_splash/image].
- </member>
- <member name="portrait_launch_screens/iphone_1125x2436" type="String" setter="" getter="">
- Application launch screen image file. If left empty, it will fallback to [member ProjectSettings.application/boot_splash/image].
- </member>
- <member name="portrait_launch_screens/iphone_1242x2208" type="String" setter="" getter="">
- Application launch screen image file. If left empty, it will fallback to [member ProjectSettings.application/boot_splash/image].
- </member>
<member name="privacy/camera_usage_description" type="String" setter="" getter="">
A message displayed when requesting access to the device's camera (in English).
</member>
@@ -174,9 +156,6 @@
<member name="storyboard/use_custom_bg_color" type="bool" setter="" getter="">
If [code]true[/code], [member storyboard/custom_bg_color] is used as a launch screen background color, otherwise [code]application/boot_splash/bg_color[/code] project setting is used.
</member>
- <member name="storyboard/use_launch_screen_storyboard" type="bool" setter="" getter="">
- If [code]true[/code], storyboard launch screen is used instead of launch screen images.
- </member>
<member name="user_data/accessible_from_files_app" type="bool" setter="" getter="">
If [code]true[/code], the app "Documents" folder can be accessed via "Files" app. See [url=https://developer.apple.com/documentation/bundleresources/information_property_list/lssupportsopeningdocumentsinplace]LSSupportsOpeningDocumentsInPlace[/url].
</member>
diff --git a/platform/ios/export/export_plugin.cpp b/platform/ios/export/export_plugin.cpp
index b478759e45..d35819c34d 100644
--- a/platform/ios/export/export_plugin.cpp
+++ b/platform/ios/export/export_plugin.cpp
@@ -34,14 +34,15 @@
#include "run_icon_svg.gen.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_scale.h"
#include "editor/editor_string_names.h"
#include "editor/export/editor_export.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 and svg.
#ifdef MODULE_SVG_ENABLED
@@ -105,29 +106,6 @@ static const IconInfo icon_infos[] = {
{ PNAME("icons/notification_60x60"), "iphone", "Icon-60.png", "60", "3x", "20x20", false }
};
-struct LoadingScreenInfo {
- const char *preset_key;
- const char *export_name;
- int width = 0;
- int height = 0;
- bool rotate = false;
-};
-
-static const LoadingScreenInfo loading_screen_infos[] = {
- { PNAME("landscape_launch_screens/iphone_2436x1125"), "Default-Landscape-X.png", 2436, 1125, false },
- { PNAME("landscape_launch_screens/iphone_2208x1242"), "Default-Landscape-736h@3x.png", 2208, 1242, false },
- { PNAME("landscape_launch_screens/ipad_1024x768"), "Default-Landscape.png", 1024, 768, false },
- { PNAME("landscape_launch_screens/ipad_2048x1536"), "Default-Landscape@2x.png", 2048, 1536, false },
-
- { PNAME("portrait_launch_screens/iphone_640x960"), "Default-480h@2x.png", 640, 960, false },
- { PNAME("portrait_launch_screens/iphone_640x1136"), "Default-568h@2x.png", 640, 1136, false },
- { PNAME("portrait_launch_screens/iphone_750x1334"), "Default-667h@2x.png", 750, 1334, false },
- { PNAME("portrait_launch_screens/iphone_1125x2436"), "Default-Portrait-X.png", 1125, 2436, false },
- { PNAME("portrait_launch_screens/ipad_768x1024"), "Default-Portrait.png", 768, 1024, false },
- { PNAME("portrait_launch_screens/ipad_1536x2048"), "Default-Portrait@2x.png", 1536, 2048, false },
- { PNAME("portrait_launch_screens/iphone_1242x2208"), "Default-Portrait-736h@3x.png", 1242, 2208, false }
-};
-
String EditorExportPlatformIOS::get_export_option_warning(const EditorExportPreset *p_preset, const StringName &p_name) const {
if (p_preset) {
if (p_name == "application/app_store_team_id") {
@@ -147,12 +125,6 @@ String EditorExportPlatformIOS::get_export_option_warning(const EditorExportPres
}
bool EditorExportPlatformIOS::get_export_option_visibility(const EditorExportPreset *p_preset, const String &p_option) const {
- if (p_preset) {
- bool sb = p_preset->get("storyboard/use_launch_screen_storyboard");
- if (!sb && p_option != "storyboard/use_launch_screen_storyboard" && p_option.begins_with("storyboard/")) {
- return false;
- }
- }
return true;
}
@@ -181,8 +153,11 @@ void EditorExportPlatformIOS::get_export_options(List<ExportOption> *r_options)
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/min_ios_version"), "12.0"));
+
+ 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::INT, "application/launch_screens_interpolation", PROPERTY_HINT_ENUM, "Nearest neighbor,Bilinear,Cubic,Trilinear,Lanczos"), 4));
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "application/export_project_only"), false));
@@ -217,6 +192,8 @@ void EditorExportPlatformIOS::get_export_options(List<ExportOption> *r_options)
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "capabilities/access_wifi"), false));
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "capabilities/push_notifications"), 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::BOOL, "user_data/accessible_from_files_app"), false));
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "user_data/accessible_from_itunes_sharing"), false));
@@ -235,16 +212,11 @@ void EditorExportPlatformIOS::get_export_options(List<ExportOption> *r_options)
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, true));
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,*.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,*.jpg,*.jpeg"), ""));
- }
}
void EditorExportPlatformIOS::_fix_config_file(const Ref<EditorExportPreset> &p_preset, Vector<uint8_t> &pfile, const IOSConfigData &p_config, bool p_debug) {
@@ -262,8 +234,8 @@ void EditorExportPlatformIOS::_fix_config_file(const Ref<EditorExportPreset> &p_
};
String dbg_sign_id = p_preset->get("application/code_sign_identity_debug").operator String().is_empty() ? "iPhone Developer" : p_preset->get("application/code_sign_identity_debug");
String rel_sign_id = p_preset->get("application/code_sign_identity_release").operator String().is_empty() ? "iPhone Distribution" : p_preset->get("application/code_sign_identity_release");
- bool dbg_manual = !p_preset->get_or_env("application/provisioning_profile_uuid_debug", ENV_IOS_PROFILE_UUID_DEBUG).operator String().is_empty() || (dbg_sign_id != "iPhone Developer");
- bool rel_manual = !p_preset->get_or_env("application/provisioning_profile_uuid_release", ENV_IOS_PROFILE_UUID_RELEASE).operator String().is_empty() || (rel_sign_id != "iPhone Distribution");
+ bool dbg_manual = !p_preset->get_or_env("application/provisioning_profile_uuid_debug", ENV_IOS_PROFILE_UUID_DEBUG).operator String().is_empty() || (dbg_sign_id != "iPhone Developer" && dbg_sign_id != "iPhone Distribution");
+ bool rel_manual = !p_preset->get_or_env("application/provisioning_profile_uuid_release", ENV_IOS_PROFILE_UUID_RELEASE).operator String().is_empty() || (rel_sign_id != "iPhone Developer" && rel_sign_id != "iPhone Distribution");
String str;
String strnew;
str.parse_utf8((const char *)pfile.ptr(), pfile.size());
@@ -287,6 +259,8 @@ void EditorExportPlatformIOS::_fix_config_file(const Ref<EditorExportPreset> &p_
strnew += lines[i].replace("$short_version", p_preset->get_version("application/short_version")) + "\n";
} else if (lines[i].find("$version") != -1) {
strnew += lines[i].replace("$version", p_preset->get_version("application/version")) + "\n";
+ } else if (lines[i].find("$min_version") != -1) {
+ strnew += lines[i].replace("$min_version", p_preset->get("application/min_ios_version")) + "\n";
} else if (lines[i].find("$signature") != -1) {
strnew += lines[i].replace("$signature", p_preset->get("application/signature")) + "\n";
} else if (lines[i].find("$team_id") != -1) {
@@ -359,7 +333,12 @@ void EditorExportPlatformIOS::_fix_config_file(const Ref<EditorExportPreset> &p_
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 += "<string>" + capabilities_list[idx] + "</string>\n";
}
@@ -413,29 +392,20 @@ void EditorExportPlatformIOS::_fix_config_file(const Ref<EditorExportPreset> &p_
String description = p_preset->get("privacy/photolibrary_usage_description");
strnew += lines[i].replace("$photolibrary_usage_description", description) + "\n";
} else if (lines[i].find("$plist_launch_screen_name") != -1) {
- bool is_on = p_preset->get("storyboard/use_launch_screen_storyboard");
- String value = is_on ? "<key>UILaunchStoryboardName</key>\n<string>Launch Screen</string>" : "";
+ String value = "<key>UILaunchStoryboardName</key>\n<string>Launch Screen</string>";
strnew += lines[i].replace("$plist_launch_screen_name", value) + "\n";
} else if (lines[i].find("$pbx_launch_screen_file_reference") != -1) {
- bool is_on = p_preset->get("storyboard/use_launch_screen_storyboard");
- String value = is_on ? "90DD2D9D24B36E8000717FE1 = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = \"Launch Screen.storyboard\"; sourceTree = \"<group>\"; };" : "";
+ String value = "90DD2D9D24B36E8000717FE1 = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = \"Launch Screen.storyboard\"; sourceTree = \"<group>\"; };";
strnew += lines[i].replace("$pbx_launch_screen_file_reference", value) + "\n";
} else if (lines[i].find("$pbx_launch_screen_copy_files") != -1) {
- bool is_on = p_preset->get("storyboard/use_launch_screen_storyboard");
- String value = is_on ? "90DD2D9D24B36E8000717FE1 /* Launch Screen.storyboard */," : "";
+ String value = "90DD2D9D24B36E8000717FE1 /* Launch Screen.storyboard */,";
strnew += lines[i].replace("$pbx_launch_screen_copy_files", value) + "\n";
} else if (lines[i].find("$pbx_launch_screen_build_phase") != -1) {
- bool is_on = p_preset->get("storyboard/use_launch_screen_storyboard");
- String value = is_on ? "90DD2D9E24B36E8000717FE1 /* Launch Screen.storyboard in Resources */," : "";
+ String value = "90DD2D9E24B36E8000717FE1 /* Launch Screen.storyboard in Resources */,";
strnew += lines[i].replace("$pbx_launch_screen_build_phase", value) + "\n";
} else if (lines[i].find("$pbx_launch_screen_build_reference") != -1) {
- bool is_on = p_preset->get("storyboard/use_launch_screen_storyboard");
- String value = is_on ? "90DD2D9E24B36E8000717FE1 /* Launch Screen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 90DD2D9D24B36E8000717FE1 /* Launch Screen.storyboard */; };" : "";
+ 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";
- } else if (lines[i].find("$pbx_launch_image_usage_setting") != -1) {
- bool is_on = p_preset->get("storyboard/use_launch_screen_storyboard");
- String value = is_on ? "" : "ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage;";
- strnew += lines[i].replace("$pbx_launch_image_usage_setting", value) + "\n";
} else if (lines[i].find("$launch_screen_image_mode") != -1) {
int image_scale_mode = p_preset->get("storyboard/image_scale_mode");
String value;
@@ -783,94 +753,6 @@ Error EditorExportPlatformIOS::_export_loading_screen_file(const Ref<EditorExpor
return OK;
}
-Error EditorExportPlatformIOS::_export_loading_screen_images(const Ref<EditorExportPreset> &p_preset, const String &p_dest_dir) {
- Ref<DirAccess> da = DirAccess::open(p_dest_dir);
- ERR_FAIL_COND_V_MSG(da.is_null(), ERR_CANT_OPEN, "Cannot open directory '" + p_dest_dir + "'.");
-
- for (uint64_t i = 0; i < sizeof(loading_screen_infos) / sizeof(loading_screen_infos[0]); ++i) {
- LoadingScreenInfo info = loading_screen_infos[i];
- String loading_screen_file = p_preset->get(info.preset_key);
-
- 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.
- Ref<Image> img = memnew(Image);
- Error err = ImageLoader::load_image(loading_screen_file, img);
- if (err != OK) {
- ERR_PRINT("Invalid loading screen (" + String(info.preset_key) + "): '" + loading_screen_file + "'.");
- return ERR_UNCONFIGURED;
- }
- if (img->get_width() != info.width || img->get_height() != info.height) {
- WARN_PRINT("Loading screen (" + String(info.preset_key) + "): '" + loading_screen_file + "' has incorrect size (" + String::num_int64(img->get_width()) + "x" + String::num_int64(img->get_height()) + ") and was automatically resized to " + String::num_int64(info.width) + "x" + String::num_int64(info.height) + ".");
- 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, (Image::Interpolation)(p_preset->get("application/launch_screens_interpolation").operator int()));
- } else {
- img->resize(info.width, info.width / aspect_ratio, (Image::Interpolation)(p_preset->get("application/launch_screens_interpolation").operator int()));
- }
- }
- 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);
- } else {
- err = da->copy(loading_screen_file, p_dest_dir + info.export_name);
- }
- if (err) {
- String err_str = String("Failed to export loading screen (") + info.preset_key + ") from path '" + loading_screen_file + "'.";
- ERR_PRINT(err_str.utf8().get_data());
- return err;
- }
- } else {
- // Generate loading screen from the splash screen
- Ref<Image> img = Image::create_empty(info.width, info.height, false, Image::FORMAT_RGBA8);
- img->fill(boot_bg_color);
-
- Ref<Image> img_bs;
-
- if (boot_logo_path.length() > 0) {
- img_bs = Ref<Image>(memnew(Image));
- ImageLoader::load_image(boot_logo_path, img_bs);
- }
- if (!img_bs.is_valid()) {
- img_bs = Ref<Image>(memnew(Image(boot_splash_png)));
- }
- if (img_bs.is_valid()) {
- float aspect_ratio = (float)img_bs->get_width() / (float)img_bs->get_height();
- if (info.rotate) {
- if (boot_logo_scale) {
- if (info.width * aspect_ratio <= info.height) {
- 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, (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, (Image::Interpolation)(p_preset->get("application/launch_screens_interpolation").operator int()));
- } else {
- img_bs->resize(info.width, info.width / aspect_ratio, (Image::Interpolation)(p_preset->get("application/launch_screens_interpolation").operator int()));
- }
- }
- }
- _blend_and_rotate(img, img_bs, info.rotate);
- }
- Error err = img->save_png(p_dest_dir + info.export_name);
- if (err) {
- String err_str = String("Failed to export loading screen (") + info.preset_key + ") from splash screen.";
- WARN_PRINT(err_str.utf8().get_data());
- }
- }
- }
-
- return OK;
-}
-
Error EditorExportPlatformIOS::_walk_dir_recursive(Ref<DirAccess> &p_da, FileHandler p_handler, void *p_userdata) {
Vector<String> dirs;
String current_dir = p_da->get_current_dir();
@@ -986,7 +868,183 @@ struct ExportLibsData {
String dest_dir;
};
-void EditorExportPlatformIOS::_add_assets_to_project(const Ref<EditorExportPreset> &p_preset, Vector<uint8_t> &p_project_data, const Vector<IOSExportAsset> &p_additional_assets) {
+void EditorExportPlatformIOS::_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;
+ plist.instantiate();
+ plist->load_file(p_path.path_join("Info.plist"));
+ Ref<PListNode> root_node = plist->get_root();
+ if (root_node.is_null()) {
+ return;
+ }
+ Dictionary root = root_node->get_value();
+ if (!root.has("AvailableLibraries")) {
+ return;
+ }
+ Ref<PListNode> 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<PListNode> lib_node = libs[j];
+ if (lib_node.is_null()) {
+ return;
+ }
+ Dictionary lib = lib_node->get_value();
+ if (lib.has("BinaryPath")) {
+ Ref<PListNode> 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 EditorExportPlatformIOS::_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<DirAccess> da = DirAccess::create_for_path(p_source);
+ if (da.is_null()) {
+ return ERR_CANT_OPEN;
+ }
+
+ Ref<DirAccess> 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(p_source.length() - 1) : p_source;
+ if (asset.ends_with(".xcframework")) {
+ Ref<PList> plist;
+ plist.instantiate();
+ plist->load_file(p_source.path_join("Info.plist"));
+ Ref<PListNode> 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<PListNode> 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<PListNode> 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<PListNode> bpath_node = lib["BinaryPath"];
+ Ref<PListNode> lpath_node = lib["LibraryPath"];
+ Ref<PListNode> 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<PListNode> 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<FileAccess> 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<String> 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 info_plist_format = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
+ "<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n"
+ "<plist version=\"1.0\">\n"
+ " <dict>\n"
+ " <key>CFBundleShortVersionString</key>\n"
+ " <string>1.0</string>\n"
+ " <key>CFBundleIdentifier</key>\n"
+ " <string>$id.framework.$name</string>\n"
+ " <key>CFBundleName</key>\n"
+ " <string>$name</string>\n"
+ " <key>CFBundleExecutable</key>\n"
+ " <string>$name</string>\n"
+ " <key>DTPlatformName</key>\n"
+ " <string>iphoneos</string>\n"
+ " <key>CFBundleInfoDictionaryVersion</key>\n"
+ " <string>6.0</string>\n"
+ " <key>CFBundleVersion</key>\n"
+ " <string>1</string>\n"
+ " <key>CFBundlePackageType</key>\n"
+ " <string>FMWK</string>\n"
+ " <key>MinimumOSVersion</key>\n"
+ " <string>12.0</string>\n"
+ " </dict>\n"
+ "</plist>";
+
+ String info_plist = info_plist_format.replace("$id", p_id).replace("$name", file_name);
+
+ Ref<FileAccess> f = FileAccess::open(p_destination.path_join("Info.plist"), FileAccess::WRITE);
+ if (f.is_valid()) {
+ f->store_string(info_plist);
+ }
+ }
+ }
+
+ return OK;
+}
+
+void EditorExportPlatformIOS::_add_assets_to_project(const String &p_out_dir, const Ref<EditorExportPreset> &p_preset, Vector<uint8_t> &p_project_data, const Vector<IOSExportAsset> &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 };
@@ -1011,7 +1069,12 @@ void EditorExportPlatformIOS::_add_assets_to_project(const Ref<EditorExportPrese
String type;
if (asset.exported_path.ends_with(".framework")) {
- if (asset.should_embed) {
+ 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";
@@ -1074,12 +1137,12 @@ void EditorExportPlatformIOS::_add_assets_to_project(const Ref<EditorExportPrese
}
}
-Error EditorExportPlatformIOS::_copy_asset(const String &p_out_dir, const String &p_asset, const String *p_custom_file_name, bool p_is_framework, bool p_should_embed, Vector<IOSExportAsset> &r_exported_assets) {
+Error EditorExportPlatformIOS::_copy_asset(const Ref<EditorExportPreset> &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<IOSExportAsset> &r_exported_assets) {
String binary_name = p_out_dir.get_file().get_basename();
Ref<DirAccess> da = DirAccess::create_for_path(p_asset);
if (da.is_null()) {
- ERR_FAIL_V_MSG(ERR_CANT_CREATE, "Can't create directory: " + p_asset + ".");
+ 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);
@@ -1093,7 +1156,8 @@ Error EditorExportPlatformIOS::_copy_asset(const String &p_out_dir, const String
String destination;
String asset_path;
- bool create_framework = false;
+ Ref<DirAccess> 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 iOS we need to turn .dylib into .framework
@@ -1113,10 +1177,23 @@ Error EditorExportPlatformIOS::_copy_asset(const String &p_out_dir, const String
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 && (asset.ends_with(".framework") || asset.ends_with(".xcframework"))) {
- asset_path = String("dylibs").path_join(base_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 iOS 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) {
@@ -1128,8 +1205,29 @@ Error EditorExportPlatformIOS::_copy_asset(const String &p_out_dir, const String
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;
+
+ 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;
@@ -1139,99 +1237,67 @@ Error EditorExportPlatformIOS::_copy_asset(const String &p_out_dir, const String
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);
- }
-
- Ref<DirAccess> 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 + "'.");
+ destination_dir = p_out_dir.path_join(asset_path);
+ destination = destination_dir;
- 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;
+ // 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(asset_path.length() - 1);
- }
- IOSExportAsset exported_asset = { binary_name.path_join(asset_path), p_is_framework, p_should_embed };
- r_exported_assets.push_back(exported_asset);
+ 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;
- if (create_framework) {
String file_name;
if (!p_custom_file_name) {
- file_name = p_asset.get_basename().get_file();
+ file_name = p_asset.get_file();
} else {
file_name = *p_custom_file_name;
}
- String framework_name = file_name + ".framework";
-
- // Performing `install_name_tool -id @rpath/{name}.framework/{name} ./{name}` on dylib
- {
- List<String> 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(destination);
-
- OS::get_singleton()->execute("install_name_tool", install_name_args);
- }
-
- // Creating Info.plist
- {
- String info_plist_format = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
- "<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n"
- "<plist version=\"1.0\">\n"
- "<dict>\n"
- "<key>CFBundleShortVersionString</key>\n"
- "<string>1.0</string>\n"
- "<key>CFBundleIdentifier</key>\n"
- "<string>com.gdextension.framework.$name</string>\n"
- "<key>CFBundleName</key>\n"
- "<string>$name</string>\n"
- "<key>CFBundleExecutable</key>\n"
- "<string>$name</string>\n"
- "<key>DTPlatformName</key>\n"
- "<string>iphoneos</string>\n"
- "<key>CFBundleInfoDictionaryVersion</key>\n"
- "<string>6.0</string>\n"
- "<key>CFBundleVersion</key>\n"
- "<string>1</string>\n"
- "<key>CFBundlePackageType</key>\n"
- "<string>FMWK</string>\n"
- "<key>MinimumOSVersion</key>\n"
- "<string>10.0</string>\n"
- "</dict>\n"
- "</plist>";
-
- String info_plist = info_plist_format.replace("$name", 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);
- Ref<FileAccess> f = FileAccess::open(destination_dir.path_join("Info.plist"), FileAccess::WRITE);
- if (f.is_valid()) {
- f->store_string(info_plist);
+ // 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(asset_path.length() - 1);
+ }
+ IOSExportAsset exported_asset = { binary_name.path_join(asset_path), p_is_framework, p_should_embed };
+ r_exported_assets.push_back(exported_asset);
+
return OK;
}
-Error EditorExportPlatformIOS::_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) {
+Error EditorExportPlatformIOS::_export_additional_assets(const Ref<EditorExportPreset> &p_preset, const String &p_out_dir, const Vector<String> &p_assets, bool p_is_framework, bool p_should_embed, Vector<IOSExportAsset> &r_exported_assets) {
for (int f_idx = 0; f_idx < p_assets.size(); ++f_idx) {
- String asset = p_assets[f_idx];
+ const String &asset = p_assets[f_idx];
if (asset.begins_with("res://")) {
- Error err = _copy_asset(p_out_dir, asset, nullptr, p_is_framework, p_should_embed, r_exported_assets);
+ 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 (ProjectSettings::get_singleton()->localize_path(asset).begins_with("res://")) {
- Error err = _copy_asset(p_out_dir, ProjectSettings::get_singleton()->localize_path(asset), nullptr, p_is_framework, p_should_embed, r_exported_assets);
+ 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
@@ -1243,26 +1309,26 @@ Error EditorExportPlatformIOS::_export_additional_assets(const String &p_out_dir
return OK;
}
-Error EditorExportPlatformIOS::_export_additional_assets(const String &p_out_dir, const Vector<SharedObject> &p_libraries, Vector<IOSExportAsset> &r_exported_assets) {
+Error EditorExportPlatformIOS::_export_additional_assets(const Ref<EditorExportPreset> &p_preset, const String &p_out_dir, const Vector<SharedObject> &p_libraries, Vector<IOSExportAsset> &r_exported_assets) {
Vector<Ref<EditorExportPlugin>> export_plugins = EditorExport::get_singleton()->get_export_plugins();
for (int i = 0; i < export_plugins.size(); i++) {
Vector<String> linked_frameworks = export_plugins[i]->get_ios_frameworks();
- Error err = _export_additional_assets(p_out_dir, linked_frameworks, true, false, r_exported_assets);
+ Error err = _export_additional_assets(p_preset, p_out_dir, linked_frameworks, true, false, r_exported_assets);
ERR_FAIL_COND_V(err, err);
Vector<String> embedded_frameworks = export_plugins[i]->get_ios_embedded_frameworks();
- err = _export_additional_assets(p_out_dir, embedded_frameworks, true, true, r_exported_assets);
+ err = _export_additional_assets(p_preset, p_out_dir, embedded_frameworks, true, true, r_exported_assets);
ERR_FAIL_COND_V(err, err);
Vector<String> project_static_libs = export_plugins[i]->get_ios_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_out_dir, project_static_libs, true, false, r_exported_assets);
+ err = _export_additional_assets(p_preset, p_out_dir, project_static_libs, true, false, r_exported_assets);
ERR_FAIL_COND_V(err, err);
Vector<String> ios_bundle_files = export_plugins[i]->get_ios_bundle_files();
- err = _export_additional_assets(p_out_dir, ios_bundle_files, false, false, r_exported_assets);
+ err = _export_additional_assets(p_preset, p_out_dir, ios_bundle_files, false, false, r_exported_assets);
ERR_FAIL_COND_V(err, err);
}
@@ -1270,7 +1336,7 @@ Error EditorExportPlatformIOS::_export_additional_assets(const String &p_out_dir
for (int i = 0; i < p_libraries.size(); ++i) {
library_paths.push_back(p_libraries[i].path);
}
- Error err = _export_additional_assets(p_out_dir, library_paths, true, true, r_exported_assets);
+ 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;
@@ -1315,7 +1381,7 @@ Error EditorExportPlatformIOS::_export_ios_plugins(const Ref<EditorExportPreset>
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(dest_dir, plugin_main_binary, &plugin_binary_result_file, true, false, r_exported_assets);
+ 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.
@@ -1440,15 +1506,15 @@ Error EditorExportPlatformIOS::_export_ios_plugins(const Ref<EditorExportPreset>
// Export files
{
// Export linked plugin dependency
- err = _export_additional_assets(dest_dir, plugin_linked_dependencies, true, false, r_exported_assets);
+ 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(dest_dir, plugin_embedded_dependencies, true, true, r_exported_assets);
+ 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(dest_dir, plugin_files, false, false, r_exported_assets);
+ err = _export_additional_assets(p_preset, dest_dir, plugin_files, false, false, r_exported_assets);
ERR_FAIL_COND_V(err != OK, err);
}
@@ -1619,6 +1685,8 @@ Error EditorExportPlatformIOS::_export_project_helper(const Ref<EditorExportPres
false
};
+ config_data.plist_content += p_preset->get("application/additional_plist_content").operator String() + "\n";
+
Vector<IOSExportAsset> assets;
Ref<DirAccess> tmp_app_path = DirAccess::create_for_path(dest_dir);
@@ -1824,9 +1892,6 @@ Error EditorExportPlatformIOS::_export_project_helper(const Ref<EditorExportPres
}
{
- bool use_storyboard = p_preset->get("storyboard/use_launch_screen_storyboard");
-
- String launch_image_path = binary_dir + "/Images.xcassets/LaunchImage.launchimage/";
String splash_image_path = binary_dir + "/Images.xcassets/SplashImage.imageset/";
Ref<DirAccess> launch_screen_da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
@@ -1835,30 +1900,11 @@ Error EditorExportPlatformIOS::_export_project_helper(const Ref<EditorExportPres
return ERR_CANT_CREATE;
}
- if (use_storyboard) {
- print_line("Using Launch Storyboard");
+ print_line("Exporting launch screen storyboard");
- if (launch_screen_da->change_dir(launch_image_path) == OK) {
- launch_screen_da->erase_contents_recursive();
- launch_screen_da->remove(launch_image_path);
- }
-
- err = _export_loading_screen_file(p_preset, splash_image_path);
+ err = _export_loading_screen_file(p_preset, splash_image_path);
+ if (err != OK) {
add_message(EXPORT_MESSAGE_ERROR, TTR("Export"), vformat(TTR("Failed to create a file at path \"%s\" with code %d."), splash_image_path, err));
- } else {
- print_line("Using Launch Images");
-
- const String launch_screen_path = binary_dir + "/Launch Screen.storyboard";
-
- launch_screen_da->remove(launch_screen_path);
-
- if (launch_screen_da->change_dir(splash_image_path) == OK) {
- launch_screen_da->erase_contents_recursive();
- launch_screen_da->remove(splash_image_path);
- }
-
- err = _export_loading_screen_images(p_preset, launch_image_path);
- add_message(EXPORT_MESSAGE_ERROR, TTR("Export"), vformat(TTR("Failed to create a file at path \"%s\" with code %d."), launch_image_path, err));
}
}
@@ -1867,8 +1913,8 @@ Error EditorExportPlatformIOS::_export_project_helper(const Ref<EditorExportPres
}
print_line("Exporting additional assets");
- _export_additional_assets(binary_dir, libraries, assets);
- _add_assets_to_project(p_preset, project_file_data, assets);
+ _export_additional_assets(p_preset, binary_dir, libraries, assets);
+ _add_assets_to_project(dest_dir, p_preset, project_file_data, assets);
String project_file_name = binary_dir + ".xcodeproj/project.pbxproj";
{
Ref<FileAccess> f = FileAccess::open(project_file_name, FileAccess::WRITE);
@@ -2010,6 +2056,26 @@ bool EditorExportPlatformIOS::has_valid_export_configuration(const Ref<EditorExp
valid = dvalid || rvalid;
r_missing_templates = !valid;
+ const String &additional_plist_content = p_preset->get("application/additional_plist_content");
+ if (!additional_plist_content.is_empty()) {
+ const String &plist = vformat("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
+ "<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">"
+ "<plist version=\"1.0\">"
+ "<dict>\n"
+ "%s\n"
+ "</dict>\n"
+ "</plist>\n",
+ additional_plist_content);
+
+ String plist_err;
+ Ref<PList> plist_parser;
+ plist_parser.instantiate();
+ if (!plist_parser->load_string(plist, plist_err)) {
+ err += TTR("Invalid additional PList content: ") + plist_err + "\n";
+ valid = false;
+ }
+ }
+
if (!err.is_empty()) {
r_error = err;
}
diff --git a/platform/ios/export/export_plugin.h b/platform/ios/export/export_plugin.h
index fd2243bcda..edbe566dab 100644
--- a/platform/ios/export/export_plugin.h
+++ b/platform/ios/export/export_plugin.h
@@ -127,17 +127,19 @@ class EditorExportPlatformIOS : public EditorExportPlatform {
String _get_linker_flags();
String _get_cpp_code();
void _fix_config_file(const Ref<EditorExportPreset> &p_preset, Vector<uint8_t> &pfile, const IOSConfigData &p_config, bool p_debug);
- Error _export_loading_screen_images(const Ref<EditorExportPreset> &p_preset, const String &p_dest_dir);
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() 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);
- Error _copy_asset(const String &p_out_dir, const String &p_asset, const String *p_custom_file_name, bool p_is_framework, bool p_should_embed, Vector<IOSExportAsset> &r_exported_assets);
- Error _export_additional_assets(const String &p_out_dir, const Vector<SharedObject> &p_libraries, Vector<IOSExportAsset> &r_exported_assets);
+ void _check_xcframework_content(const String &p_path, int &r_total_libs, int &r_static_libs, int &r_dylibs, int &r_frameworks) const;
+ Error _convert_to_framework(const String &p_source, const String &p_destination, const String &p_id) const;
+
+ void _add_assets_to_project(const String &p_out_dir, const Ref<EditorExportPreset> &p_preset, Vector<uint8_t> &p_project_data, const Vector<IOSExportAsset> &p_additional_assets);
+ Error _export_additional_assets(const Ref<EditorExportPreset> &p_preset, const String &p_out_dir, const Vector<String> &p_assets, bool p_is_framework, bool p_should_embed, Vector<IOSExportAsset> &r_exported_assets);
+ Error _copy_asset(const Ref<EditorExportPreset> &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<IOSExportAsset> &r_exported_assets);
+ Error _export_additional_assets(const Ref<EditorExportPreset> &p_preset, const String &p_out_dir, const Vector<SharedObject> &p_libraries, Vector<IOSExportAsset> &r_exported_assets);
Error _export_ios_plugins(const Ref<EditorExportPreset> &p_preset, IOSConfigData &p_config_data, const String &dest_dir, Vector<IOSExportAsset> &r_exported_assets, bool p_debug);
Error _export_project_helper(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags, bool p_simulator, bool p_oneclick);
diff --git a/platform/ios/ios_terminal_logger.h b/platform/ios/ios_terminal_logger.h
new file mode 100644
index 0000000000..7f0bc37a07
--- /dev/null
+++ b/platform/ios/ios_terminal_logger.h
@@ -0,0 +1,45 @@
+/**************************************************************************/
+/* ios_terminal_logger.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. */
+/**************************************************************************/
+
+#ifndef IOS_TERMINAL_LOGGER_H
+#define IOS_TERMINAL_LOGGER_H
+
+#ifdef IOS_ENABLED
+
+#include "core/io/logger.h"
+
+class IOSTerminalLogger : 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) override;
+};
+
+#endif // IOS_ENABLED
+
+#endif // IOS_TERMINAL_LOGGER_H
diff --git a/platform/ios/ios_terminal_logger.mm b/platform/ios/ios_terminal_logger.mm
new file mode 100644
index 0000000000..b4c9821cdc
--- /dev/null
+++ b/platform/ios/ios_terminal_logger.mm
@@ -0,0 +1,74 @@
+/**************************************************************************/
+/* ios_terminal_logger.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. */
+/**************************************************************************/
+
+#include "ios_terminal_logger.h"
+
+#ifdef IOS_ENABLED
+
+#include <os/log.h>
+
+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) {
+ if (!should_log(true)) {
+ return;
+ }
+
+ const char *err_details;
+ if (p_rationale && p_rationale[0]) {
+ err_details = p_rationale;
+ } else {
+ err_details = p_code;
+ }
+
+ switch (p_type) {
+ case ERR_WARNING:
+ os_log_info(OS_LOG_DEFAULT,
+ "WARNING: %{public}s\nat: %{public}s (%{public}s:%i)",
+ err_details, p_function, p_file, p_line);
+ break;
+ case ERR_SCRIPT:
+ os_log_error(OS_LOG_DEFAULT,
+ "SCRIPT ERROR: %{public}s\nat: %{public}s (%{public}s:%i)",
+ err_details, p_function, p_file, p_line);
+ break;
+ case ERR_SHADER:
+ os_log_error(OS_LOG_DEFAULT,
+ "SHADER ERROR: %{public}s\nat: %{public}s (%{public}s:%i)",
+ err_details, p_function, p_file, p_line);
+ break;
+ case ERR_ERROR:
+ default:
+ os_log_error(OS_LOG_DEFAULT,
+ "ERROR: %{public}s\nat: %{public}s (%{public}s:%i)",
+ err_details, p_function, p_file, p_line);
+ break;
+ }
+}
+
+#endif // IOS_ENABLED
diff --git a/platform/ios/os_ios.mm b/platform/ios/os_ios.mm
index a646705305..6ac21fa9c8 100644
--- a/platform/ios/os_ios.mm
+++ b/platform/ios/os_ios.mm
@@ -35,6 +35,7 @@
#import "app_delegate.h"
#import "display_server_ios.h"
#import "godot_view.h"
+#import "ios_terminal_logger.h"
#import "view_controller.h"
#include "core/config/project_settings.h"
@@ -105,12 +106,7 @@ OS_IOS::OS_IOS() {
main_loop = nullptr;
Vector<Logger *> loggers;
- loggers.push_back(memnew(SyslogLogger));
-#ifdef DEBUG_ENABLED
- // it seems iOS app's stdout/stderr is only obtainable if you launch it from
- // Xcode
- loggers.push_back(memnew(StdLogger));
-#endif
+ loggers.push_back(memnew(IOSTerminalLogger));
_set_logger(memnew(CompositeLogger(loggers)));
AudioDriverManager::add_driver(&audio_driver);
@@ -257,6 +253,8 @@ Error OS_IOS::open_dynamic_library(const String p_path, void *&p_library_handle,
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()));
diff --git a/platform/linuxbsd/detect.py b/platform/linuxbsd/detect.py
index 59cc6e7962..eaaaad82b9 100644
--- a/platform/linuxbsd/detect.py
+++ b/platform/linuxbsd/detect.py
@@ -434,7 +434,7 @@ def configure(env: "Environment"):
env.Append(CPPDEFINES=["X11_ENABLED"])
if env["vulkan"]:
- env.Append(CPPDEFINES=["VULKAN_ENABLED"])
+ env.Append(CPPDEFINES=["VULKAN_ENABLED", "RD_ENABLED"])
if not env["use_volk"]:
env.ParseConfig("pkg-config vulkan --cflags --libs")
if not env["builtin_glslang"]:
diff --git a/platform/linuxbsd/export/export_plugin.cpp b/platform/linuxbsd/export/export_plugin.cpp
index 64efcffae3..773b124c6a 100644
--- a/platform/linuxbsd/export/export_plugin.cpp
+++ b/platform/linuxbsd/export/export_plugin.cpp
@@ -36,9 +36,9 @@
#include "core/config/project_settings.h"
#include "editor/editor_node.h"
#include "editor/editor_paths.h"
-#include "editor/editor_scale.h"
#include "editor/editor_string_names.h"
#include "editor/export/editor_export.h"
+#include "editor/themes/editor_scale.h"
#include "modules/modules_enabled.gen.h" // For svg.
#ifdef MODULE_SVG_ENABLED
diff --git a/platform/linuxbsd/freedesktop_portal_desktop.cpp b/platform/linuxbsd/freedesktop_portal_desktop.cpp
index 6a5b5b8064..3641f20c70 100644
--- a/platform/linuxbsd/freedesktop_portal_desktop.cpp
+++ b/platform/linuxbsd/freedesktop_portal_desktop.cpp
@@ -162,7 +162,7 @@ void FreeDesktopPortalDesktop::append_dbus_dict_filters(DBusMessageIter *p_iter,
append_dbus_string(&struct_iter, p_filter_names[i]);
dbus_message_iter_open_container(&struct_iter, DBUS_TYPE_ARRAY, "(us)", &array_iter);
- String flt = p_filter_exts[i];
+ const String &flt = p_filter_exts[i];
int filter_slice_count = flt.get_slice_count(",");
for (int j = 0; j < filter_slice_count; j++) {
dbus_message_iter_open_container(&array_iter, DBUS_TYPE_STRUCT, nullptr, &array_struct_iter);
diff --git a/platform/linuxbsd/os_linuxbsd.cpp b/platform/linuxbsd/os_linuxbsd.cpp
index d22d398a67..aed8574902 100644
--- a/platform/linuxbsd/os_linuxbsd.cpp
+++ b/platform/linuxbsd/os_linuxbsd.cpp
@@ -316,7 +316,7 @@ Vector<String> OS_LinuxBSD::get_video_adapter_driver_info() const {
continue;
}
String device_class = columns[1].trim_suffix(":");
- String vendor_device_id_mapping = columns[2];
+ const String &vendor_device_id_mapping = columns[2];
#ifdef MODULE_REGEX_ENABLED
if (regex_id_format.search(vendor_device_id_mapping).is_null()) {
diff --git a/platform/linuxbsd/x11/display_server_x11.cpp b/platform/linuxbsd/x11/display_server_x11.cpp
index d774a117e8..2fff4ce32c 100644
--- a/platform/linuxbsd/x11/display_server_x11.cpp
+++ b/platform/linuxbsd/x11/display_server_x11.cpp
@@ -2891,10 +2891,15 @@ void DisplayServerX11::window_move_to_foreground(WindowID p_window) {
XFlush(x11_display);
}
+DisplayServerX11::WindowID DisplayServerX11::get_focused_window() const {
+ return last_focused_window;
+}
+
bool DisplayServerX11::window_is_focused(WindowID p_window) const {
_THREAD_SAFE_METHOD_
ERR_FAIL_COND_V(!windows.has(p_window), false);
+
const WindowData &wd = windows[p_window];
return wd.focused;
@@ -3875,7 +3880,7 @@ void DisplayServerX11::_xim_preedit_draw_callback(::XIM xim, ::XPointer client_d
ds->im_selection = Point2i();
}
- OS_Unix::get_singleton()->get_main_loop()->call_deferred(SNAME("notification"), MainLoop::NOTIFICATION_OS_IME_UPDATE);
+ callable_mp((Object *)OS_Unix::get_singleton()->get_main_loop(), &Object::notification).call_deferred(MainLoop::NOTIFICATION_OS_IME_UPDATE, false);
}
}
diff --git a/platform/linuxbsd/x11/display_server_x11.h b/platform/linuxbsd/x11/display_server_x11.h
index 3ecc4dca3f..3b362e5c22 100644
--- a/platform/linuxbsd/x11/display_server_x11.h
+++ b/platform/linuxbsd/x11/display_server_x11.h
@@ -492,6 +492,8 @@ public:
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 WindowID get_focused_window() const override;
+
virtual bool window_can_draw(WindowID p_window = MAIN_WINDOW_ID) const override;
virtual bool can_any_window_draw() const override;
diff --git a/platform/linuxbsd/x11/key_mapping_x11.cpp b/platform/linuxbsd/x11/key_mapping_x11.cpp
index 0f709872cb..c0e6b91d57 100644
--- a/platform/linuxbsd/x11/key_mapping_x11.cpp
+++ b/platform/linuxbsd/x11/key_mapping_x11.cpp
@@ -88,7 +88,6 @@ void KeyMappingX11::initialize() {
xkeysym_map[XK_KP_Equal] = Key::EQUAL;
xkeysym_map[XK_KP_Separator] = Key::COMMA;
xkeysym_map[XK_KP_Decimal] = Key::KP_PERIOD;
- xkeysym_map[XK_KP_Delete] = Key::KP_PERIOD;
xkeysym_map[XK_KP_Multiply] = Key::KP_MULTIPLY;
xkeysym_map[XK_KP_Divide] = Key::KP_DIVIDE;
xkeysym_map[XK_KP_Subtract] = Key::KP_SUBTRACT;
@@ -105,6 +104,7 @@ void KeyMappingX11::initialize() {
xkeysym_map[XK_KP_9] = Key::KP_9;
// Same keys but with numlock off.
xkeysym_map[XK_KP_Insert] = Key::INSERT;
+ xkeysym_map[XK_KP_Delete] = Key::KEY_DELETE;
xkeysym_map[XK_KP_End] = Key::END;
xkeysym_map[XK_KP_Down] = Key::DOWN;
xkeysym_map[XK_KP_Page_Down] = Key::PAGEDOWN;
diff --git a/platform/macos/detect.py b/platform/macos/detect.py
index b41d2141fb..bc14d233bb 100644
--- a/platform/macos/detect.py
+++ b/platform/macos/detect.py
@@ -67,21 +67,30 @@ def get_mvk_sdk_path():
if not os.path.exists(dirname):
return ""
- ver_file = "0.0.0.0"
- ver_num = ver_parse(ver_file)
-
+ ver_num = ver_parse("0.0.0.0")
files = os.listdir(dirname)
+ lib_name_out = dirname
for file in files:
if os.path.isdir(os.path.join(dirname, file)):
ver_comp = ver_parse(file)
- lib_name = os.path.join(
- os.path.join(dirname, file), "MoltenVK/MoltenVK.xcframework/macos-arm64_x86_64/libMoltenVK.a"
- )
- if os.path.isfile(lib_name) and ver_comp > ver_num:
- ver_num = ver_comp
- ver_file = file
+ if ver_comp > ver_num:
+ # Try new SDK location.
+ lib_name = os.path.join(
+ os.path.join(dirname, file), "macOS/lib/MoltenVK.xcframework/macos-arm64_x86_64/"
+ )
+ if os.path.isfile(os.path.join(lib_name, "libMoltenVK.a")):
+ ver_num = ver_comp
+ lib_name_out = lib_name
+ else:
+ # Try old SDK location.
+ lib_name = os.path.join(
+ os.path.join(dirname, file), "MoltenVK/MoltenVK.xcframework/macos-arm64_x86_64/"
+ )
+ if os.path.isfile(os.path.join(lib_name, "libMoltenVK.a")):
+ ver_num = ver_comp
+ lib_name_out = lib_name
- return os.path.join(os.path.join(dirname, ver_file), "MoltenVK/MoltenVK.xcframework/macos-arm64_x86_64/")
+ return lib_name_out
def configure(env: "Environment"):
@@ -260,7 +269,7 @@ def configure(env: "Environment"):
env.Append(LINKFLAGS=["-rpath", "@executable_path/../Frameworks", "-rpath", "@executable_path"])
if env["vulkan"]:
- env.Append(CPPDEFINES=["VULKAN_ENABLED"])
+ env.Append(CPPDEFINES=["VULKAN_ENABLED", "RD_ENABLED"])
env.Append(LINKFLAGS=["-framework", "Metal", "-framework", "IOSurface"])
if not env["use_volk"]:
env.Append(LINKFLAGS=["-lMoltenVK"])
@@ -272,6 +281,12 @@ def configure(env: "Environment"):
mvk_list.insert(
0,
os.path.join(
+ os.path.expanduser(env["vulkan_sdk_path"]), "macOS/lib/MoltenVK.xcframework/macos-arm64_x86_64/"
+ ),
+ )
+ mvk_list.insert(
+ 0,
+ os.path.join(
os.path.expanduser(env["vulkan_sdk_path"]), "MoltenVK/MoltenVK.xcframework/macos-arm64_x86_64/"
),
)
diff --git a/platform/macos/display_server_macos.h b/platform/macos/display_server_macos.h
index f8fd0f93ef..cc343a6d64 100644
--- a/platform/macos/display_server_macos.h
+++ b/platform/macos/display_server_macos.h
@@ -428,6 +428,8 @@ public:
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 WindowID get_focused_window() const override;
+
virtual bool window_can_draw(WindowID p_window = MAIN_WINDOW_ID) const override;
virtual bool can_any_window_draw() const override;
diff --git a/platform/macos/display_server_macos.mm b/platform/macos/display_server_macos.mm
index bd92b7d472..378688f78a 100644
--- a/platform/macos/display_server_macos.mm
+++ b/platform/macos/display_server_macos.mm
@@ -3723,6 +3723,10 @@ bool DisplayServerMacOS::window_is_focused(WindowID p_window) const {
return wd.focused;
}
+DisplayServerMacOS::WindowID DisplayServerMacOS::get_focused_window() const {
+ return last_focused_window;
+}
+
bool DisplayServerMacOS::window_can_draw(WindowID p_window) const {
return windows[p_window].is_visible;
}
diff --git a/platform/macos/doc_classes/EditorExportPlatformMacOS.xml b/platform/macos/doc_classes/EditorExportPlatformMacOS.xml
index 85fefe65c0..506b0dffb8 100644
--- a/platform/macos/doc_classes/EditorExportPlatformMacOS.xml
+++ b/platform/macos/doc_classes/EditorExportPlatformMacOS.xml
@@ -10,6 +10,13 @@
<link title="Running Godot apps on macOS">$DOCS_URL/tutorials//export/running_on_macos.html</link>
</tutorials>
<members>
+ <member name="application/additional_plist_content" type="String" setter="" getter="">
+ Additional data added to the root [code]&lt;dict&gt;[/code] section of the [url=https://developer.apple.com/documentation/bundleresources/information_property_list]Info.plist[/url] file. The value should be an XML section with pairs of key-value elements, e.g.:
+ [codeblock]
+ &lt;key&gt;key_name&lt;/key&gt;
+ &lt;string&gt;value&lt;/string&gt;
+ [/codeblock]
+ </member>
<member name="application/app_category" type="String" setter="" getter="">
Application category for the App Store.
</member>
diff --git a/platform/macos/export/codesign.cpp b/platform/macos/export/codesign.cpp
index 2b8898e6a1..082c0abea7 100644
--- a/platform/macos/export/codesign.cpp
+++ b/platform/macos/export/codesign.cpp
@@ -32,8 +32,8 @@
#include "lipo.h"
#include "macho.h"
-#include "plist.h"
+#include "core/io/plist.h"
#include "core/os/os.h"
#include "editor/editor_paths.h"
#include "editor/editor_settings.h"
diff --git a/platform/macos/export/codesign.h b/platform/macos/export/codesign.h
index 3e61751a96..49d53b376e 100644
--- a/platform/macos/export/codesign.h
+++ b/platform/macos/export/codesign.h
@@ -41,11 +41,10 @@
// - Requirements code generator is not implemented (only hard-coded requirements for the ad-hoc signing is supported).
// - RFC5652/CMS blob generation is not implemented, supports ad-hoc signing only.
-#include "plist.h"
-
#include "core/crypto/crypto_core.h"
#include "core/io/dir_access.h"
#include "core/io/file_access.h"
+#include "core/io/plist.h"
#include "core/object/ref_counted.h"
#include "modules/modules_enabled.gen.h" // For regex.
diff --git a/platform/macos/export/export_plugin.cpp b/platform/macos/export/export_plugin.cpp
index 6f9db1427b..7ed78db6f8 100644
--- a/platform/macos/export/export_plugin.cpp
+++ b/platform/macos/export/export_plugin.cpp
@@ -37,13 +37,14 @@
#include "run_icon_svg.gen.h"
#include "core/io/image_loader.h"
+#include "core/io/plist.h"
#include "core/string/translation.h"
#include "drivers/png/png_driver_common.h"
#include "editor/editor_node.h"
#include "editor/editor_paths.h"
-#include "editor/editor_scale.h"
#include "editor/editor_string_names.h"
#include "editor/import/resource_importer_texture_settings.h"
+#include "editor/themes/editor_scale.h"
#include "scene/resources/image_texture.h"
#include "modules/modules_enabled.gen.h" // For svg and regex.
@@ -388,6 +389,8 @@ void EditorExportPlatformMacOS::get_export_options(List<ExportOption> *r_options
r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "application/export_angle", PROPERTY_HINT_ENUM, "Auto,Yes,No"), 0, true));
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "display/high_res"), true));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/additional_plist_content", PROPERTY_HINT_MULTILINE_TEXT), ""));
+
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "xcode/platform_build"), "14C18"));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "xcode/sdk_version"), "13.1"));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "xcode/sdk_build"), "22C55"));
@@ -672,6 +675,8 @@ void EditorExportPlatformMacOS::_fix_plist(const Ref<EditorExportPreset> &p_pres
strnew += lines[i].replace("$min_version", p_preset->get("application/min_macos_version")) + "\n";
} else if (lines[i].find("$highres") != -1) {
strnew += lines[i].replace("$highres", p_preset->get("display/high_res") ? "\t<true/>" : "\t<false/>") + "\n";
+ } else if (lines[i].find("$additional_plist_content") != -1) {
+ strnew += lines[i].replace("$additional_plist_content", p_preset->get("application/additional_plist_content")) + "\n";
} else if (lines[i].find("$platfbuild") != -1) {
strnew += lines[i].replace("$platfbuild", p_preset->get("xcode/platform_build")) + "\n";
} else if (lines[i].find("$sdkver") != -1) {
@@ -2051,13 +2056,17 @@ bool EditorExportPlatformMacOS::has_valid_export_configuration(const Ref<EditorE
String architecture = p_preset->get("binary_format/architecture");
if (architecture == "universal" || architecture == "x86_64") {
if (!ResourceImporterTextureSettings::should_import_s3tc_bptc()) {
+ err += TTR("Cannot export for universal or x86_64 if S3TC BPTC texture format is disabled. Enable it in the Project Settings (Rendering > Textures > VRAM Compression > Import S3TC BPTC).") + "\n";
valid = false;
}
- } else if (architecture == "arm64") {
+ }
+ if (architecture == "universal" || architecture == "arm64") {
if (!ResourceImporterTextureSettings::should_import_etc2_astc()) {
+ err += TTR("Cannot export for universal or arm64 if ETC2 ASTC texture format is disabled. Enable it in the Project Settings (Rendering > Textures > VRAM Compression > Import ETC2 ASTC).") + "\n";
valid = false;
}
- } else {
+ }
+ if (architecture != "universal" && architecture != "x86_64" && architecture != "arm64") {
ERR_PRINT("Invalid architecture");
}
@@ -2091,6 +2100,26 @@ bool EditorExportPlatformMacOS::has_valid_project_configuration(const Ref<Editor
};
}
+ const String &additional_plist_content = p_preset->get("application/additional_plist_content");
+ if (!additional_plist_content.is_empty()) {
+ const String &plist = vformat("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
+ "<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">"
+ "<plist version=\"1.0\">"
+ "<dict>\n"
+ "%s\n"
+ "</dict>\n"
+ "</plist>\n",
+ additional_plist_content);
+
+ String plist_err;
+ Ref<PList> plist_parser;
+ plist_parser.instantiate();
+ if (!plist_parser->load_string(plist, plist_err)) {
+ err += TTR("Invalid additional PList content: ") + plist_err + "\n";
+ valid = false;
+ }
+ }
+
List<ExportOption> options;
get_export_options(&options);
for (const EditorExportPlatform::ExportOption &E : options) {
diff --git a/platform/macos/export/plist.cpp b/platform/macos/export/plist.cpp
deleted file mode 100644
index f494c58fc9..0000000000
--- a/platform/macos/export/plist.cpp
+++ /dev/null
@@ -1,848 +0,0 @@
-/**************************************************************************/
-/* plist.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 "plist.h"
-
-PList::PLNodeType PListNode::get_type() const {
- return data_type;
-}
-
-Variant PListNode::get_value() const {
- switch (data_type) {
- case PList::PL_NODE_TYPE_NIL: {
- return Variant();
- } break;
- case PList::PL_NODE_TYPE_STRING: {
- return String::utf8(data_string.get_data());
- } break;
- case PList::PL_NODE_TYPE_ARRAY: {
- Array arr;
- for (const Ref<PListNode> &E : data_array) {
- arr.push_back(E);
- }
- return arr;
- } break;
- case PList::PL_NODE_TYPE_DICT: {
- Dictionary dict;
- for (const KeyValue<String, Ref<PListNode>> &E : data_dict) {
- dict[E.key] = E.value;
- }
- return dict;
- } break;
- case PList::PL_NODE_TYPE_BOOLEAN: {
- return data_bool;
- } break;
- case PList::PL_NODE_TYPE_INTEGER: {
- return data_int;
- } break;
- case PList::PL_NODE_TYPE_REAL: {
- return data_real;
- } break;
- case PList::PL_NODE_TYPE_DATA: {
- int strlen = data_string.length();
-
- size_t arr_len = 0;
- Vector<uint8_t> buf;
- {
- buf.resize(strlen / 4 * 3 + 1);
- uint8_t *w = buf.ptrw();
-
- ERR_FAIL_COND_V(CryptoCore::b64_decode(&w[0], buf.size(), &arr_len, (unsigned char *)data_string.get_data(), strlen) != OK, Vector<uint8_t>());
- }
- buf.resize(arr_len);
- return buf;
- } break;
- case PList::PL_NODE_TYPE_DATE: {
- return String(data_string.get_data());
- } break;
- }
- return Variant();
-}
-
-Ref<PListNode> PListNode::new_node(const Variant &p_value) {
- Ref<PListNode> node;
- node.instantiate();
-
- switch (p_value.get_type()) {
- case Variant::NIL: {
- node->data_type = PList::PL_NODE_TYPE_NIL;
- } break;
- case Variant::BOOL: {
- node->data_type = PList::PL_NODE_TYPE_BOOLEAN;
- node->data_bool = p_value;
- } break;
- case Variant::INT: {
- node->data_type = PList::PL_NODE_TYPE_INTEGER;
- node->data_int = p_value;
- } break;
- case Variant::FLOAT: {
- node->data_type = PList::PL_NODE_TYPE_REAL;
- node->data_real = p_value;
- } break;
- case Variant::STRING_NAME:
- case Variant::STRING: {
- node->data_type = PList::PL_NODE_TYPE_STRING;
- node->data_string = p_value.operator String().utf8();
- } break;
- case Variant::DICTIONARY: {
- node->data_type = PList::PL_NODE_TYPE_DICT;
- Dictionary dict = p_value;
- const Variant *next = dict.next(nullptr);
- while (next) {
- Ref<PListNode> sub_node = dict[*next];
- ERR_FAIL_COND_V_MSG(sub_node.is_null(), Ref<PListNode>(), "Invalid dictionary element, should be PListNode.");
- node->data_dict[*next] = sub_node;
- next = dict.next(next);
- }
- } break;
- case Variant::ARRAY: {
- node->data_type = PList::PL_NODE_TYPE_ARRAY;
- Array ar = p_value;
- for (int i = 0; i < ar.size(); i++) {
- Ref<PListNode> sub_node = ar[i];
- ERR_FAIL_COND_V_MSG(sub_node.is_null(), Ref<PListNode>(), "Invalid array element, should be PListNode.");
- node->data_array.push_back(sub_node);
- }
- } break;
- case Variant::PACKED_BYTE_ARRAY: {
- node->data_type = PList::PL_NODE_TYPE_DATA;
- PackedByteArray buf = p_value;
- node->data_string = CryptoCore::b64_encode_str(buf.ptr(), buf.size()).utf8();
- } break;
- default: {
- ERR_FAIL_V_MSG(Ref<PListNode>(), "Unsupported data type.");
- } break;
- }
- return node;
-}
-
-Ref<PListNode> PListNode::new_array() {
- Ref<PListNode> node = memnew(PListNode());
- ERR_FAIL_COND_V(node.is_null(), Ref<PListNode>());
- node->data_type = PList::PLNodeType::PL_NODE_TYPE_ARRAY;
- return node;
-}
-
-Ref<PListNode> PListNode::new_dict() {
- Ref<PListNode> node = memnew(PListNode());
- ERR_FAIL_COND_V(node.is_null(), Ref<PListNode>());
- node->data_type = PList::PLNodeType::PL_NODE_TYPE_DICT;
- return node;
-}
-
-Ref<PListNode> PListNode::new_string(const String &p_string) {
- Ref<PListNode> node = memnew(PListNode());
- ERR_FAIL_COND_V(node.is_null(), Ref<PListNode>());
- node->data_type = PList::PLNodeType::PL_NODE_TYPE_STRING;
- node->data_string = p_string.utf8();
- return node;
-}
-
-Ref<PListNode> PListNode::new_data(const String &p_string) {
- Ref<PListNode> node = memnew(PListNode());
- ERR_FAIL_COND_V(node.is_null(), Ref<PListNode>());
- node->data_type = PList::PLNodeType::PL_NODE_TYPE_DATA;
- node->data_string = p_string.utf8();
- return node;
-}
-
-Ref<PListNode> PListNode::new_date(const String &p_string) {
- Ref<PListNode> node = memnew(PListNode());
- ERR_FAIL_COND_V(node.is_null(), Ref<PListNode>());
- node->data_type = PList::PLNodeType::PL_NODE_TYPE_DATE;
- node->data_string = p_string.utf8();
- node->data_real = (double)Time::get_singleton()->get_unix_time_from_datetime_string(p_string) - 978307200.0;
- return node;
-}
-
-Ref<PListNode> PListNode::new_bool(bool p_bool) {
- Ref<PListNode> node = memnew(PListNode());
- ERR_FAIL_COND_V(node.is_null(), Ref<PListNode>());
- node->data_type = PList::PLNodeType::PL_NODE_TYPE_BOOLEAN;
- node->data_bool = p_bool;
- return node;
-}
-
-Ref<PListNode> PListNode::new_int(int64_t p_int) {
- Ref<PListNode> node = memnew(PListNode());
- ERR_FAIL_COND_V(node.is_null(), Ref<PListNode>());
- node->data_type = PList::PLNodeType::PL_NODE_TYPE_INTEGER;
- node->data_int = p_int;
- return node;
-}
-
-Ref<PListNode> PListNode::new_real(double p_real) {
- Ref<PListNode> node = memnew(PListNode());
- ERR_FAIL_COND_V(node.is_null(), Ref<PListNode>());
- node->data_type = PList::PLNodeType::PL_NODE_TYPE_REAL;
- node->data_real = p_real;
- return node;
-}
-
-bool PListNode::push_subnode(const Ref<PListNode> &p_node, const String &p_key) {
- ERR_FAIL_COND_V(p_node.is_null(), false);
- if (data_type == PList::PLNodeType::PL_NODE_TYPE_DICT) {
- ERR_FAIL_COND_V(p_key.is_empty(), false);
- ERR_FAIL_COND_V(data_dict.has(p_key), false);
- data_dict[p_key] = p_node;
- return true;
- } else if (data_type == PList::PLNodeType::PL_NODE_TYPE_ARRAY) {
- data_array.push_back(p_node);
- return true;
- } else {
- ERR_FAIL_V_MSG(false, "PList: Invalid parent node type, should be DICT or ARRAY.");
- }
-}
-
-size_t PListNode::get_asn1_size(uint8_t p_len_octets) const {
- // Get size of all data, excluding type and size information.
- switch (data_type) {
- case PList::PLNodeType::PL_NODE_TYPE_NIL: {
- return 0;
- } break;
- case PList::PLNodeType::PL_NODE_TYPE_DATA:
- case PList::PLNodeType::PL_NODE_TYPE_DATE: {
- ERR_FAIL_V_MSG(0, "PList: DATE and DATA nodes are not supported by ASN.1 serialization.");
- } break;
- case PList::PLNodeType::PL_NODE_TYPE_STRING: {
- return data_string.length();
- } break;
- case PList::PLNodeType::PL_NODE_TYPE_BOOLEAN: {
- return 1;
- } break;
- case PList::PLNodeType::PL_NODE_TYPE_INTEGER:
- case PList::PLNodeType::PL_NODE_TYPE_REAL: {
- return 4;
- } break;
- case PList::PLNodeType::PL_NODE_TYPE_ARRAY: {
- size_t size = 0;
- for (int i = 0; i < data_array.size(); i++) {
- size += 1 + _asn1_size_len(p_len_octets) + data_array[i]->get_asn1_size(p_len_octets);
- }
- return size;
- } break;
- case PList::PLNodeType::PL_NODE_TYPE_DICT: {
- size_t size = 0;
-
- for (const KeyValue<String, Ref<PListNode>> &E : data_dict) {
- size += 1 + _asn1_size_len(p_len_octets); // Sequence.
- size += 1 + _asn1_size_len(p_len_octets) + E.key.utf8().length(); //Key.
- size += 1 + _asn1_size_len(p_len_octets) + E.value->get_asn1_size(p_len_octets); // Value.
- }
- return size;
- } break;
- default: {
- return 0;
- } break;
- }
-}
-
-int PListNode::_asn1_size_len(uint8_t p_len_octets) {
- if (p_len_octets > 1) {
- return p_len_octets + 1;
- } else {
- return 1;
- }
-}
-
-void PListNode::store_asn1_size(PackedByteArray &p_stream, uint8_t p_len_octets) const {
- uint32_t size = get_asn1_size(p_len_octets);
- if (p_len_octets > 1) {
- p_stream.push_back(0x80 + p_len_octets);
- }
- for (int i = p_len_octets - 1; i >= 0; i--) {
- uint8_t x = (size >> i * 8) & 0xFF;
- p_stream.push_back(x);
- }
-}
-
-bool PListNode::store_asn1(PackedByteArray &p_stream, uint8_t p_len_octets) const {
- // Convert to binary ASN1 stream.
- bool valid = true;
- switch (data_type) {
- case PList::PLNodeType::PL_NODE_TYPE_NIL: {
- // Nothing to store.
- } break;
- case PList::PLNodeType::PL_NODE_TYPE_DATE:
- case PList::PLNodeType::PL_NODE_TYPE_DATA: {
- ERR_FAIL_V_MSG(false, "PList: DATE and DATA nodes are not supported by ASN.1 serialization.");
- } break;
- case PList::PLNodeType::PL_NODE_TYPE_STRING: {
- p_stream.push_back(0x0C);
- store_asn1_size(p_stream, p_len_octets);
- for (int i = 0; i < data_string.size(); i++) {
- p_stream.push_back(data_string[i]);
- }
- } break;
- case PList::PLNodeType::PL_NODE_TYPE_BOOLEAN: {
- p_stream.push_back(0x01);
- store_asn1_size(p_stream, p_len_octets);
- if (data_bool) {
- p_stream.push_back(0x01);
- } else {
- p_stream.push_back(0x00);
- }
- } break;
- case PList::PLNodeType::PL_NODE_TYPE_INTEGER: {
- p_stream.push_back(0x02);
- store_asn1_size(p_stream, p_len_octets);
- for (int i = 4; i >= 0; i--) {
- uint8_t x = (data_int >> i * 8) & 0xFF;
- p_stream.push_back(x);
- }
- } break;
- case PList::PLNodeType::PL_NODE_TYPE_REAL: {
- p_stream.push_back(0x03);
- store_asn1_size(p_stream, p_len_octets);
- for (int i = 4; i >= 0; i--) {
- uint8_t x = (data_int >> i * 8) & 0xFF;
- p_stream.push_back(x);
- }
- } break;
- case PList::PLNodeType::PL_NODE_TYPE_ARRAY: {
- p_stream.push_back(0x30); // Sequence.
- store_asn1_size(p_stream, p_len_octets);
- for (int i = 0; i < data_array.size(); i++) {
- valid = valid && data_array[i]->store_asn1(p_stream, p_len_octets);
- }
- } break;
- case PList::PLNodeType::PL_NODE_TYPE_DICT: {
- p_stream.push_back(0x31); // Set.
- store_asn1_size(p_stream, p_len_octets);
- for (const KeyValue<String, Ref<PListNode>> &E : data_dict) {
- CharString cs = E.key.utf8();
- uint32_t size = cs.length();
-
- // Sequence.
- p_stream.push_back(0x30);
- uint32_t seq_size = 2 * (1 + _asn1_size_len(p_len_octets)) + size + E.value->get_asn1_size(p_len_octets);
- if (p_len_octets > 1) {
- p_stream.push_back(0x80 + p_len_octets);
- }
- for (int i = p_len_octets - 1; i >= 0; i--) {
- uint8_t x = (seq_size >> i * 8) & 0xFF;
- p_stream.push_back(x);
- }
- // Key.
- p_stream.push_back(0x0C);
- if (p_len_octets > 1) {
- p_stream.push_back(0x80 + p_len_octets);
- }
- for (int i = p_len_octets - 1; i >= 0; i--) {
- uint8_t x = (size >> i * 8) & 0xFF;
- p_stream.push_back(x);
- }
- for (uint32_t i = 0; i < size; i++) {
- p_stream.push_back(cs[i]);
- }
- // Value.
- valid = valid && E.value->store_asn1(p_stream, p_len_octets);
- }
- } break;
- }
- return valid;
-}
-
-void PListNode::store_text(String &p_stream, uint8_t p_indent) const {
- // Convert to text XML stream.
- switch (data_type) {
- case PList::PLNodeType::PL_NODE_TYPE_NIL: {
- // Nothing to store.
- } break;
- case PList::PLNodeType::PL_NODE_TYPE_DATA: {
- p_stream += String("\t").repeat(p_indent);
- p_stream += "<data>\n";
- p_stream += String("\t").repeat(p_indent);
- p_stream += data_string + "\n";
- p_stream += String("\t").repeat(p_indent);
- p_stream += "</data>\n";
- } break;
- case PList::PLNodeType::PL_NODE_TYPE_DATE: {
- p_stream += String("\t").repeat(p_indent);
- p_stream += "<date>";
- p_stream += data_string;
- p_stream += "</date>\n";
- } break;
- case PList::PLNodeType::PL_NODE_TYPE_STRING: {
- p_stream += String("\t").repeat(p_indent);
- p_stream += "<string>";
- p_stream += String::utf8(data_string);
- p_stream += "</string>\n";
- } break;
- case PList::PLNodeType::PL_NODE_TYPE_BOOLEAN: {
- p_stream += String("\t").repeat(p_indent);
- if (data_bool) {
- p_stream += "<true/>\n";
- } else {
- p_stream += "<false/>\n";
- }
- } break;
- case PList::PLNodeType::PL_NODE_TYPE_INTEGER: {
- p_stream += String("\t").repeat(p_indent);
- p_stream += "<integer>";
- p_stream += itos(data_int);
- p_stream += "</integer>\n";
- } break;
- case PList::PLNodeType::PL_NODE_TYPE_REAL: {
- p_stream += String("\t").repeat(p_indent);
- p_stream += "<real>";
- p_stream += rtos(data_real);
- p_stream += "</real>\n";
- } break;
- case PList::PLNodeType::PL_NODE_TYPE_ARRAY: {
- p_stream += String("\t").repeat(p_indent);
- p_stream += "<array>\n";
- for (int i = 0; i < data_array.size(); i++) {
- data_array[i]->store_text(p_stream, p_indent + 1);
- }
- p_stream += String("\t").repeat(p_indent);
- p_stream += "</array>\n";
- } break;
- case PList::PLNodeType::PL_NODE_TYPE_DICT: {
- p_stream += String("\t").repeat(p_indent);
- p_stream += "<dict>\n";
- for (const KeyValue<String, Ref<PListNode>> &E : data_dict) {
- p_stream += String("\t").repeat(p_indent + 1);
- p_stream += "<key>";
- p_stream += E.key;
- p_stream += "</key>\n";
- E.value->store_text(p_stream, p_indent + 1);
- }
- p_stream += String("\t").repeat(p_indent);
- p_stream += "</dict>\n";
- } break;
- }
-}
-
-/*************************************************************************/
-
-PList::PList() {
- root = PListNode::new_dict();
-}
-
-PList::PList(const String &p_string) {
- load_string(p_string);
-}
-
-uint64_t PList::read_bplist_var_size_int(Ref<FileAccess> p_file, uint8_t p_size) {
- uint64_t pos = p_file->get_position();
- uint64_t ret = 0;
- switch (p_size) {
- case 1: {
- ret = p_file->get_8();
- } break;
- case 2: {
- ret = BSWAP16(p_file->get_16());
- } break;
- case 3: {
- ret = BSWAP32(p_file->get_32() & 0x00FFFFFF);
- } break;
- case 4: {
- ret = BSWAP32(p_file->get_32());
- } break;
- case 5: {
- ret = BSWAP64(p_file->get_64() & 0x000000FFFFFFFFFF);
- } break;
- case 6: {
- ret = BSWAP64(p_file->get_64() & 0x0000FFFFFFFFFFFF);
- } break;
- case 7: {
- ret = BSWAP64(p_file->get_64() & 0x00FFFFFFFFFFFFFF);
- } break;
- case 8: {
- ret = BSWAP64(p_file->get_64());
- } break;
- default: {
- ret = 0;
- }
- }
- p_file->seek(pos + p_size);
-
- return ret;
-}
-
-Ref<PListNode> PList::read_bplist_obj(Ref<FileAccess> p_file, uint64_t p_offset_idx) {
- Ref<PListNode> node;
- node.instantiate();
-
- uint64_t ot_off = trailer.offset_table_start + p_offset_idx * trailer.offset_size;
- p_file->seek(ot_off);
- uint64_t marker_off = read_bplist_var_size_int(p_file, trailer.offset_size);
- ERR_FAIL_COND_V_MSG(marker_off == 0, Ref<PListNode>(), "Invalid marker size.");
-
- p_file->seek(marker_off);
- uint8_t marker = p_file->get_8();
- uint8_t marker_type = marker & 0xF0;
- uint64_t marker_size = marker & 0x0F;
-
- switch (marker_type) {
- case 0x00: {
- if (marker_size == 0x00) {
- node->data_type = PL_NODE_TYPE_NIL;
- } else if (marker_size == 0x08) {
- node->data_type = PL_NODE_TYPE_BOOLEAN;
- node->data_bool = false;
- } else if (marker_size == 0x09) {
- node->data_type = PL_NODE_TYPE_BOOLEAN;
- node->data_bool = true;
- } else {
- ERR_FAIL_V_MSG(Ref<PListNode>(), "Invalid nil/bool marker value.");
- }
- } break;
- case 0x10: {
- node->data_type = PL_NODE_TYPE_INTEGER;
- node->data_int = static_cast<int64_t>(read_bplist_var_size_int(p_file, pow(2, marker_size)));
- } break;
- case 0x20: {
- node->data_type = PL_NODE_TYPE_REAL;
- node->data_int = static_cast<int64_t>(read_bplist_var_size_int(p_file, pow(2, marker_size)));
- } break;
- case 0x30: {
- node->data_type = PL_NODE_TYPE_DATE;
- node->data_int = BSWAP64(p_file->get_64());
- node->data_string = Time::get_singleton()->get_datetime_string_from_unix_time(node->data_real + 978307200.0).utf8();
- } break;
- case 0x40: {
- if (marker_size == 0x0F) {
- uint8_t ext = p_file->get_8() & 0xF;
- marker_size = read_bplist_var_size_int(p_file, pow(2, ext));
- }
- node->data_type = PL_NODE_TYPE_DATA;
- PackedByteArray buf;
- buf.resize(marker_size + 1);
- p_file->get_buffer(reinterpret_cast<uint8_t *>(buf.ptrw()), marker_size);
- node->data_string = CryptoCore::b64_encode_str(buf.ptr(), buf.size()).utf8();
- } break;
- case 0x50: {
- if (marker_size == 0x0F) {
- uint8_t ext = p_file->get_8() & 0xF;
- marker_size = read_bplist_var_size_int(p_file, pow(2, ext));
- }
- node->data_type = PL_NODE_TYPE_STRING;
- node->data_string.resize(marker_size + 1);
- p_file->get_buffer(reinterpret_cast<uint8_t *>(node->data_string.ptrw()), marker_size);
- } break;
- case 0x60: {
- if (marker_size == 0x0F) {
- uint8_t ext = p_file->get_8() & 0xF;
- marker_size = read_bplist_var_size_int(p_file, pow(2, ext));
- }
- Char16String cs16;
- cs16.resize(marker_size + 1);
- for (uint64_t i = 0; i < marker_size; i++) {
- cs16[i] = BSWAP16(p_file->get_16());
- }
- node->data_type = PL_NODE_TYPE_STRING;
- node->data_string = String::utf16(cs16.ptr(), cs16.length()).utf8();
- } break;
- case 0x80: {
- node->data_type = PL_NODE_TYPE_INTEGER;
- node->data_int = static_cast<int64_t>(read_bplist_var_size_int(p_file, marker_size + 1));
- } break;
- case 0xA0:
- case 0xC0: {
- if (marker_size == 0x0F) {
- uint8_t ext = p_file->get_8() & 0xF;
- marker_size = read_bplist_var_size_int(p_file, pow(2, ext));
- }
- uint64_t pos = p_file->get_position();
-
- node->data_type = PL_NODE_TYPE_ARRAY;
- for (uint64_t i = 0; i < marker_size; i++) {
- p_file->seek(pos + trailer.ref_size * i);
- uint64_t ref = read_bplist_var_size_int(p_file, trailer.ref_size);
-
- Ref<PListNode> element = read_bplist_obj(p_file, ref);
- ERR_FAIL_COND_V(element.is_null(), Ref<PListNode>());
- node->data_array.push_back(element);
- }
- } break;
- case 0xD0: {
- if (marker_size == 0x0F) {
- uint8_t ext = p_file->get_8() & 0xF;
- marker_size = read_bplist_var_size_int(p_file, pow(2, ext));
- }
- uint64_t pos = p_file->get_position();
-
- node->data_type = PL_NODE_TYPE_DICT;
- for (uint64_t i = 0; i < marker_size; i++) {
- p_file->seek(pos + trailer.ref_size * i);
- uint64_t key_ref = read_bplist_var_size_int(p_file, trailer.ref_size);
-
- p_file->seek(pos + trailer.ref_size * (i + marker_size));
- uint64_t obj_ref = read_bplist_var_size_int(p_file, trailer.ref_size);
-
- Ref<PListNode> element_key = read_bplist_obj(p_file, key_ref);
- ERR_FAIL_COND_V(element_key.is_null() || element_key->data_type != PL_NODE_TYPE_STRING, Ref<PListNode>());
- Ref<PListNode> element = read_bplist_obj(p_file, obj_ref);
- ERR_FAIL_COND_V(element.is_null(), Ref<PListNode>());
- node->data_dict[String::utf8(element_key->data_string.ptr(), element_key->data_string.length())] = element;
- }
- } break;
- default: {
- ERR_FAIL_V_MSG(Ref<PListNode>(), "Invalid marker type.");
- }
- }
- return node;
-}
-
-bool PList::load_file(const String &p_filename) {
- root = Ref<PListNode>();
-
- Ref<FileAccess> fb = FileAccess::open(p_filename, FileAccess::READ);
- if (fb.is_null()) {
- return false;
- }
-
- unsigned char magic[8];
- fb->get_buffer(magic, 8);
-
- if (String((const char *)magic, 8) == "bplist00") {
- fb->seek_end(-26);
- trailer.offset_size = fb->get_8();
- trailer.ref_size = fb->get_8();
- trailer.object_num = BSWAP64(fb->get_64());
- trailer.root_offset_idx = BSWAP64(fb->get_64());
- trailer.offset_table_start = BSWAP64(fb->get_64());
- root = read_bplist_obj(fb, trailer.root_offset_idx);
-
- return root.is_valid();
- } else {
- // Load text plist.
- Error err;
- Vector<uint8_t> array = FileAccess::get_file_as_bytes(p_filename, &err);
- ERR_FAIL_COND_V(err != OK, false);
-
- String ret;
- ret.parse_utf8((const char *)array.ptr(), array.size());
- return load_string(ret);
- }
-}
-
-bool PList::load_string(const String &p_string) {
- root = Ref<PListNode>();
-
- int pos = 0;
- bool in_plist = false;
- bool done_plist = false;
- List<Ref<PListNode>> stack;
- String key;
- while (pos >= 0) {
- int open_token_s = p_string.find("<", pos);
- if (open_token_s == -1) {
- ERR_FAIL_V_MSG(false, "PList: Unexpected end of data. No tags found.");
- }
- int open_token_e = p_string.find(">", open_token_s);
- pos = open_token_e;
-
- String token = p_string.substr(open_token_s + 1, open_token_e - open_token_s - 1);
- if (token.is_empty()) {
- ERR_FAIL_V_MSG(false, "PList: Invalid token name.");
- }
- String value;
- if (token[0] == '?' || token[0] == '!') { // Skip <?xml ... ?> and <!DOCTYPE ... >
- int end_token_e = p_string.find(">", open_token_s);
- pos = end_token_e;
- continue;
- }
-
- if (token.find("plist", 0) == 0) {
- in_plist = true;
- continue;
- }
-
- if (token == "/plist") {
- done_plist = true;
- break;
- }
-
- if (!in_plist) {
- ERR_FAIL_V_MSG(false, "PList: Node outside of <plist> tag.");
- }
-
- if (token == "dict") {
- if (!stack.is_empty()) {
- // Add subnode end enter it.
- Ref<PListNode> dict = PListNode::new_dict();
- dict->data_type = PList::PLNodeType::PL_NODE_TYPE_DICT;
- if (!stack.back()->get()->push_subnode(dict, key)) {
- ERR_FAIL_V_MSG(false, "PList: Can't push subnode, invalid parent type.");
- }
- stack.push_back(dict);
- } else {
- // Add root node.
- if (!root.is_null()) {
- ERR_FAIL_V_MSG(false, "PList: Root node already set.");
- }
- Ref<PListNode> dict = PListNode::new_dict();
- stack.push_back(dict);
- root = dict;
- }
- continue;
- }
-
- if (token == "/dict") {
- // Exit current dict.
- if (stack.is_empty() || stack.back()->get()->data_type != PList::PLNodeType::PL_NODE_TYPE_DICT) {
- ERR_FAIL_V_MSG(false, "PList: Mismatched </dict> tag.");
- }
- stack.pop_back();
- continue;
- }
-
- if (token == "array") {
- if (!stack.is_empty()) {
- // Add subnode end enter it.
- Ref<PListNode> arr = PListNode::new_array();
- if (!stack.back()->get()->push_subnode(arr, key)) {
- ERR_FAIL_V_MSG(false, "PList: Can't push subnode, invalid parent type.");
- }
- stack.push_back(arr);
- } else {
- // Add root node.
- if (!root.is_null()) {
- ERR_FAIL_V_MSG(false, "PList: Root node already set.");
- }
- Ref<PListNode> arr = PListNode::new_array();
- stack.push_back(arr);
- root = arr;
- }
- continue;
- }
-
- if (token == "/array") {
- // Exit current array.
- if (stack.is_empty() || stack.back()->get()->data_type != PList::PLNodeType::PL_NODE_TYPE_ARRAY) {
- ERR_FAIL_V_MSG(false, "PList: Mismatched </array> tag.");
- }
- stack.pop_back();
- continue;
- }
-
- if (token[token.length() - 1] == '/') {
- token = token.substr(0, token.length() - 1);
- } else {
- int end_token_s = p_string.find("</", pos);
- if (end_token_s == -1) {
- ERR_FAIL_V_MSG(false, vformat("PList: Mismatched <%s> tag.", token));
- }
- int end_token_e = p_string.find(">", end_token_s);
- pos = end_token_e;
- String end_token = p_string.substr(end_token_s + 2, end_token_e - end_token_s - 2);
- if (end_token != token) {
- ERR_FAIL_V_MSG(false, vformat("PList: Mismatched <%s> and <%s> token pair.", token, end_token));
- }
- value = p_string.substr(open_token_e + 1, end_token_s - open_token_e - 1);
- }
- if (token == "key") {
- key = value;
- } else {
- Ref<PListNode> var = nullptr;
- if (token == "true") {
- var = PListNode::new_bool(true);
- } else if (token == "false") {
- var = PListNode::new_bool(false);
- } else if (token == "integer") {
- var = PListNode::new_int(value.to_int());
- } else if (token == "real") {
- var = PListNode::new_real(value.to_float());
- } else if (token == "string") {
- var = PListNode::new_string(value);
- } else if (token == "data") {
- var = PListNode::new_data(value);
- } else if (token == "date") {
- var = PListNode::new_date(value);
- } else {
- ERR_FAIL_V_MSG(false, "PList: Invalid value type.");
- }
- if (stack.is_empty() || !stack.back()->get()->push_subnode(var, key)) {
- ERR_FAIL_V_MSG(false, "PList: Can't push subnode, invalid parent type.");
- }
- }
- }
- if (!stack.is_empty() || !done_plist) {
- ERR_FAIL_V_MSG(false, "PList: Unexpected end of data. Root node is not closed.");
- }
- return true;
-}
-
-PackedByteArray PList::save_asn1() const {
- if (root == nullptr) {
- ERR_FAIL_V_MSG(PackedByteArray(), "PList: Invalid PList, no root node.");
- }
- size_t size = root->get_asn1_size(1);
- uint8_t len_octets = 0;
- if (size < 0x80) {
- len_octets = 1;
- } else {
- size = root->get_asn1_size(2);
- if (size < 0xFFFF) {
- len_octets = 2;
- } else {
- size = root->get_asn1_size(3);
- if (size < 0xFFFFFF) {
- len_octets = 3;
- } else {
- size = root->get_asn1_size(4);
- if (size < 0xFFFFFFFF) {
- len_octets = 4;
- } else {
- ERR_FAIL_V_MSG(PackedByteArray(), "PList: Data is too big for ASN.1 serializer, should be < 4 GiB.");
- }
- }
- }
- }
-
- PackedByteArray ret;
- if (!root->store_asn1(ret, len_octets)) {
- ERR_FAIL_V_MSG(PackedByteArray(), "PList: ASN.1 serializer error.");
- }
- return ret;
-}
-
-String PList::save_text() const {
- if (root == nullptr) {
- ERR_FAIL_V_MSG(String(), "PList: Invalid PList, no root node.");
- }
-
- String ret;
- ret += "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
- ret += "<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n";
- ret += "<plist version=\"1.0\">\n";
-
- root->store_text(ret, 0);
-
- ret += "</plist>\n\n";
- return ret;
-}
-
-Ref<PListNode> PList::get_root() {
- return root;
-}
diff --git a/platform/macos/export/plist.h b/platform/macos/export/plist.h
deleted file mode 100644
index 28b02e4eb7..0000000000
--- a/platform/macos/export/plist.h
+++ /dev/null
@@ -1,128 +0,0 @@
-/**************************************************************************/
-/* plist.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. */
-/**************************************************************************/
-
-#ifndef MACOS_PLIST_H
-#define MACOS_PLIST_H
-
-// Property list file format (application/x-plist) parser, property list ASN-1 serialization.
-
-#include "core/crypto/crypto_core.h"
-#include "core/io/file_access.h"
-#include "core/os/time.h"
-
-class PListNode;
-
-class PList : public RefCounted {
- friend class PListNode;
-
-public:
- enum PLNodeType {
- PL_NODE_TYPE_NIL,
- PL_NODE_TYPE_STRING,
- PL_NODE_TYPE_ARRAY,
- PL_NODE_TYPE_DICT,
- PL_NODE_TYPE_BOOLEAN,
- PL_NODE_TYPE_INTEGER,
- PL_NODE_TYPE_REAL,
- PL_NODE_TYPE_DATA,
- PL_NODE_TYPE_DATE,
- };
-
-private:
- struct PListTrailer {
- uint8_t offset_size;
- uint8_t ref_size;
- uint64_t object_num;
- uint64_t root_offset_idx;
- uint64_t offset_table_start;
- };
-
- PListTrailer trailer;
- Ref<PListNode> root;
-
- uint64_t read_bplist_var_size_int(Ref<FileAccess> p_file, uint8_t p_size);
- Ref<PListNode> read_bplist_obj(Ref<FileAccess> p_file, uint64_t p_offset_idx);
-
-public:
- PList();
- PList(const String &p_string);
-
- bool load_file(const String &p_filename);
- bool load_string(const String &p_string);
-
- PackedByteArray save_asn1() const;
- String save_text() const;
-
- Ref<PListNode> get_root();
-};
-
-/*************************************************************************/
-
-class PListNode : public RefCounted {
- static int _asn1_size_len(uint8_t p_len_octets);
-
-public:
- PList::PLNodeType data_type = PList::PLNodeType::PL_NODE_TYPE_NIL;
-
- CharString data_string;
- Vector<Ref<PListNode>> data_array;
- HashMap<String, Ref<PListNode>> data_dict;
- union {
- int64_t data_int;
- bool data_bool;
- double data_real;
- };
-
- PList::PLNodeType get_type() const;
- Variant get_value() const;
-
- static Ref<PListNode> new_node(const Variant &p_value);
- static Ref<PListNode> new_array();
- static Ref<PListNode> new_dict();
- static Ref<PListNode> new_string(const String &p_string);
- static Ref<PListNode> new_data(const String &p_string);
- static Ref<PListNode> new_date(const String &p_string);
- static Ref<PListNode> new_bool(bool p_bool);
- static Ref<PListNode> new_int(int64_t p_int);
- static Ref<PListNode> new_real(double p_real);
-
- bool push_subnode(const Ref<PListNode> &p_node, const String &p_key = "");
-
- size_t get_asn1_size(uint8_t p_len_octets) const;
-
- void store_asn1_size(PackedByteArray &p_stream, uint8_t p_len_octets) const;
- bool store_asn1(PackedByteArray &p_stream, uint8_t p_len_octets) const;
- void store_text(String &p_stream, uint8_t p_indent) const;
-
- PListNode() {}
- ~PListNode() {}
-};
-
-#endif // MACOS_PLIST_H
diff --git a/platform/macos/os_macos.mm b/platform/macos/os_macos.mm
index 29dff683d5..b8496d72fb 100644
--- a/platform/macos/os_macos.mm
+++ b/platform/macos/os_macos.mm
@@ -230,6 +230,8 @@ Error OS_MacOS::open_dynamic_library(const String p_path, void *&p_library_handl
path = get_framework_executable(get_executable_path().get_base_dir().path_join("../Frameworks").path_join(p_path.get_file()));
}
+ 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()));
diff --git a/platform/web/.eslintrc.html.js b/platform/web/.eslintrc.html.js
index 5cb8de360a..8c9a3d83da 100644
--- a/platform/web/.eslintrc.html.js
+++ b/platform/web/.eslintrc.html.js
@@ -15,5 +15,7 @@ module.exports = {
"Godot": true,
"Engine": true,
"$GODOT_CONFIG": true,
+ "$GODOT_THREADS_ENABLED": true,
+ "___GODOT_THREADS_ENABLED___": true,
},
};
diff --git a/platform/web/SCsub b/platform/web/SCsub
index 1af0642554..3e0cc9ac4a 100644
--- a/platform/web/SCsub
+++ b/platform/web/SCsub
@@ -13,7 +13,7 @@ if "serve" in COMMAND_LINE_TARGETS or "run" in COMMAND_LINE_TARGETS:
except Exception:
print("GODOT_WEB_TEST_PORT must be a valid integer")
sys.exit(255)
- serve(env.Dir("#bin/.web_zip").abspath, port, "run" in COMMAND_LINE_TARGETS)
+ serve(env.Dir(env.GetTemplateZipPath()).abspath, port, "run" in COMMAND_LINE_TARGETS)
sys.exit(0)
web_files = [
@@ -95,7 +95,7 @@ engine = [
"js/engine/engine.js",
]
externs = [env.File("#platform/web/js/engine/engine.externs.js")]
-js_engine = env.CreateEngineFile("#bin/godot${PROGSUFFIX}.engine.js", engine, externs)
+js_engine = env.CreateEngineFile("#bin/godot${PROGSUFFIX}.engine.js", engine, externs, env["threads"])
env.Depends(js_engine, externs)
wrap_list = [
diff --git a/platform/web/audio_driver_web.cpp b/platform/web/audio_driver_web.cpp
index 1298d28ebf..ec3c22bf7c 100644
--- a/platform/web/audio_driver_web.cpp
+++ b/platform/web/audio_driver_web.cpp
@@ -30,6 +30,8 @@
#include "audio_driver_web.h"
+#include "godot_audio.h"
+
#include "core/config/project_settings.h"
#include <emscripten.h>
@@ -184,6 +186,8 @@ Error AudioDriverWeb::input_stop() {
return OK;
}
+#ifdef THREADS_ENABLED
+
/// AudioWorkletNode implementation (threads)
void AudioDriverWorklet::_audio_thread_func(void *p_data) {
AudioDriverWorklet *driver = static_cast<AudioDriverWorklet *>(p_data);
@@ -245,3 +249,51 @@ void AudioDriverWorklet::finish_driver() {
quit = true; // Ask thread to quit.
thread.wait_to_finish();
}
+
+#else // No threads.
+
+/// AudioWorkletNode implementation (no threads)
+AudioDriverWorklet *AudioDriverWorklet::singleton = nullptr;
+
+Error AudioDriverWorklet::create(int &p_buffer_size, int p_channels) {
+ if (!godot_audio_has_worklet()) {
+ return ERR_UNAVAILABLE;
+ }
+ return (Error)godot_audio_worklet_create(p_channels);
+}
+
+void AudioDriverWorklet::start(float *p_out_buf, int p_out_buf_size, float *p_in_buf, int p_in_buf_size) {
+ _audio_driver_process();
+ godot_audio_worklet_start_no_threads(p_out_buf, p_out_buf_size, &_process_callback, p_in_buf, p_in_buf_size, &_capture_callback);
+}
+
+void AudioDriverWorklet::_process_callback(int p_pos, int p_samples) {
+ AudioDriverWorklet *driver = AudioDriverWorklet::get_singleton();
+ driver->_audio_driver_process(p_pos, p_samples);
+}
+
+void AudioDriverWorklet::_capture_callback(int p_pos, int p_samples) {
+ AudioDriverWorklet *driver = AudioDriverWorklet::get_singleton();
+ driver->_audio_driver_capture(p_pos, p_samples);
+}
+
+/// ScriptProcessorNode implementation
+AudioDriverScriptProcessor *AudioDriverScriptProcessor::singleton = nullptr;
+
+void AudioDriverScriptProcessor::_process_callback() {
+ AudioDriverScriptProcessor::get_singleton()->_audio_driver_capture();
+ AudioDriverScriptProcessor::get_singleton()->_audio_driver_process();
+}
+
+Error AudioDriverScriptProcessor::create(int &p_buffer_samples, int p_channels) {
+ if (!godot_audio_has_script_processor()) {
+ return ERR_UNAVAILABLE;
+ }
+ return (Error)godot_audio_script_create(&p_buffer_samples, p_channels);
+}
+
+void AudioDriverScriptProcessor::start(float *p_out_buf, int p_out_buf_size, float *p_in_buf, int p_in_buf_size) {
+ godot_audio_script_start(p_in_buf, p_in_buf_size, p_out_buf, p_out_buf_size, &_process_callback);
+}
+
+#endif // THREADS_ENABLED
diff --git a/platform/web/audio_driver_web.h b/platform/web/audio_driver_web.h
index 12a61746c3..df88d0a94c 100644
--- a/platform/web/audio_driver_web.h
+++ b/platform/web/audio_driver_web.h
@@ -90,6 +90,7 @@ public:
AudioDriverWeb() {}
};
+#ifdef THREADS_ENABLED
class AudioDriverWorklet : public AudioDriverWeb {
private:
enum {
@@ -120,4 +121,54 @@ public:
virtual void unlock() override;
};
+#else
+
+class AudioDriverWorklet : public AudioDriverWeb {
+private:
+ static void _process_callback(int p_pos, int p_samples);
+ static void _capture_callback(int p_pos, int p_samples);
+
+ static AudioDriverWorklet *singleton;
+
+protected:
+ virtual Error create(int &p_buffer_size, int p_output_channels) override;
+ virtual void start(float *p_out_buf, int p_out_buf_size, float *p_in_buf, int p_in_buf_size) override;
+
+public:
+ virtual const char *get_name() const override {
+ return "AudioWorklet";
+ }
+
+ virtual void lock() override {}
+ virtual void unlock() override {}
+
+ static AudioDriverWorklet *get_singleton() { return singleton; }
+
+ AudioDriverWorklet() { singleton = this; }
+};
+
+class AudioDriverScriptProcessor : public AudioDriverWeb {
+private:
+ static void _process_callback();
+
+ static AudioDriverScriptProcessor *singleton;
+
+protected:
+ virtual Error create(int &p_buffer_size, int p_output_channels) override;
+ virtual void start(float *p_out_buf, int p_out_buf_size, float *p_in_buf, int p_in_buf_size) override;
+ virtual void finish_driver() override;
+
+public:
+ virtual const char *get_name() const override { return "ScriptProcessor"; }
+
+ virtual void lock() override {}
+ virtual void unlock() override {}
+
+ static AudioDriverScriptProcessor *get_singleton() { return singleton; }
+
+ AudioDriverScriptProcessor() { singleton = this; }
+};
+
+#endif // THREADS_ENABLED
+
#endif // AUDIO_DRIVER_WEB_H
diff --git a/platform/web/detect.py b/platform/web/detect.py
index 579eaaff03..bce03eb79e 100644
--- a/platform/web/detect.py
+++ b/platform/web/detect.py
@@ -8,6 +8,7 @@ from emscripten_helpers import (
add_js_pre,
add_js_externs,
create_template_zip,
+ get_template_zip_path,
)
from methods import get_compiler_version
from SCons.Util import WhereIs
@@ -30,6 +31,9 @@ def get_opts():
return [
("initial_memory", "Initial WASM memory (in MiB)", 32),
+ # Matches default values from before Emscripten 3.1.27. New defaults are too low for Godot.
+ ("stack_size", "WASM stack size (in KiB)", 5120),
+ ("default_pthread_stack_size", "WASM pthread default stack size (in KiB)", 2048),
BoolVariable("use_assertions", "Use Emscripten runtime assertions", False),
BoolVariable("use_ubsan", "Use Emscripten undefined behavior sanitizer (UBSAN)", False),
BoolVariable("use_asan", "Use Emscripten address sanitizer (ASAN)", False),
@@ -158,6 +162,9 @@ def configure(env: "Environment"):
# Add method that joins/compiles our Engine files.
env.AddMethod(create_engine_file, "CreateEngineFile")
+ # Add method for getting the final zip path
+ env.AddMethod(get_template_zip_path, "GetTemplateZipPath")
+
# Add method for creating the final zip file
env.AddMethod(create_template_zip, "CreateTemplateZip")
@@ -193,11 +200,6 @@ def configure(env: "Environment"):
env.Prepend(CPPPATH=["#platform/web"])
env.Append(CPPDEFINES=["WEB_ENABLED", "UNIX_ENABLED"])
- if cc_semver >= (3, 1, 25):
- env.Append(LINKFLAGS=["-s", "STACK_SIZE=5MB"])
- else:
- env.Append(LINKFLAGS=["-s", "TOTAL_STACK=5MB"])
-
if env["opengl3"]:
env.AppendUnique(CPPDEFINES=["GLES3_ENABLED"])
# This setting just makes WebGL 2 APIs available, it does NOT disable WebGL 1.
@@ -208,13 +210,20 @@ def configure(env: "Environment"):
if env["javascript_eval"]:
env.Append(CPPDEFINES=["JAVASCRIPT_EVAL_ENABLED"])
- # Thread support (via SharedArrayBuffer).
- env.Append(CPPDEFINES=["PTHREAD_NO_RENAME"])
- env.Append(CCFLAGS=["-s", "USE_PTHREADS=1"])
- env.Append(LINKFLAGS=["-s", "USE_PTHREADS=1"])
- env.Append(LINKFLAGS=["-s", "DEFAULT_PTHREAD_STACK_SIZE=2MB"])
- env.Append(LINKFLAGS=["-s", "PTHREAD_POOL_SIZE=8"])
- env.Append(LINKFLAGS=["-s", "WASM_MEM_MAX=2048MB"])
+ stack_size_opt = "STACK_SIZE" if cc_semver >= (3, 1, 25) else "TOTAL_STACK"
+ env.Append(LINKFLAGS=["-s", "%s=%sKB" % (stack_size_opt, env["stack_size"])])
+
+ if env["threads"]:
+ # Thread support (via SharedArrayBuffer).
+ env.Append(CPPDEFINES=["PTHREAD_NO_RENAME"])
+ env.Append(CCFLAGS=["-s", "USE_PTHREADS=1"])
+ env.Append(LINKFLAGS=["-s", "USE_PTHREADS=1"])
+ env.Append(LINKFLAGS=["-s", "DEFAULT_PTHREAD_STACK_SIZE=%sKB" % env["default_pthread_stack_size"]])
+ env.Append(LINKFLAGS=["-s", "PTHREAD_POOL_SIZE=8"])
+ env.Append(LINKFLAGS=["-s", "WASM_MEM_MAX=2048MB"])
+ elif env["proxy_to_pthread"]:
+ print('"threads=no" support requires "proxy_to_pthread=no", disabling proxy to pthread.')
+ env["proxy_to_pthread"] = False
if env["lto"] != "none":
# Workaround https://github.com/emscripten-core/emscripten/issues/19781.
@@ -223,7 +232,7 @@ def configure(env: "Environment"):
if env["dlink_enabled"]:
if env["proxy_to_pthread"]:
- print("GDExtension support requires proxy_to_pthread=no, disabling")
+ print("GDExtension support requires proxy_to_pthread=no, disabling proxy to pthread.")
env["proxy_to_pthread"] = False
if cc_semver < (3, 1, 14):
diff --git a/platform/web/doc_classes/EditorExportPlatformWeb.xml b/platform/web/doc_classes/EditorExportPlatformWeb.xml
index c4c4fd870b..f07f265b0d 100644
--- a/platform/web/doc_classes/EditorExportPlatformWeb.xml
+++ b/platform/web/doc_classes/EditorExportPlatformWeb.xml
@@ -47,6 +47,10 @@
</member>
<member name="variant/extensions_support" type="bool" setter="" getter="">
</member>
+ <member name="variant/thread_support" type="bool" setter="" getter="">
+ If enabled, the exported game will support threads. It requires [url=https://web.dev/articles/coop-coep]a "cross-origin isolated" website[/url], which can be difficult to setup and brings some limitations (e.g. not being able to communicate with third-party websites).
+ If disabled, the exported game will not support threads. As a result, it is more prone to performance and audio issues, but will only require to be run on a HTTPS website.
+ </member>
<member name="vram_texture_compression/for_desktop" type="bool" setter="" getter="">
</member>
<member name="vram_texture_compression/for_mobile" type="bool" setter="" getter="">
diff --git a/platform/web/emscripten_helpers.py b/platform/web/emscripten_helpers.py
index ec33397842..3ba133c9a1 100644
--- a/platform/web/emscripten_helpers.py
+++ b/platform/web/emscripten_helpers.py
@@ -4,7 +4,12 @@ from SCons.Util import WhereIs
def run_closure_compiler(target, source, env, for_signature):
- closure_bin = os.path.join(os.path.dirname(WhereIs("emcc")), "node_modules", ".bin", "google-closure-compiler")
+ closure_bin = os.path.join(
+ os.path.dirname(WhereIs("emcc")),
+ "node_modules",
+ ".bin",
+ "google-closure-compiler",
+ )
cmd = [WhereIs("node"), closure_bin]
cmd.extend(["--compilation_level", "ADVANCED_OPTIMIZATIONS"])
for f in env["JSEXTERNS"]:
@@ -31,27 +36,29 @@ def get_build_version():
return v
-def create_engine_file(env, target, source, externs):
+def create_engine_file(env, target, source, externs, threads_enabled):
if env["use_closure_compiler"]:
return env.BuildJS(target, source, JSEXTERNS=externs)
- return env.Textfile(target, [env.File(s) for s in source])
+ subst_dict = {"___GODOT_THREADS_ENABLED": "true" if threads_enabled else "false"}
+ return env.Substfile(target=target, source=[env.File(s) for s in source], SUBST_DICT=subst_dict)
def create_template_zip(env, js, wasm, worker, side):
binary_name = "godot.editor" if env.editor_build else "godot"
- zip_dir = env.Dir("#bin/.web_zip")
+ zip_dir = env.Dir(env.GetTemplateZipPath())
in_files = [
js,
wasm,
- worker,
"#platform/web/js/libs/audio.worklet.js",
]
out_files = [
zip_dir.File(binary_name + ".js"),
zip_dir.File(binary_name + ".wasm"),
- zip_dir.File(binary_name + ".worker.js"),
zip_dir.File(binary_name + ".audio.worklet.js"),
]
+ if env["threads"]:
+ in_files.append(worker)
+ out_files.append(zip_dir.File(binary_name + ".worker.js"))
# Dynamic linking (extensions) specific.
if env["dlink_enabled"]:
in_files.append(side) # Side wasm (contains the actual Godot code).
@@ -65,18 +72,20 @@ def create_template_zip(env, js, wasm, worker, side):
"godot.editor.html",
"offline.html",
"godot.editor.js",
- "godot.editor.worker.js",
"godot.editor.audio.worklet.js",
"logo.svg",
"favicon.png",
]
+ if env["threads"]:
+ cache.append("godot.editor.worker.js")
opt_cache = ["godot.editor.wasm"]
subst_dict = {
- "@GODOT_VERSION@": get_build_version(),
- "@GODOT_NAME@": "GodotEngine",
- "@GODOT_CACHE@": json.dumps(cache),
- "@GODOT_OPT_CACHE@": json.dumps(opt_cache),
- "@GODOT_OFFLINE_PAGE@": "offline.html",
+ "___GODOT_VERSION___": get_build_version(),
+ "___GODOT_NAME___": "GodotEngine",
+ "___GODOT_CACHE___": json.dumps(cache),
+ "___GODOT_OPT_CACHE___": json.dumps(opt_cache),
+ "___GODOT_OFFLINE_PAGE___": "offline.html",
+ "___GODOT_THREADS_ENABLED___": "true" if env["threads"] else "false",
}
html = env.Substfile(target="#bin/godot${PROGSUFFIX}.html", source=html, SUBST_DICT=subst_dict)
in_files.append(html)
@@ -88,7 +97,9 @@ def create_template_zip(env, js, wasm, worker, side):
out_files.append(zip_dir.File("favicon.png"))
# PWA
service_worker = env.Substfile(
- target="#bin/godot${PROGSUFFIX}.service.worker.js", source=service_worker, SUBST_DICT=subst_dict
+ target="#bin/godot${PROGSUFFIX}.service.worker.js",
+ source=service_worker,
+ SUBST_DICT=subst_dict,
)
in_files.append(service_worker)
out_files.append(zip_dir.File("service.worker.js"))
@@ -115,6 +126,10 @@ def create_template_zip(env, js, wasm, worker, side):
)
+def get_template_zip_path(env):
+ return "#bin/.web_zip"
+
+
def add_js_libraries(env, libraries):
env.Append(JS_LIBS=env.File(libraries))
diff --git a/platform/web/export/export_plugin.cpp b/platform/web/export/export_plugin.cpp
index a70812cf5b..d7e72612b4 100644
--- a/platform/web/export/export_plugin.cpp
+++ b/platform/web/export/export_plugin.cpp
@@ -34,11 +34,11 @@
#include "run_icon_svg.gen.h"
#include "core/config/project_settings.h"
-#include "editor/editor_scale.h"
#include "editor/editor_settings.h"
#include "editor/editor_string_names.h"
#include "editor/export/editor_export.h"
#include "editor/import/resource_importer_texture_settings.h"
+#include "editor/themes/editor_scale.h"
#include "scene/resources/image_texture.h"
#include "modules/modules_enabled.gen.h" // For mono and svg.
@@ -112,7 +112,7 @@ Error EditorExportPlatformWeb::_write_or_error(const uint8_t *p_content, int p_s
return OK;
}
-void EditorExportPlatformWeb::_replace_strings(HashMap<String, String> p_replaces, Vector<uint8_t> &r_template) {
+void EditorExportPlatformWeb::_replace_strings(const HashMap<String, String> &p_replaces, Vector<uint8_t> &r_template) {
String str_template = String::utf8(reinterpret_cast<const char *>(r_template.ptr()), r_template.size());
String out;
Vector<String> lines = str_template.split("\n");
@@ -169,6 +169,13 @@ void EditorExportPlatformWeb::_fix_html(Vector<uint8_t> &p_html, const Ref<Edito
replaces["$GODOT_PROJECT_NAME"] = GLOBAL_GET("application/config/name");
replaces["$GODOT_HEAD_INCLUDE"] = head_include + custom_head_include;
replaces["$GODOT_CONFIG"] = str_config;
+
+ if (p_preset->get("variant/thread_support")) {
+ replaces["$GODOT_THREADS_ENABLED"] = "true";
+ } else {
+ replaces["$GODOT_THREADS_ENABLED"] = "false";
+ }
+
_replace_strings(replaces, p_html);
}
@@ -216,9 +223,9 @@ Error EditorExportPlatformWeb::_build_pwa(const Ref<EditorExportPreset> &p_prese
const String name = p_path.get_file().get_basename();
bool extensions = (bool)p_preset->get("variant/extensions_support");
HashMap<String, String> replaces;
- replaces["@GODOT_VERSION@"] = String::num_int64(OS::get_singleton()->get_unix_time()) + "|" + String::num_int64(OS::get_singleton()->get_ticks_usec());
- replaces["@GODOT_NAME@"] = proj_name.substr(0, 16);
- replaces["@GODOT_OFFLINE_PAGE@"] = name + ".offline.html";
+ replaces["___GODOT_VERSION___"] = String::num_int64(OS::get_singleton()->get_unix_time()) + "|" + String::num_int64(OS::get_singleton()->get_ticks_usec());
+ replaces["___GODOT_NAME___"] = proj_name.substr(0, 16);
+ replaces["___GODOT_OFFLINE_PAGE___"] = name + ".offline.html";
// Files cached during worker install.
Array cache_files;
@@ -231,7 +238,7 @@ Error EditorExportPlatformWeb::_build_pwa(const Ref<EditorExportPreset> &p_prese
}
cache_files.push_back(name + ".worker.js");
cache_files.push_back(name + ".audio.worklet.js");
- replaces["@GODOT_CACHE@"] = Variant(cache_files).to_json_string();
+ replaces["___GODOT_CACHE___"] = Variant(cache_files).to_json_string();
// Heavy files that are cached on demand.
Array opt_cache_files;
@@ -243,7 +250,7 @@ Error EditorExportPlatformWeb::_build_pwa(const Ref<EditorExportPreset> &p_prese
opt_cache_files.push_back(p_shared_objects[i].path.get_file());
}
}
- replaces["@GODOT_OPT_CACHE@"] = Variant(opt_cache_files).to_json_string();
+ replaces["___GODOT_OPT_CACHE___"] = Variant(opt_cache_files).to_json_string();
const String sw_path = dir.path_join(name + ".service.worker.js");
Vector<uint8_t> sw;
@@ -335,6 +342,7 @@ void EditorExportPlatformWeb::get_export_options(List<ExportOption> *r_options)
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "custom_template/release", PROPERTY_HINT_GLOBAL_FILE, "*.zip"), ""));
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "variant/extensions_support"), false)); // Export type.
+ r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "variant/thread_support"), true)); // Thread support (i.e. run with or without COEP/COOP headers).
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "vram_texture_compression/for_desktop"), true)); // S3TC
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "vram_texture_compression/for_mobile"), false)); // ETC or ETC2, depending on renderer
@@ -377,10 +385,11 @@ bool EditorExportPlatformWeb::has_valid_export_configuration(const Ref<EditorExp
String err;
bool valid = false;
bool extensions = (bool)p_preset->get("variant/extensions_support");
+ bool thread_support = (bool)p_preset->get("variant/thread_support");
// Look for export templates (first official, and if defined custom templates).
- bool dvalid = exists_export_template(_get_template_name(extensions, true), &err);
- bool rvalid = exists_export_template(_get_template_name(extensions, false), &err);
+ bool dvalid = exists_export_template(_get_template_name(extensions, thread_support, true), &err);
+ bool rvalid = exists_export_template(_get_template_name(extensions, thread_support, false), &err);
if (p_preset->get("custom_template/debug") != "") {
dvalid = FileAccess::exists(p_preset->get("custom_template/debug"));
@@ -454,7 +463,8 @@ Error EditorExportPlatformWeb::export_project(const Ref<EditorExportPreset> &p_p
template_path = template_path.strip_edges();
if (template_path.is_empty()) {
bool extensions = (bool)p_preset->get("variant/extensions_support");
- template_path = find_export_template(_get_template_name(extensions, p_debug));
+ bool thread_support = (bool)p_preset->get("variant/thread_support");
+ template_path = find_export_template(_get_template_name(extensions, thread_support, p_debug));
}
if (!template_path.is_empty() && !FileAccess::exists(template_path)) {
diff --git a/platform/web/export/export_plugin.h b/platform/web/export/export_plugin.h
index 887000ac45..98e3fe729e 100644
--- a/platform/web/export/export_plugin.h
+++ b/platform/web/export/export_plugin.h
@@ -56,11 +56,14 @@ class EditorExportPlatformWeb : public EditorExportPlatform {
Mutex server_lock;
Thread server_thread;
- String _get_template_name(bool p_extension, bool p_debug) const {
+ String _get_template_name(bool p_extension, bool p_thread_support, bool p_debug) const {
String name = "web";
if (p_extension) {
name += "_dlink";
}
+ if (!p_thread_support) {
+ name += "_nothreads";
+ }
if (p_debug) {
name += "_debug.zip";
} else {
@@ -90,7 +93,7 @@ class EditorExportPlatformWeb : public EditorExportPlatform {
}
Error _extract_template(const String &p_template, const String &p_dir, const String &p_name, bool pwa);
- void _replace_strings(HashMap<String, String> p_replaces, Vector<uint8_t> &r_template);
+ void _replace_strings(const HashMap<String, String> &p_replaces, Vector<uint8_t> &r_template);
void _fix_html(Vector<uint8_t> &p_html, const Ref<EditorExportPreset> &p_preset, const String &p_name, bool p_debug, int p_flags, const Vector<SharedObject> p_shared_objects, const Dictionary &p_file_sizes);
Error _add_manifest_icon(const String &p_path, const String &p_icon, int p_size, Array &r_arr);
Error _build_pwa(const Ref<EditorExportPreset> &p_preset, const String p_path, const Vector<SharedObject> &p_shared_objects);
diff --git a/platform/web/js/engine/features.js b/platform/web/js/engine/features.js
index b7c6c9d445..81bc82f3c6 100644
--- a/platform/web/js/engine/features.js
+++ b/platform/web/js/engine/features.js
@@ -72,8 +72,14 @@ const Features = { // eslint-disable-line no-unused-vars
*
* @returns {Array<string>} A list of human-readable missing features.
* @function Engine.getMissingFeatures
+ * @typedef {{ threads: boolean }} SupportedFeatures
+ * @param {SupportedFeatures} supportedFeatures
*/
- getMissingFeatures: function () {
+ getMissingFeatures: function (supportedFeatures = {}) {
+ const {
+ threads: supportsThreads = true,
+ } = supportedFeatures;
+
const missing = [];
if (!Features.isWebGLAvailable(2)) {
missing.push('WebGL2 - Check web browser configuration and hardware support');
@@ -84,12 +90,16 @@ const Features = { // eslint-disable-line no-unused-vars
if (!Features.isSecureContext()) {
missing.push('Secure Context - Check web server configuration (use HTTPS)');
}
- if (!Features.isCrossOriginIsolated()) {
- missing.push('Cross Origin Isolation - Check web server configuration (send correct headers)');
- }
- if (!Features.isSharedArrayBufferAvailable()) {
- missing.push('SharedArrayBuffer - Check web server configuration (send correct headers)');
+
+ if (supportsThreads) {
+ if (!Features.isCrossOriginIsolated()) {
+ missing.push('Cross-Origin Isolation - Check that the web server configuration sends the correct headers.');
+ }
+ if (!Features.isSharedArrayBufferAvailable()) {
+ missing.push('SharedArrayBuffer - Check that the web server configuration sends the correct headers.');
+ }
}
+
// Audio is normally optional since we have a dummy fallback.
return missing;
},
diff --git a/platform/web/js/libs/audio.worklet.js b/platform/web/js/libs/audio.worklet.js
index 89b581b3d6..3b94cab85c 100644
--- a/platform/web/js/libs/audio.worklet.js
+++ b/platform/web/js/libs/audio.worklet.js
@@ -167,7 +167,7 @@ class GodotProcessor extends AudioWorkletProcessor {
GodotProcessor.write_input(this.input_buffer, input);
this.input.write(this.input_buffer);
} else {
- this.port.postMessage('Input buffer is full! Skipping input frame.');
+ // this.port.postMessage('Input buffer is full! Skipping input frame.'); // Uncomment this line to debug input buffer.
}
}
const process_output = GodotProcessor.array_has_data(outputs);
@@ -184,7 +184,7 @@ class GodotProcessor extends AudioWorkletProcessor {
this.port.postMessage({ 'cmd': 'read', 'data': chunk });
}
} else {
- this.port.postMessage('Output buffer has not enough frames! Skipping output frame.');
+ // this.port.postMessage('Output buffer has not enough frames! Skipping output frame.'); // Uncomment this line to debug output buffer.
}
}
this.process_notify();
diff --git a/platform/windows/SCsub b/platform/windows/SCsub
index 0549e408a7..1cfbc33ef8 100644
--- a/platform/windows/SCsub
+++ b/platform/windows/SCsub
@@ -3,6 +3,7 @@
Import("env")
import os
+from pathlib import Path
from platform_methods import run_in_subprocess
import platform_windows_builders
@@ -25,6 +26,19 @@ common_win_wrap = [
"console_wrapper_windows.cpp",
]
+
+def arrange_program_clean(prog):
+ """
+ Given an SCons program, arrange for output files SCons doesn't know about
+ to be cleaned when SCons is called with --clean
+ """
+ extensions_to_clean = [".ilk", ".exp", ".pdb", ".lib"]
+ for program in prog:
+ executable_stem = Path(program.name).stem
+ extra_files_to_clean = [f"#bin/{executable_stem}{extension}" for extension in extensions_to_clean]
+ Clean(prog, extra_files_to_clean)
+
+
res_file = "godot_res.rc"
res_target = "godot_res" + env["OBJSUFFIX"]
res_obj = env.RES(res_target, res_file)
@@ -32,6 +46,7 @@ res_obj = env.RES(res_target, res_file)
sources = common_win + res_obj
prog = env.add_program("#bin/godot", sources, PROGSUFFIX=env["PROGSUFFIX"])
+arrange_program_clean(prog)
# Build console wrapper app.
if env["windows_subsystem"] == "gui":
@@ -48,6 +63,7 @@ if env["windows_subsystem"] == "gui":
env_wrap.Append(LIBS=["version"])
prog_wrap = env_wrap.add_program("#bin/godot", common_win_wrap + res_wrap_obj, PROGSUFFIX=env["PROGSUFFIX_WRAP"])
+ arrange_program_clean(prog_wrap)
env_wrap.Depends(prog_wrap, prog)
# Microsoft Visual Studio Project Generation
diff --git a/platform/windows/detect.py b/platform/windows/detect.py
index 801b32140a..79698f5bd7 100644
--- a/platform/windows/detect.py
+++ b/platform/windows/detect.py
@@ -436,16 +436,12 @@ def configure_msvc(env, vcvars_msvc_config):
LIBS += ["psapi", "dbghelp"]
if env["vulkan"]:
- env.AppendUnique(CPPDEFINES=["VULKAN_ENABLED"])
+ env.AppendUnique(CPPDEFINES=["VULKAN_ENABLED", "RD_ENABLED"])
if not env["use_volk"]:
LIBS += ["vulkan"]
if env["d3d12"]:
- if env["dxc_path"] == "":
- print("The Direct3D 12 rendering driver requires dxc_path to be set.")
- sys.exit(255)
-
- env.AppendUnique(CPPDEFINES=["D3D12_ENABLED"])
+ env.AppendUnique(CPPDEFINES=["D3D12_ENABLED", "RD_ENABLED"])
LIBS += ["d3d12", "dxgi", "dxguid"]
LIBS += ["version"] # Mesa dependency.
@@ -657,12 +653,12 @@ def configure_mingw(env):
env.Append(LIBS=["psapi", "dbghelp"])
if env["vulkan"]:
- env.Append(CPPDEFINES=["VULKAN_ENABLED"])
+ env.Append(CPPDEFINES=["VULKAN_ENABLED", "RD_ENABLED"])
if not env["use_volk"]:
env.Append(LIBS=["vulkan"])
if env["d3d12"]:
- env.AppendUnique(CPPDEFINES=["D3D12_ENABLED"])
+ env.AppendUnique(CPPDEFINES=["D3D12_ENABLED", "RD_ENABLED"])
env.Append(LIBS=["d3d12", "dxgi", "dxguid"])
arch_subdir = "arm64" if env["arch"] == "arm64" else "x64"
diff --git a/platform/windows/display_server_windows.cpp b/platform/windows/display_server_windows.cpp
index 2bec804c7c..b56954ae81 100644
--- a/platform/windows/display_server_windows.cpp
+++ b/platform/windows/display_server_windows.cpp
@@ -687,6 +687,8 @@ typedef struct {
} EnumRectData;
typedef struct {
+ Vector<DISPLAYCONFIG_PATH_INFO> paths;
+ Vector<DISPLAYCONFIG_MODE_INFO> modes;
int count;
int screen;
float rate;
@@ -738,12 +740,30 @@ static BOOL CALLBACK _MonitorEnumProcRefreshRate(HMONITOR hMonitor, HDC hdcMonit
minfo.cbSize = sizeof(minfo);
GetMonitorInfoW(hMonitor, &minfo);
- DEVMODEW dm;
- memset(&dm, 0, sizeof(dm));
- dm.dmSize = sizeof(dm);
- EnumDisplaySettingsW(minfo.szDevice, ENUM_CURRENT_SETTINGS, &dm);
+ bool found = false;
+ for (const DISPLAYCONFIG_PATH_INFO &path : data->paths) {
+ DISPLAYCONFIG_SOURCE_DEVICE_NAME source_name;
+ memset(&source_name, 0, sizeof(source_name));
+ source_name.header.type = DISPLAYCONFIG_DEVICE_INFO_GET_SOURCE_NAME;
+ source_name.header.size = sizeof(source_name);
+ source_name.header.adapterId = path.sourceInfo.adapterId;
+ source_name.header.id = path.sourceInfo.id;
+ if (DisplayConfigGetDeviceInfo(&source_name.header) == ERROR_SUCCESS) {
+ if (wcscmp(minfo.szDevice, source_name.viewGdiDeviceName) == 0 && path.targetInfo.refreshRate.Numerator != 0 && path.targetInfo.refreshRate.Denominator != 0) {
+ data->rate = (double)path.targetInfo.refreshRate.Numerator / (double)path.targetInfo.refreshRate.Denominator;
+ found = true;
+ break;
+ }
+ }
+ }
+ if (!found) {
+ DEVMODEW dm;
+ memset(&dm, 0, sizeof(dm));
+ dm.dmSize = sizeof(dm);
+ EnumDisplaySettingsW(minfo.szDevice, ENUM_CURRENT_SETTINGS, &dm);
- data->rate = dm.dmDisplayFrequency;
+ data->rate = dm.dmDisplayFrequency;
+ }
}
data->count++;
@@ -932,7 +952,19 @@ float DisplayServerWindows::screen_get_refresh_rate(int p_screen) const {
_THREAD_SAFE_METHOD_
p_screen = _get_screen_index(p_screen);
- EnumRefreshRateData data = { 0, p_screen, SCREEN_REFRESH_RATE_FALLBACK };
+ EnumRefreshRateData data = { Vector<DISPLAYCONFIG_PATH_INFO>(), Vector<DISPLAYCONFIG_MODE_INFO>(), 0, p_screen, SCREEN_REFRESH_RATE_FALLBACK };
+
+ uint32_t path_count = 0;
+ uint32_t mode_count = 0;
+ if (GetDisplayConfigBufferSizes(QDC_ONLY_ACTIVE_PATHS, &path_count, &mode_count) == ERROR_SUCCESS) {
+ data.paths.resize(path_count);
+ data.modes.resize(mode_count);
+ if (QueryDisplayConfig(QDC_ONLY_ACTIVE_PATHS, &path_count, data.paths.ptrw(), &mode_count, data.modes.ptrw(), nullptr) != ERROR_SUCCESS) {
+ data.paths.clear();
+ data.modes.clear();
+ }
+ }
+
EnumDisplayMonitors(nullptr, nullptr, _MonitorEnumProcRefreshRate, (LPARAM)&data);
return data.rate;
}
@@ -1916,6 +1948,10 @@ bool DisplayServerWindows::window_is_focused(WindowID p_window) const {
return wd.window_focused;
}
+DisplayServerWindows::WindowID DisplayServerWindows::get_focused_window() const {
+ return last_focused_window;
+}
+
bool DisplayServerWindows::window_can_draw(WindowID p_window) const {
_THREAD_SAFE_METHOD_
diff --git a/platform/windows/display_server_windows.h b/platform/windows/display_server_windows.h
index 29c2460c10..2668e14540 100644
--- a/platform/windows/display_server_windows.h
+++ b/platform/windows/display_server_windows.h
@@ -611,6 +611,8 @@ public:
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 WindowID get_focused_window() const override;
+
virtual bool window_can_draw(WindowID p_window = MAIN_WINDOW_ID) const override;
virtual bool can_any_window_draw() const override;
diff --git a/platform/windows/export/export_plugin.cpp b/platform/windows/export/export_plugin.cpp
index 418f38c127..6cdd370bfe 100644
--- a/platform/windows/export/export_plugin.cpp
+++ b/platform/windows/export/export_plugin.cpp
@@ -37,9 +37,9 @@
#include "core/io/image_loader.h"
#include "editor/editor_node.h"
#include "editor/editor_paths.h"
-#include "editor/editor_scale.h"
#include "editor/editor_string_names.h"
#include "editor/export/editor_export.h"
+#include "editor/themes/editor_scale.h"
#include "modules/modules_enabled.gen.h" // For svg.
#ifdef MODULE_SVG_ENABLED
diff --git a/platform/windows/godot.natvis b/platform/windows/godot.natvis
index 36b0919185..94cc52be98 100644
--- a/platform/windows/godot.natvis
+++ b/platform/windows/godot.natvis
@@ -5,11 +5,43 @@
<Item Name="[size]">_cowdata._ptr ? (((const unsigned int *)(_cowdata._ptr))[-1]) : 0</Item>
<ArrayItems>
<Size>_cowdata._ptr ? (((const unsigned int *)(_cowdata._ptr))[-1]) : 0</Size>
- <ValuePointer>_cowdata._ptr</ValuePointer>
+ <ValuePointer>($T1 *) _cowdata._ptr</ValuePointer>
</ArrayItems>
</Expand>
</Type>
+ <Type Name="Array">
+ <Expand>
+ <Item Name="[size]">_p->array._cowdata._ptr ? (((const unsigned int *)(_p->array._cowdata._ptr))[-1]) : 0</Item>
+ <ArrayItems>
+ <Size>_p->array._cowdata._ptr ? (((const unsigned int *)(_p->array._cowdata._ptr))[-1]) : 0</Size>
+ <ValuePointer>(Variant *) _p->array._cowdata._ptr</ValuePointer>
+ </ArrayItems>
+ </Expand>
+ </Type>
+
+ <Type Name="TypedArray&lt;*&gt;">
+ <Expand>
+ <Item Name="[size]"> _p->array._cowdata._ptr ? (((const unsigned int *)(_p->array._cowdata._ptr))[-1]) : 0</Item>
+ <ArrayItems>
+ <Size>_p->array._cowdata._ptr ? (((const unsigned int *)(_p->array._cowdata._ptr))[-1]) : 0</Size>
+ <ValuePointer >(Variant *) _p->array._cowdata._ptr</ValuePointer>
+ </ArrayItems>
+ </Expand>
+ </Type>
+
+ <Type Name="Dictionary">
+ <Expand>
+ <Item Name="[size]">_p &amp;&amp; _p->variant_map.head_element ? _p->variant_map.num_elements : 0</Item>
+ <LinkedListItems>
+ <Size>_p &amp;&amp; _p->variant_map.head_element ? _p->variant_map.num_elements : 0</Size>
+ <HeadPointer>_p ? _p->variant_map.head_element : nullptr</HeadPointer>
+ <NextPointer>next</NextPointer>
+ <ValueNode Name="[{data.key}]">(*this),view(MapHelper)</ValueNode>
+ </LinkedListItems>
+ </Expand>
+ </Type>
+
<Type Name="LocalVector&lt;*&gt;">
<Expand>
<Item Name="[size]">count</Item>
@@ -32,18 +64,80 @@
</Expand>
</Type>
- <Type Name="HashMap&lt;*,*&gt;">
+ <Type Name="NodePath">
+ <DisplayString Condition="!data">[empty]</DisplayString>
+ <DisplayString Condition="!!data">{{[absolute] = {data->absolute} [path] = {data->path,view(NodePathHelper)} [subpath] = {data->subpath,view(NodePathHelper)}}}</DisplayString>
<Expand>
- <Item Name="[size]">num_elements</Item>
+ <Item Name="[path]">data->path,view(NodePathHelper)</Item>
+ <Item Name="[subpath]">data->subpath,view(NodePathHelper)</Item>
+ <Item Name="[absolute]">data->absolute</Item>
+ </Expand>
+ </Type>
+
+ <Type Name="Vector&lt;StringName&gt;" IncludeView="NodePathHelper">
+ <Expand>
+ <ArrayItems>
+ <Size>_cowdata._ptr ? (((const unsigned int *)(_cowdata._ptr))[-1]) : 0</Size>
+ <ValuePointer>((StringName *)_cowdata._ptr),view(NodePathHelper)</ValuePointer>
+ </ArrayItems>
+ </Expand>
+ </Type>
+
+ <Type Name="StringName" IncludeView="NodePathHelper">
+ <DisplayString Condition="_data &amp;&amp; _data->cname">{_data->cname,s8b}</DisplayString>
+ <DisplayString Condition="_data &amp;&amp; !_data->cname">{_data->name,s32b}</DisplayString>
+ <DisplayString Condition="!_data">[empty]</DisplayString>
+ <StringView Condition="_data &amp;&amp; _data->cname">_data->cname,s8b</StringView>
+ <StringView Condition="_data &amp;&amp; !_data->cname">_data->name,s32b</StringView>
+ </Type>
+
+ <Type Name="HashMapElement&lt;*,*&gt;">
+ <DisplayString>{{Key = {($T1 *) &amp;data.key} Value = {($T2 *) &amp;data.value}}}</DisplayString>
+ <Expand>
+ <Item Name="[key]">($T1 *) &amp;data.key</Item>
+ <Item Name="[value]">($T2 *) &amp;data.value</Item>
+ </Expand>
+ </Type>
+
+ <!-- elements displayed by index -->
+ <Type Name="HashMap&lt;*,*,*,*,*&gt;" Priority="Medium">
+ <Expand>
+ <Item Name="[size]">head_element ? num_elements : 0</Item>
<LinkedListItems>
- <Size>num_elements</Size>
+ <Size>head_element ? num_elements : 0</Size>
<HeadPointer>head_element</HeadPointer>
<NextPointer>next</NextPointer>
- <ValueNode>data</ValueNode>
+ <ValueNode>(*this)</ValueNode>
</LinkedListItems>
</Expand>
</Type>
+ <!-- elements by key:value -->
+ <!-- show elements by index by specifying "ShowElementsByIndex"-->
+ <Type Name="HashMap&lt;*,*,*,*,*&gt;" ExcludeView="ShowElementsByIndex" Priority="MediumHigh">
+ <Expand>
+ <Item Name="[size]">head_element ? num_elements : 0</Item>
+ <LinkedListItems>
+ <Size>head_element ? num_elements : 0</Size>
+ <HeadPointer>head_element</HeadPointer>
+ <NextPointer>next</NextPointer>
+ <ValueNode Name="[{data.key}]">(*this),view(MapHelper)</ValueNode>
+ </LinkedListItems>
+ </Expand>
+ </Type>
+
+ <Type Name="KeyValue&lt;*,*&gt;" IncludeView="MapHelper">
+ <DisplayString>{value}</DisplayString>
+ </Type>
+
+ <Type Name="HashMapElement&lt;*,*&gt;" IncludeView="MapHelper">
+ <DisplayString>{data.value}</DisplayString>
+ <Expand>
+ <Item Name="[key]" >($T1 *) &amp;data.key</Item>
+ <Item Name="[value]">($T2 *) &amp;data.value</Item>
+ </Expand>
+ </Type>
+
<Type Name="VMap&lt;*,*&gt;">
<Expand>
<Item Condition="_cowdata._ptr" Name="[size]">*(reinterpret_cast&lt;int*&gt;(_cowdata._ptr) - 1)</Item>
@@ -58,11 +152,6 @@
<DisplayString Condition="dynamic_cast&lt;CallableCustomMethodPointerBase*&gt;(key.custom)">{dynamic_cast&lt;CallableCustomMethodPointerBase*&gt;(key.custom)->text}</DisplayString>
</Type>
- <!-- requires PR 64364
- <Type Name="GDScriptThreadContext">
- <DisplayString Condition="_is_main == true">main thread {_debug_thread_id}</DisplayString>
- </Type>
- -->
<Type Name="Variant">
<DisplayString Condition="type == Variant::NIL">nil</DisplayString>
@@ -82,22 +171,22 @@
<DisplayString Condition="type == Variant::PLANE">{*(Plane *)_data._mem}</DisplayString>
<DisplayString Condition="type == Variant::QUATERNION">{*(Quaternion *)_data._mem}</DisplayString>
<DisplayString Condition="type == Variant::COLOR">{*(Color *)_data._mem}</DisplayString>
+ <DisplayString Condition="type == Variant::STRING_NAME">{*(StringName *)_data._mem}</DisplayString>
<DisplayString Condition="type == Variant::NODE_PATH">{*(NodePath *)_data._mem}</DisplayString>
<DisplayString Condition="type == Variant::RID">{*(::RID *)_data._mem}</DisplayString>
- <DisplayString Condition="type == Variant::OBJECT">{*(Object *)_data._mem}</DisplayString>
+ <DisplayString Condition="type == Variant::OBJECT">{*(*reinterpret_cast&lt;ObjData*&gt;(&amp;_data._mem[0])).obj}</DisplayString>
<DisplayString Condition="type == Variant::DICTIONARY">{*(Dictionary *)_data._mem}</DisplayString>
<DisplayString Condition="type == Variant::ARRAY">{*(Array *)_data._mem}</DisplayString>
<DisplayString Condition="type == Variant::PACKED_BYTE_ARRAY">{reinterpret_cast&lt;const Variant::PackedArrayRef&lt;unsigned char&gt;*&gt;(_data.packed_array)->array}</DisplayString>
<DisplayString Condition="type == Variant::PACKED_INT32_ARRAY">{reinterpret_cast&lt;const Variant::PackedArrayRef&lt;int&gt;*&gt;(_data.packed_array)->array}</DisplayString>
- <!-- broken, will show incorrect data
- <DisplayString Condition="type == Variant::PACKED_INT64_ARRAY">{*(PackedInt64Array *)_data._mem}</DisplayString>
- -->
+ <DisplayString Condition="type == Variant::PACKED_INT64_ARRAY">{*reinterpret_cast&lt;PackedInt64Array *&gt;(&amp;_data.packed_array[1])}</DisplayString>
<DisplayString Condition="type == Variant::PACKED_FLOAT32_ARRAY">{reinterpret_cast&lt;const Variant::PackedArrayRef&lt;float&gt;*&gt;(_data.packed_array)->array}</DisplayString>
<DisplayString Condition="type == Variant::PACKED_FLOAT64_ARRAY">{reinterpret_cast&lt;const Variant::PackedArrayRef&lt;double&gt;*&gt;(_data.packed_array)->array}</DisplayString>
<DisplayString Condition="type == Variant::PACKED_STRING_ARRAY">{reinterpret_cast&lt;const Variant::PackedArrayRef&lt;String&gt;*&gt;(_data.packed_array)->array}</DisplayString>
<DisplayString Condition="type == Variant::PACKED_VECTOR2_ARRAY">{reinterpret_cast&lt;const Variant::PackedArrayRef&lt;Vector2&gt;*&gt;(_data.packed_array)->array}</DisplayString>
<DisplayString Condition="type == Variant::PACKED_VECTOR3_ARRAY">{reinterpret_cast&lt;const Variant::PackedArrayRef&lt;Vector3&gt;*&gt;(_data.packed_array)->array}</DisplayString>
<DisplayString Condition="type == Variant::PACKED_COLOR_ARRAY">{reinterpret_cast&lt;const Variant::PackedArrayRef&lt;Color&gt;*&gt;(_data.packed_array)->array}</DisplayString>
+ <DisplayString Condition="type &lt; 0 || type >= Variant::VARIANT_MAX">[INVALID]</DisplayString>
<StringView Condition="type == Variant::STRING &amp;&amp; ((String *)(_data._mem))->_cowdata._ptr">((String *)(_data._mem))->_cowdata._ptr,s32</StringView>
@@ -116,20 +205,22 @@
<Item Name="[value]" Condition="type == Variant::PLANE">*(Plane *)_data._mem</Item>
<Item Name="[value]" Condition="type == Variant::QUATERNION">*(Quaternion *)_data._mem</Item>
<Item Name="[value]" Condition="type == Variant::COLOR">*(Color *)_data._mem</Item>
+ <Item Name="[value]" Condition="type == Variant::STRING_NAME">*(StringName *)_data._mem</Item>
<Item Name="[value]" Condition="type == Variant::NODE_PATH">*(NodePath *)_data._mem</Item>
<Item Name="[value]" Condition="type == Variant::RID">*(::RID *)_data._mem</Item>
- <Item Name="[value]" Condition="type == Variant::OBJECT">*(Object *)_data._mem</Item>
+ <Item Name="[value]" Condition="type == Variant::OBJECT">*(*reinterpret_cast&lt;ObjData*&gt;(&amp;_data._mem[0])).obj</Item>
<Item Name="[value]" Condition="type == Variant::DICTIONARY">*(Dictionary *)_data._mem</Item>
<Item Name="[value]" Condition="type == Variant::ARRAY">*(Array *)_data._mem</Item>
<Item Name="[value]" Condition="type == Variant::PACKED_BYTE_ARRAY">reinterpret_cast&lt;const Variant::PackedArrayRef&lt;unsigned char&gt;*&gt;(_data.packed_array)->array</Item>
- <Item Name="[value]" Condition="type == Variant::PACKED_INT32_ARRAY">*(PackedInt32Array *)_data._mem</Item>
- <Item Name="[value]" Condition="type == Variant::PACKED_INT64_ARRAY">*(PackedInt64Array *)_data._mem</Item>
- <Item Name="[value]" Condition="type == Variant::PACKED_FLOAT32_ARRAY">*(PackedFloat32Array *)_data._mem</Item>
- <Item Name="[value]" Condition="type == Variant::PACKED_FLOAT64_ARRAY">*(PackedFloat64Array *)_data._mem</Item>
- <Item Name="[value]" Condition="type == Variant::PACKED_STRING_ARRAY">*(PackedStringArray *)_data._mem</Item>
- <Item Name="[value]" Condition="type == Variant::PACKED_VECTOR2_ARRAY">*(PackedVector2Array *)_data._mem</Item>
- <Item Name="[value]" Condition="type == Variant::PACKED_VECTOR3_ARRAY">*(PackedVector3Array *)_data._mem</Item>
- <Item Name="[value]" Condition="type == Variant::PACKED_COLOR_ARRAY">*(PackedColorArray *)_data._mem</Item>
+ <Item Name="[value]" Condition="type == Variant::PACKED_INT32_ARRAY">reinterpret_cast&lt;const Variant::PackedArrayRef&lt;int&gt;*&gt;(_data.packed_array)->array</Item>
+ <Item Name="[value]" Condition="type == Variant::PACKED_INT64_ARRAY">*reinterpret_cast&lt;PackedInt64Array *&gt;(&amp;_data.packed_array[1])</Item>
+ <Item Name="[value]" Condition="type == Variant::PACKED_FLOAT32_ARRAY">reinterpret_cast&lt;const Variant::PackedArrayRef&lt;float&gt;*&gt;(_data.packed_array)->array</Item>
+ <Item Name="[value]" Condition="type == Variant::PACKED_FLOAT64_ARRAY">reinterpret_cast&lt;const Variant::PackedArrayRef&lt;double&gt;*&gt;(_data.packed_array)->array</Item>
+ <Item Name="[value]" Condition="type == Variant::PACKED_STRING_ARRAY">reinterpret_cast&lt;const Variant::PackedArrayRef&lt;String&gt;*&gt;(_data.packed_array)->array</Item>
+ <Item Name="[value]" Condition="type == Variant::PACKED_VECTOR2_ARRAY">reinterpret_cast&lt;const Variant::PackedArrayRef&lt;Vector2&gt;*&gt;(_data.packed_array)->array</Item>
+ <Item Name="[value]" Condition="type == Variant::PACKED_VECTOR3_ARRAY">reinterpret_cast&lt;const Variant::PackedArrayRef&lt;Vector3&gt;*&gt;(_data.packed_array)->array</Item>
+ <Item Name="[value]" Condition="type == Variant::PACKED_COLOR_ARRAY">reinterpret_cast&lt;const Variant::PackedArrayRef&lt;Color&gt;*&gt;(_data.packed_array)->array</Item>
+
</Expand>
</Type>
@@ -148,10 +239,10 @@
</Type>
<Type Name="StringName">
- <DisplayString Condition="_data &amp;&amp; _data->cname">{_data->cname}</DisplayString>
+ <DisplayString Condition="_data &amp;&amp; _data->cname">{_data->cname,na}</DisplayString>
<DisplayString Condition="_data &amp;&amp; !_data->cname">{_data->name,s32}</DisplayString>
<DisplayString Condition="!_data">[empty]</DisplayString>
- <StringView Condition="_data &amp;&amp; _data->cname">_data->cname</StringView>
+ <StringView Condition="_data &amp;&amp; _data->cname">_data->cname,na</StringView>
<StringView Condition="_data &amp;&amp; !_data->cname">_data->name,s32</StringView>
</Type>
diff --git a/platform/windows/os_windows.cpp b/platform/windows/os_windows.cpp
index 1d3b80e21e..6ad616da85 100644
--- a/platform/windows/os_windows.cpp
+++ b/platform/windows/os_windows.cpp
@@ -360,6 +360,8 @@ Error OS_Windows::open_dynamic_library(const String p_path, void *&p_library_han
path = get_executable_path().get_base_dir().path_join(p_path.get_file());
}
+ ERR_FAIL_COND_V(!FileAccess::exists(path), ERR_FILE_NOT_FOUND);
+
typedef DLL_DIRECTORY_COOKIE(WINAPI * PAddDllDirectory)(PCWSTR);
typedef BOOL(WINAPI * PRemoveDllDirectory)(DLL_DIRECTORY_COOKIE);
@@ -1183,7 +1185,7 @@ Vector<String> OS_Windows::get_system_font_path_for_text(const String &p_font_na
if (FAILED(hr)) {
continue;
}
- String fpath = String::utf16((const char16_t *)&file_path[0]);
+ String fpath = String::utf16((const char16_t *)&file_path[0]).replace("\\", "/");
WIN32_FIND_DATAW d;
HANDLE fnd = FindFirstFileW((LPCWSTR)&file_path[0], &d);
@@ -1262,7 +1264,7 @@ String OS_Windows::get_system_font_path(const String &p_font_name, int p_weight,
if (FAILED(hr)) {
continue;
}
- String fpath = String::utf16((const char16_t *)&file_path[0]);
+ String fpath = String::utf16((const char16_t *)&file_path[0]).replace("\\", "/");
WIN32_FIND_DATAW d;
HANDLE fnd = FindFirstFileW((LPCWSTR)&file_path[0], &d);
diff --git a/platform/windows/vulkan_context_win.h b/platform/windows/vulkan_context_win.h
index 29ab1d45c3..770a59de9e 100644
--- a/platform/windows/vulkan_context_win.h
+++ b/platform/windows/vulkan_context_win.h
@@ -39,7 +39,7 @@
#include <windows.h>
class VulkanContextWindows : public VulkanContext {
- virtual const char *_get_platform_surface_extension() const;
+ virtual const char *_get_platform_surface_extension() const override final;
public:
struct WindowPlatformData {