diff options
Diffstat (limited to 'platform')
55 files changed, 633 insertions, 412 deletions
diff --git a/platform/SCsub b/platform/SCsub index cdaa6074ba..7c9d07f6ef 100644 --- a/platform/SCsub +++ b/platform/SCsub @@ -1,4 +1,5 @@ #!/usr/bin/env python +from misc.utility.scons_hints import * from glob import glob from pathlib import Path diff --git a/platform/android/SCsub b/platform/android/SCsub index 8c88b419b3..3bc8959351 100644 --- a/platform/android/SCsub +++ b/platform/android/SCsub @@ -1,4 +1,5 @@ #!/usr/bin/env python +from misc.utility.scons_hints import * import subprocess import sys diff --git a/platform/android/api/api.cpp b/platform/android/api/api.cpp index 6920f801e5..078b9ab748 100644 --- a/platform/android/api/api.cpp +++ b/platform/android/api/api.cpp @@ -41,13 +41,11 @@ static JavaClassWrapper *java_class_wrapper = nullptr; void register_android_api() { #if !defined(ANDROID_ENABLED) - // On Android platforms, the `java_class_wrapper` instantiation and the - // `JNISingleton` registration occurs in + // On Android platforms, the `java_class_wrapper` instantiation occurs in // `platform/android/java_godot_lib_jni.cpp#Java_org_godotengine_godot_GodotLib_setup` - java_class_wrapper = memnew(JavaClassWrapper); // Dummy - GDREGISTER_CLASS(JNISingleton); + java_class_wrapper = memnew(JavaClassWrapper); #endif - + GDREGISTER_CLASS(JNISingleton); GDREGISTER_CLASS(JavaClass); GDREGISTER_CLASS(JavaObject); GDREGISTER_CLASS(JavaClassWrapper); @@ -108,7 +106,7 @@ Ref<JavaClass> JavaObject::get_java_class() const { JavaClassWrapper *JavaClassWrapper::singleton = nullptr; -Ref<JavaClass> JavaClassWrapper::wrap(const String &) { +Ref<JavaClass> JavaClassWrapper::_wrap(const String &, bool) { return Ref<JavaClass>(); } diff --git a/platform/android/api/java_class_wrapper.h b/platform/android/api/java_class_wrapper.h index 52df1644be..c74cef8dd0 100644 --- a/platform/android/api/java_class_wrapper.h +++ b/platform/android/api/java_class_wrapper.h @@ -47,7 +47,7 @@ class JavaClass : public RefCounted { GDCLASS(JavaClass, RefCounted); #ifdef ANDROID_ENABLED - enum ArgumentType{ + enum ArgumentType { ARG_TYPE_VOID, ARG_TYPE_BOOLEAN, ARG_TYPE_BYTE, @@ -262,6 +262,8 @@ class JavaClassWrapper : public Object { bool _get_type_sig(JNIEnv *env, jobject obj, uint32_t &sig, String &strsig); #endif + Ref<JavaClass> _wrap(const String &p_class, bool p_allow_private_methods_access); + static JavaClassWrapper *singleton; protected: @@ -270,15 +272,14 @@ protected: public: static JavaClassWrapper *get_singleton() { return singleton; } - Ref<JavaClass> wrap(const String &p_class); + Ref<JavaClass> wrap(const String &p_class) { + return _wrap(p_class, false); + } #ifdef ANDROID_ENABLED - Ref<JavaClass> wrap_jclass(jclass p_class); - - JavaClassWrapper(jobject p_activity = nullptr); -#else - JavaClassWrapper(); + Ref<JavaClass> wrap_jclass(jclass p_class, bool p_allow_private_methods_access = false); #endif + JavaClassWrapper(); }; #endif // JAVA_CLASS_WRAPPER_H diff --git a/platform/android/api/jni_singleton.h b/platform/android/api/jni_singleton.h index 06afc4eb78..5e940819bc 100644 --- a/platform/android/api/jni_singleton.h +++ b/platform/android/api/jni_singleton.h @@ -31,193 +31,53 @@ #ifndef JNI_SINGLETON_H #define JNI_SINGLETON_H +#include "java_class_wrapper.h" + #include "core/config/engine.h" #include "core/variant/variant.h" -#ifdef ANDROID_ENABLED -#include "jni_utils.h" -#endif - class JNISingleton : public Object { GDCLASS(JNISingleton, Object); -#ifdef ANDROID_ENABLED struct MethodData { - jmethodID method; Variant::Type ret_type; Vector<Variant::Type> argtypes; }; - jobject instance; RBMap<StringName, MethodData> method_map; -#endif + Ref<JavaObject> wrapped_object; public: virtual Variant callp(const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error) override { -#ifdef ANDROID_ENABLED - RBMap<StringName, MethodData>::Element *E = method_map.find(p_method); - - // Check the method we're looking for is in the JNISingleton map and that - // the arguments match. - bool call_error = !E || E->get().argtypes.size() != p_argcount; - if (!call_error) { - for (int i = 0; i < p_argcount; i++) { - if (!Variant::can_convert(p_args[i]->get_type(), E->get().argtypes[i])) { - call_error = true; - break; + if (wrapped_object.is_valid()) { + RBMap<StringName, MethodData>::Element *E = method_map.find(p_method); + + // Check the method we're looking for is in the JNISingleton map and that + // the arguments match. + bool call_error = !E || E->get().argtypes.size() != p_argcount; + if (!call_error) { + for (int i = 0; i < p_argcount; i++) { + if (!Variant::can_convert(p_args[i]->get_type(), E->get().argtypes[i])) { + call_error = true; + break; + } } } - } - - if (call_error) { - // The method is not in this map, defaulting to the regular instance calls. - return Object::callp(p_method, p_args, p_argcount, r_error); - } - - ERR_FAIL_NULL_V(instance, Variant()); - - r_error.error = Callable::CallError::CALL_OK; - - jvalue *v = nullptr; - if (p_argcount) { - v = (jvalue *)alloca(sizeof(jvalue) * p_argcount); - } - - JNIEnv *env = get_jni_env(); - - int res = env->PushLocalFrame(16); - - ERR_FAIL_COND_V(res != 0, Variant()); - - List<jobject> to_erase; - for (int i = 0; i < p_argcount; i++) { - jvalret vr = _variant_to_jvalue(env, E->get().argtypes[i], p_args[i]); - v[i] = vr.val; - if (vr.obj) { - to_erase.push_back(vr.obj); + if (!call_error) { + return wrapped_object->callp(p_method, p_args, p_argcount, r_error); } } - Variant ret; - - switch (E->get().ret_type) { - case Variant::NIL: { - env->CallVoidMethodA(instance, E->get().method, v); - } break; - case Variant::BOOL: { - ret = env->CallBooleanMethodA(instance, E->get().method, v) == JNI_TRUE; - } break; - case Variant::INT: { - ret = env->CallIntMethodA(instance, E->get().method, v); - } break; - case Variant::FLOAT: { - ret = env->CallFloatMethodA(instance, E->get().method, v); - } break; - case Variant::STRING: { - jobject o = env->CallObjectMethodA(instance, E->get().method, v); - ret = jstring_to_string((jstring)o, env); - env->DeleteLocalRef(o); - } break; - case Variant::PACKED_STRING_ARRAY: { - jobjectArray arr = (jobjectArray)env->CallObjectMethodA(instance, E->get().method, v); - - ret = _jobject_to_variant(env, arr); - - env->DeleteLocalRef(arr); - } break; - case Variant::PACKED_INT32_ARRAY: { - jintArray arr = (jintArray)env->CallObjectMethodA(instance, E->get().method, v); - - int fCount = env->GetArrayLength(arr); - Vector<int> sarr; - sarr.resize(fCount); - - int *w = sarr.ptrw(); - env->GetIntArrayRegion(arr, 0, fCount, w); - ret = sarr; - env->DeleteLocalRef(arr); - } break; - case Variant::PACKED_INT64_ARRAY: { - jlongArray arr = (jlongArray)env->CallObjectMethodA(instance, E->get().method, v); - - int fCount = env->GetArrayLength(arr); - Vector<int64_t> sarr; - sarr.resize(fCount); - - int64_t *w = sarr.ptrw(); - env->GetLongArrayRegion(arr, 0, fCount, w); - ret = sarr; - env->DeleteLocalRef(arr); - } break; - case Variant::PACKED_FLOAT32_ARRAY: { - jfloatArray arr = (jfloatArray)env->CallObjectMethodA(instance, E->get().method, v); - - int fCount = env->GetArrayLength(arr); - Vector<float> sarr; - sarr.resize(fCount); - - float *w = sarr.ptrw(); - env->GetFloatArrayRegion(arr, 0, fCount, w); - ret = sarr; - env->DeleteLocalRef(arr); - } break; - case Variant::PACKED_FLOAT64_ARRAY: { - jdoubleArray arr = (jdoubleArray)env->CallObjectMethodA(instance, E->get().method, v); - - int fCount = env->GetArrayLength(arr); - Vector<double> sarr; - sarr.resize(fCount); - - double *w = sarr.ptrw(); - env->GetDoubleArrayRegion(arr, 0, fCount, w); - ret = sarr; - env->DeleteLocalRef(arr); - } break; - case Variant::DICTIONARY: { - jobject obj = env->CallObjectMethodA(instance, E->get().method, v); - ret = _jobject_to_variant(env, obj); - env->DeleteLocalRef(obj); - - } break; - case Variant::OBJECT: { - jobject obj = env->CallObjectMethodA(instance, E->get().method, v); - ret = _jobject_to_variant(env, obj); - env->DeleteLocalRef(obj); - } break; - default: { - env->PopLocalFrame(nullptr); - ERR_FAIL_V(Variant()); - } break; - } - - while (to_erase.size()) { - env->DeleteLocalRef(to_erase.front()->get()); - to_erase.pop_front(); - } - - env->PopLocalFrame(nullptr); - - return ret; -#else // ANDROID_ENABLED - - // Defaulting to the regular instance calls. return Object::callp(p_method, p_args, p_argcount, r_error); -#endif } -#ifdef ANDROID_ENABLED - jobject get_instance() const { - return instance; + Ref<JavaObject> get_wrapped_object() const { + return wrapped_object; } - void set_instance(jobject p_instance) { - instance = p_instance; - } - - void add_method(const StringName &p_name, jmethodID p_method, const Vector<Variant::Type> &p_args, Variant::Type p_ret_type) { + void add_method(const StringName &p_name, const Vector<Variant::Type> &p_args, Variant::Type p_ret_type) { MethodData md; - md.method = p_method; md.argtypes = p_args; md.ret_type = p_ret_type; method_map[p_name] = md; @@ -232,24 +92,15 @@ public: ADD_SIGNAL(mi); } -#endif + JNISingleton() {} - JNISingleton() { -#ifdef ANDROID_ENABLED - instance = nullptr; -#endif + JNISingleton(const Ref<JavaObject> &p_wrapped_object) { + wrapped_object = p_wrapped_object; } ~JNISingleton() { -#ifdef ANDROID_ENABLED method_map.clear(); - if (instance) { - JNIEnv *env = get_jni_env(); - ERR_FAIL_NULL(env); - - env->DeleteGlobalRef(instance); - } -#endif + wrapped_object.unref(); } }; diff --git a/platform/android/display_server_android.cpp b/platform/android/display_server_android.cpp index c1053215c6..fa5b970a96 100644 --- a/platform/android/display_server_android.cpp +++ b/platform/android/display_server_android.cpp @@ -304,6 +304,13 @@ int DisplayServerAndroid::virtual_keyboard_get_height() const { return godot_io_java->get_vk_height(); } +bool DisplayServerAndroid::has_hardware_keyboard() const { + GodotIOJavaWrapper *godot_io_java = OS_Android::get_singleton()->get_godot_io_java(); + ERR_FAIL_NULL_V(godot_io_java, false); + + return godot_io_java->has_hardware_keyboard(); +} + void DisplayServerAndroid::window_set_window_event_callback(const Callable &p_callable, DisplayServer::WindowID p_window) { window_event_callback = p_callable; } diff --git a/platform/android/display_server_android.h b/platform/android/display_server_android.h index 90bda18cfa..65c6a53446 100644 --- a/platform/android/display_server_android.h +++ b/platform/android/display_server_android.h @@ -138,6 +138,7 @@ public: virtual void virtual_keyboard_show(const String &p_existing_text, const Rect2 &p_screen_rect = Rect2(), VirtualKeyboardType p_type = KEYBOARD_TYPE_DEFAULT, int p_max_length = -1, int p_cursor_start = -1, int p_cursor_end = -1) override; virtual void virtual_keyboard_hide() override; virtual int virtual_keyboard_get_height() const override; + virtual bool has_hardware_keyboard() const override; virtual void window_set_window_event_callback(const Callable &p_callable, WindowID p_window = MAIN_WINDOW_ID) override; virtual void window_set_input_event_callback(const Callable &p_callable, WindowID p_window = MAIN_WINDOW_ID) override; diff --git a/platform/android/doc_classes/EditorExportPlatformAndroid.xml b/platform/android/doc_classes/EditorExportPlatformAndroid.xml index 020e432155..2fe5539e56 100644 --- a/platform/android/doc_classes/EditorExportPlatformAndroid.xml +++ b/platform/android/doc_classes/EditorExportPlatformAndroid.xml @@ -102,6 +102,9 @@ <member name="launcher_icons/adaptive_foreground_432x432" type="String" setter="" getter=""> Foreground layer of the application adaptive icon file. See [url=https://developer.android.com/develop/ui/views/launch/icon_design_adaptive#design-adaptive-icons]Design adaptive icons[/url]. </member> + <member name="launcher_icons/adaptive_monochrome_432x432" type="String" setter="" getter=""> + Monochrome layer of the application adaptive icon file. See [url=https://developer.android.com/develop/ui/views/launch/icon_design_adaptive#design-adaptive-icons]Design adaptive icons[/url]. + </member> <member name="launcher_icons/main_192x192" type="String" setter="" getter=""> Application icon file. If left empty, it will fallback to [member ProjectSettings.application/config/icon]. </member> diff --git a/platform/android/export/export_plugin.cpp b/platform/android/export/export_plugin.cpp index e3d9807af7..41f460ca8f 100644 --- a/platform/android/export/export_plugin.cpp +++ b/platform/android/export/export_plugin.cpp @@ -222,6 +222,7 @@ static const int icon_densities_count = 6; static const char *launcher_icon_option = PNAME("launcher_icons/main_192x192"); static const char *launcher_adaptive_icon_foreground_option = PNAME("launcher_icons/adaptive_foreground_432x432"); static const char *launcher_adaptive_icon_background_option = PNAME("launcher_icons/adaptive_background_432x432"); +static const char *launcher_adaptive_icon_monochrome_option = PNAME("launcher_icons/adaptive_monochrome_432x432"); static const LauncherIcon launcher_icons[icon_densities_count] = { { "res/mipmap-xxxhdpi-v4/icon.png", 192 }, @@ -250,6 +251,15 @@ static const LauncherIcon launcher_adaptive_icon_backgrounds[icon_densities_coun { "res/mipmap/icon_background.png", 432 } }; +static const LauncherIcon launcher_adaptive_icon_monochromes[icon_densities_count] = { + { "res/mipmap-xxxhdpi-v4/icon_monochrome.png", 432 }, + { "res/mipmap-xxhdpi-v4/icon_monochrome.png", 324 }, + { "res/mipmap-xhdpi-v4/icon_monochrome.png", 216 }, + { "res/mipmap-hdpi-v4/icon_monochrome.png", 162 }, + { "res/mipmap-mdpi-v4/icon_monochrome.png", 108 }, + { "res/mipmap/icon_monochrome.png", 432 } +}; + static const int EXPORT_FORMAT_APK = 0; static const int EXPORT_FORMAT_AAB = 1; @@ -1644,12 +1654,13 @@ void EditorExportPlatformAndroid::_process_launcher_icons(const String &p_file_n } } -void EditorExportPlatformAndroid::load_icon_refs(const Ref<EditorExportPreset> &p_preset, Ref<Image> &icon, Ref<Image> &foreground, Ref<Image> &background) { +void EditorExportPlatformAndroid::load_icon_refs(const Ref<EditorExportPreset> &p_preset, Ref<Image> &icon, Ref<Image> &foreground, Ref<Image> &background, Ref<Image> &monochrome) { String project_icon_path = GLOBAL_GET("application/config/icon"); icon.instantiate(); foreground.instantiate(); background.instantiate(); + monochrome.instantiate(); // Regular icon: user selection -> project icon -> default. String path = static_cast<String>(p_preset->get(launcher_icon_option)).strip_edges(); @@ -1677,12 +1688,20 @@ void EditorExportPlatformAndroid::load_icon_refs(const Ref<EditorExportPreset> & print_verbose("Loading adaptive background icon from " + path); ImageLoader::load_image(path, background); } + + // Adaptive monochrome: user selection -> default. + path = static_cast<String>(p_preset->get(launcher_adaptive_icon_monochrome_option)).strip_edges(); + if (!path.is_empty()) { + print_verbose("Loading adaptive monochrome icon from " + path); + ImageLoader::load_image(path, background); + } } void EditorExportPlatformAndroid::_copy_icons_to_gradle_project(const Ref<EditorExportPreset> &p_preset, const Ref<Image> &p_main_image, const Ref<Image> &p_foreground, - const Ref<Image> &p_background) { + const Ref<Image> &p_background, + const Ref<Image> &p_monochrome) { String gradle_build_dir = ExportTemplateManager::get_android_build_directory(p_preset); // Prepare images to be resized for the icons. If some image ends up being uninitialized, @@ -1711,6 +1730,14 @@ void EditorExportPlatformAndroid::_copy_icons_to_gradle_project(const Ref<Editor launcher_adaptive_icon_backgrounds[i].dimensions, data); store_file_at_path(gradle_build_dir.path_join(launcher_adaptive_icon_backgrounds[i].export_path), data); } + + if (p_monochrome.is_valid() && !p_monochrome->is_empty()) { + print_verbose("Processing launcher adaptive icon p_monochrome for dimension " + itos(launcher_adaptive_icon_monochromes[i].dimensions) + " into " + launcher_adaptive_icon_monochromes[i].export_path); + Vector<uint8_t> data; + _process_launcher_icons(launcher_adaptive_icon_monochromes[i].export_path, p_monochrome, + launcher_adaptive_icon_monochromes[i].dimensions, data); + store_file_at_path(gradle_build_dir.path_join(launcher_adaptive_icon_monochromes[i].export_path), data); + } } } @@ -1875,6 +1902,7 @@ void EditorExportPlatformAndroid::get_export_options(List<ExportOption> *r_optio r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, launcher_icon_option, PROPERTY_HINT_FILE, "*.png"), "")); r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, launcher_adaptive_icon_foreground_option, PROPERTY_HINT_FILE, "*.png"), "")); r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, launcher_adaptive_icon_background_option, PROPERTY_HINT_FILE, "*.png"), "")); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, launcher_adaptive_icon_monochrome_option, PROPERTY_HINT_FILE, "*.png"), "")); r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "graphics/opengl_debug"), false)); @@ -3035,8 +3063,9 @@ Error EditorExportPlatformAndroid::export_project_helper(const Ref<EditorExportP Ref<Image> main_image; Ref<Image> foreground; Ref<Image> background; + Ref<Image> monochrome; - load_icon_refs(p_preset, main_image, foreground, background); + load_icon_refs(p_preset, main_image, foreground, background, monochrome); Vector<uint8_t> command_line_flags; // Write command line flags into the command_line_flags variable. @@ -3107,7 +3136,7 @@ Error EditorExportPlatformAndroid::export_project_helper(const Ref<EditorExportP add_message(EXPORT_MESSAGE_ERROR, TTR("Export"), TTR("Unable to overwrite res/*.xml files with project name.")); } // Copies the project icon files into the appropriate Gradle project directory. - _copy_icons_to_gradle_project(p_preset, main_image, foreground, background); + _copy_icons_to_gradle_project(p_preset, main_image, foreground, background, monochrome); // Write an AndroidManifest.xml file into the Gradle project directory. _write_tmp_manifest(p_preset, p_give_internet, p_debug); @@ -3234,8 +3263,11 @@ Error EditorExportPlatformAndroid::export_project_helper(const Ref<EditorExportP cmdline.push_back(apk_build_command); } + String addons_directory = ProjectSettings::get_singleton()->globalize_path("res://addons"); + cmdline.push_back("-p"); // argument to specify the start directory. cmdline.push_back(build_path); // start directory. + cmdline.push_back("-Paddons_directory=" + addons_directory); // path to the addon directory as it may contain jar or aar dependencies cmdline.push_back("-Pexport_package_name=" + package_name); // argument to specify the package name. cmdline.push_back("-Pexport_version_code=" + version_code); // argument to specify the version code. cmdline.push_back("-Pexport_version_name=" + version_name); // argument to specify the version name. @@ -3446,6 +3478,11 @@ Error EditorExportPlatformAndroid::export_project_helper(const Ref<EditorExportP _process_launcher_icons(file, background, launcher_adaptive_icon_backgrounds[i].dimensions, data); } } + if (monochrome.is_valid() && !monochrome->is_empty()) { + if (file == launcher_adaptive_icon_monochromes[i].export_path) { + _process_launcher_icons(file, monochrome, launcher_adaptive_icon_monochromes[i].dimensions, data); + } + } } } diff --git a/platform/android/export/export_plugin.h b/platform/android/export/export_plugin.h index 708288fbf4..7e1d626486 100644 --- a/platform/android/export/export_plugin.h +++ b/platform/android/export/export_plugin.h @@ -167,12 +167,13 @@ class EditorExportPlatformAndroid : public EditorExportPlatform { void _process_launcher_icons(const String &p_file_name, const Ref<Image> &p_source_image, int dimension, Vector<uint8_t> &p_data); - void load_icon_refs(const Ref<EditorExportPreset> &p_preset, Ref<Image> &icon, Ref<Image> &foreground, Ref<Image> &background); + void load_icon_refs(const Ref<EditorExportPreset> &p_preset, Ref<Image> &icon, Ref<Image> &foreground, Ref<Image> &background, Ref<Image> &monochrome); void _copy_icons_to_gradle_project(const Ref<EditorExportPreset> &p_preset, const Ref<Image> &p_main_image, const Ref<Image> &p_foreground, - const Ref<Image> &p_background); + const Ref<Image> &p_background, + const Ref<Image> &p_monochrome); static void _create_editor_debug_keystore_if_needed(); diff --git a/platform/android/java/app/build.gradle b/platform/android/java/app/build.gradle index fdc5753798..308f126d5d 100644 --- a/platform/android/java/app/build.gradle +++ b/platform/android/java/app/build.gradle @@ -63,6 +63,12 @@ dependencies { implementation files(pluginsBinaries) } + // Automatically pick up local dependencies in res://addons + String addonsDirectory = getAddonsDirectory() + if (addonsDirectory != null && !addonsDirectory.isBlank()) { + implementation fileTree(dir: "$addonsDirectory", include: ['*.jar', '*.aar']) + } + // .NET dependencies String jar = '../../../../modules/mono/thirdparty/libSystem.Security.Cryptography.Native.Android.jar' if (file(jar).exists()) { diff --git a/platform/android/java/app/config.gradle b/platform/android/java/app/config.gradle index 597a4d5c14..e8921e1bb1 100644 --- a/platform/android/java/app/config.gradle +++ b/platform/android/java/app/config.gradle @@ -408,3 +408,8 @@ ext.shouldUseLegacyPackaging = { -> // Default behavior for minSdk >= 23 return false } + +ext.getAddonsDirectory = { -> + String addonsDirectory = project.hasProperty("addons_directory") ? project.property("addons_directory") : "" + return addonsDirectory +} diff --git a/platform/android/java/editor/src/horizonos/java/org/godotengine/editor/GodotEditor.kt b/platform/android/java/editor/src/horizonos/java/org/godotengine/editor/GodotEditor.kt index 6cb08ae94b..9dc34f2267 100644 --- a/platform/android/java/editor/src/horizonos/java/org/godotengine/editor/GodotEditor.kt +++ b/platform/android/java/editor/src/horizonos/java/org/godotengine/editor/GodotEditor.kt @@ -45,14 +45,14 @@ open class GodotEditor : BaseGodotEditor() { internal val XR_RUN_GAME_INFO = EditorWindowInfo(GodotXRGame::class.java, 1667, ":GodotXRGame") - internal const val USE_SCENE_PERMISSION = "com.oculus.permission.USE_SCENE" + internal val USE_SCENE_PERMISSIONS = listOf("com.oculus.permission.USE_SCENE", "horizonos.permission.USE_SCENE") } override fun getExcludedPermissions(): MutableSet<String> { val excludedPermissions = super.getExcludedPermissions() // The USE_SCENE permission is requested when the "xr/openxr/enabled" project setting // is enabled. - excludedPermissions.add(USE_SCENE_PERMISSION) + excludedPermissions.addAll(USE_SCENE_PERMISSIONS) return excludedPermissions } diff --git a/platform/android/java/editor/src/horizonos/java/org/godotengine/editor/GodotXRGame.kt b/platform/android/java/editor/src/horizonos/java/org/godotengine/editor/GodotXRGame.kt index 5db2879aad..0c82791e89 100644 --- a/platform/android/java/editor/src/horizonos/java/org/godotengine/editor/GodotXRGame.kt +++ b/platform/android/java/editor/src/horizonos/java/org/godotengine/editor/GodotXRGame.kt @@ -69,7 +69,7 @@ open class GodotXRGame: GodotGame() { val automaticPermissionsRequestEnabled = automaticallyRequestPermissionsSetting.isNullOrEmpty() || automaticallyRequestPermissionsSetting.toBoolean() if (automaticPermissionsRequestEnabled) { - permissionsToEnable.add(USE_SCENE_PERMISSION) + permissionsToEnable.addAll(USE_SCENE_PERMISSIONS) } } diff --git a/platform/android/java/lib/res/mipmap-anydpi-v26/icon.xml b/platform/android/java/lib/res/mipmap-anydpi-v26/icon.xml index cfdcca2ab5..bb2ae6bee5 100644 --- a/platform/android/java/lib/res/mipmap-anydpi-v26/icon.xml +++ b/platform/android/java/lib/res/mipmap-anydpi-v26/icon.xml @@ -2,4 +2,5 @@ <adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android"> <background android:drawable="@mipmap/icon_background"/> <foreground android:drawable="@mipmap/icon_foreground"/> + <monochrome android:drawable="@mipmap/icon_monochrome"/> </adaptive-icon> diff --git a/platform/android/java/lib/res/mipmap/icon_monochrome.png b/platform/android/java/lib/res/mipmap/icon_monochrome.png Binary files differnew file mode 100644 index 0000000000..28f59ea119 --- /dev/null +++ b/platform/android/java/lib/res/mipmap/icon_monochrome.png 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 5b1d09e749..567b134234 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/Godot.kt +++ b/platform/android/java/lib/src/org/godotengine/godot/Godot.kt @@ -58,6 +58,8 @@ import org.godotengine.godot.input.GodotEditText import org.godotengine.godot.input.GodotInputHandler import org.godotengine.godot.io.directory.DirectoryAccessHandler import org.godotengine.godot.io.file.FileAccessHandler +import org.godotengine.godot.plugin.AndroidRuntimePlugin +import org.godotengine.godot.plugin.GodotPlugin import org.godotengine.godot.plugin.GodotPluginRegistry import org.godotengine.godot.tts.GodotTTS import org.godotengine.godot.utils.CommandLineFileParser @@ -228,7 +230,9 @@ class Godot(private val context: Context) { window.addFlags(WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON) Log.v(TAG, "Initializing Godot plugin registry") - GodotPluginRegistry.initializePluginRegistry(this, primaryHost.getHostPlugins(this)) + val runtimePlugins = mutableSetOf<GodotPlugin>(AndroidRuntimePlugin(this)) + runtimePlugins.addAll(primaryHost.getHostPlugins(this)) + GodotPluginRegistry.initializePluginRegistry(this, runtimePlugins) if (io == null) { io = GodotIO(activity) } 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 219631284a..f060c7aaff 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/GodotIO.java +++ b/platform/android/java/lib/src/org/godotengine/godot/GodotIO.java @@ -216,6 +216,14 @@ public class GodotIO { return result; } + public boolean hasHardwareKeyboard() { + if (edit != null) { + return edit.hasHardwareKeyboard(); + } else { + return false; + } + } + public void showKeyboard(String p_existing_text, int p_type, int p_max_input_length, int p_cursor_start, int p_cursor_end) { if (edit != null) { edit.showKeyboard(p_existing_text, GodotEditText.VirtualKeyboardType.values()[p_type], p_max_input_length, p_cursor_start, p_cursor_end); diff --git a/platform/android/java/lib/src/org/godotengine/godot/input/GodotEditText.java b/platform/android/java/lib/src/org/godotengine/godot/input/GodotEditText.java index c085bb8886..cacc1643e3 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/input/GodotEditText.java +++ b/platform/android/java/lib/src/org/godotengine/godot/input/GodotEditText.java @@ -264,7 +264,7 @@ public class GodotEditText extends EditText { isModifiedKey; } - boolean hasHardwareKeyboard() { + public boolean hasHardwareKeyboard() { Configuration config = getResources().getConfiguration(); boolean hasHardwareKeyboardConfig = config.keyboard != Configuration.KEYBOARD_NOKEYS && config.hardKeyboardHidden == Configuration.HARDKEYBOARDHIDDEN_NO; diff --git a/platform/android/java/lib/src/org/godotengine/godot/plugin/AndroidRuntimePlugin.kt b/platform/android/java/lib/src/org/godotengine/godot/plugin/AndroidRuntimePlugin.kt new file mode 100644 index 0000000000..edb4e7c357 --- /dev/null +++ b/platform/android/java/lib/src/org/godotengine/godot/plugin/AndroidRuntimePlugin.kt @@ -0,0 +1,63 @@ +/**************************************************************************/ +/* AndroidRuntimePlugin.kt */ +/**************************************************************************/ +/* 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. */ +/**************************************************************************/ + +package org.godotengine.godot.plugin + +import org.godotengine.godot.Godot + +/** + * Provides access to the Android runtime capabilities. + * + * For example, from gdscript, developers can use [getApplicationContext] to access system services + * and check if the device supports vibration. + * + * var android_runtime = Engine.get_singleton("AndroidRuntime") + * if android_runtime: + * print("Checking if the device supports vibration") + * var vibrator_service = android_runtime.getApplicationContext().getSystemService("vibrator") + * if vibrator_service: + * if vibrator_service.hasVibrator(): + * print("Vibration is supported on device!") + * else: + * printerr("Vibration is not supported on device") + * else: + * printerr("Unable to retrieve the vibrator service") + * else: + * printerr("Couldn't find AndroidRuntime singleton") + */ +class AndroidRuntimePlugin(godot: Godot) : GodotPlugin(godot) { + override fun getPluginName() = "AndroidRuntime" + + @UsedByGodot + fun getApplicationContext() = activity?.applicationContext + + @UsedByGodot + override fun getActivity() = super.getActivity() +} diff --git a/platform/android/java_class_wrapper.cpp b/platform/android/java_class_wrapper.cpp index c92717e922..6bedbfd157 100644 --- a/platform/android/java_class_wrapper.cpp +++ b/platform/android/java_class_wrapper.cpp @@ -1120,7 +1120,7 @@ bool JavaClass::_convert_object_to_variant(JNIEnv *env, jobject obj, Variant &va return false; } -Ref<JavaClass> JavaClassWrapper::wrap(const String &p_class) { +Ref<JavaClass> JavaClassWrapper::_wrap(const String &p_class, bool p_allow_private_methods_access) { String class_name_dots = p_class.replace("/", "."); if (class_cache.has(class_name_dots)) { return class_cache[class_name_dots]; @@ -1175,7 +1175,7 @@ Ref<JavaClass> JavaClassWrapper::wrap(const String &p_class) { jint mods = env->CallIntMethod(obj, is_constructor ? Constructor_getModifiers : Method_getModifiers); - if (!(mods & 0x0001)) { + if (!(mods & 0x0001) && (is_constructor || !p_allow_private_methods_access)) { env->DeleteLocalRef(obj); continue; //not public bye } @@ -1336,7 +1336,7 @@ Ref<JavaClass> JavaClassWrapper::wrap(const String &p_class) { return java_class; } -Ref<JavaClass> JavaClassWrapper::wrap_jclass(jclass p_class) { +Ref<JavaClass> JavaClassWrapper::wrap_jclass(jclass p_class, bool p_allow_private_methods_access) { JNIEnv *env = get_jni_env(); ERR_FAIL_NULL_V(env, Ref<JavaClass>()); @@ -1344,12 +1344,12 @@ Ref<JavaClass> JavaClassWrapper::wrap_jclass(jclass p_class) { String class_name_string = jstring_to_string(class_name, env); env->DeleteLocalRef(class_name); - return wrap(class_name_string); + return _wrap(class_name_string, p_allow_private_methods_access); } JavaClassWrapper *JavaClassWrapper::singleton = nullptr; -JavaClassWrapper::JavaClassWrapper(jobject p_activity) { +JavaClassWrapper::JavaClassWrapper() { singleton = this; JNIEnv *env = get_jni_env(); diff --git a/platform/android/java_godot_io_wrapper.cpp b/platform/android/java_godot_io_wrapper.cpp index 49913b9c30..623db39985 100644 --- a/platform/android/java_godot_io_wrapper.cpp +++ b/platform/android/java_godot_io_wrapper.cpp @@ -63,6 +63,7 @@ GodotIOJavaWrapper::GodotIOJavaWrapper(JNIEnv *p_env, jobject p_godot_io_instanc _get_unique_id = p_env->GetMethodID(cls, "getUniqueID", "()Ljava/lang/String;"); _show_keyboard = p_env->GetMethodID(cls, "showKeyboard", "(Ljava/lang/String;IIII)V"); _hide_keyboard = p_env->GetMethodID(cls, "hideKeyboard", "()V"); + _has_hardware_keyboard = p_env->GetMethodID(cls, "hasHardwareKeyboard", "()Z"); _set_screen_orientation = p_env->GetMethodID(cls, "setScreenOrientation", "(I)V"); _get_screen_orientation = p_env->GetMethodID(cls, "getScreenOrientation", "()I"); _get_system_dir = p_env->GetMethodID(cls, "getSystemDir", "(IZ)Ljava/lang/String;"); @@ -220,6 +221,16 @@ bool GodotIOJavaWrapper::has_vk() { return (_show_keyboard != nullptr) && (_hide_keyboard != nullptr); } +bool GodotIOJavaWrapper::has_hardware_keyboard() { + if (_has_hardware_keyboard) { + JNIEnv *env = get_jni_env(); + ERR_FAIL_NULL_V(env, false); + return env->CallBooleanMethod(godot_io_instance, _has_hardware_keyboard); + } else { + return false; + } +} + void GodotIOJavaWrapper::show_vk(const String &p_existing, int p_type, int p_max_input_length, int p_cursor_start, int p_cursor_end) { if (_show_keyboard) { JNIEnv *env = get_jni_env(); diff --git a/platform/android/java_godot_io_wrapper.h b/platform/android/java_godot_io_wrapper.h index c113a13040..0a372641cb 100644 --- a/platform/android/java_godot_io_wrapper.h +++ b/platform/android/java_godot_io_wrapper.h @@ -58,6 +58,7 @@ private: jmethodID _get_unique_id = 0; jmethodID _show_keyboard = 0; jmethodID _hide_keyboard = 0; + jmethodID _has_hardware_keyboard = 0; jmethodID _set_screen_orientation = 0; jmethodID _get_screen_orientation = 0; jmethodID _get_system_dir = 0; @@ -80,6 +81,7 @@ public: Rect2i get_display_safe_area(); String get_unique_id(); bool has_vk(); + bool has_hardware_keyboard(); void show_vk(const String &p_existing, int p_type, int p_max_input_length, int p_cursor_start, int p_cursor_end); void hide_vk(); int get_vk_height(); diff --git a/platform/android/java_godot_lib_jni.cpp b/platform/android/java_godot_lib_jni.cpp index 6086f67a1e..1a256959cd 100644 --- a/platform/android/java_godot_lib_jni.cpp +++ b/platform/android/java_godot_lib_jni.cpp @@ -32,7 +32,6 @@ #include "android_input_handler.h" #include "api/java_class_wrapper.h" -#include "api/jni_singleton.h" #include "dir_access_jandroid.h" #include "display_server_android.h" #include "file_access_android.h" @@ -209,8 +208,7 @@ JNIEXPORT jboolean JNICALL Java_org_godotengine_godot_GodotLib_setup(JNIEnv *env TTS_Android::setup(p_godot_tts); - java_class_wrapper = memnew(JavaClassWrapper(godot_java->get_activity())); - GDREGISTER_CLASS(JNISingleton); + java_class_wrapper = memnew(JavaClassWrapper); return true; } diff --git a/platform/android/plugin/godot_plugin_jni.cpp b/platform/android/plugin/godot_plugin_jni.cpp index 75c8dd9528..acb18cc5c5 100644 --- a/platform/android/plugin/godot_plugin_jni.cpp +++ b/platform/android/plugin/godot_plugin_jni.cpp @@ -30,6 +30,7 @@ #include "godot_plugin_jni.h" +#include "api/java_class_wrapper.h" #include "api/jni_singleton.h" #include "jni_utils.h" #include "string_android.h" @@ -57,11 +58,15 @@ JNIEXPORT jboolean JNICALL Java_org_godotengine_godot_plugin_GodotPlugin_nativeR ERR_FAIL_COND_V(jni_singletons.has(singname), false); - JNISingleton *s = (JNISingleton *)ClassDB::instantiate("JNISingleton"); - s->set_instance(env->NewGlobalRef(obj)); - jni_singletons[singname] = s; + jclass java_class = env->GetObjectClass(obj); + Ref<JavaClass> java_class_wrapped = JavaClassWrapper::get_singleton()->wrap_jclass(java_class, true); + env->DeleteLocalRef(java_class); - Engine::get_singleton()->add_singleton(Engine::Singleton(singname, s)); + Ref<JavaObject> plugin_object = memnew(JavaObject(java_class_wrapped, obj)); + JNISingleton *plugin_singleton = memnew(JNISingleton(plugin_object)); + jni_singletons[singname] = plugin_singleton; + + Engine::get_singleton()->add_singleton(Engine::Singleton(singname, plugin_singleton)); return true; } @@ -75,7 +80,6 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_plugin_GodotPlugin_nativeRegis String mname = jstring_to_string(name, env); String retval = jstring_to_string(ret, env); Vector<Variant::Type> types; - String cs = "("; int stringCount = env->GetArrayLength(args); @@ -83,18 +87,9 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_plugin_GodotPlugin_nativeRegis jstring string = (jstring)env->GetObjectArrayElement(args, i); const String rawString = jstring_to_string(string, env); types.push_back(get_jni_type(rawString)); - cs += get_jni_sig(rawString); - } - - cs += ")"; - cs += get_jni_sig(retval); - jclass cls = env->GetObjectClass(s->get_instance()); - jmethodID mid = env->GetMethodID(cls, mname.ascii().get_data(), cs.ascii().get_data()); - if (!mid) { - print_line("Failed getting method ID " + mname); } - s->add_method(mname, mid, types, get_jni_type(retval)); + s->add_method(mname, types, get_jni_type(retval)); } JNIEXPORT void JNICALL Java_org_godotengine_godot_plugin_GodotPlugin_nativeRegisterSignal(JNIEnv *env, jclass clazz, jstring j_plugin_name, jstring j_signal_name, jobjectArray j_signal_param_types) { diff --git a/platform/android/rendering_context_driver_vulkan_android.cpp b/platform/android/rendering_context_driver_vulkan_android.cpp index a306a121f8..51fb1ca18f 100644 --- a/platform/android/rendering_context_driver_vulkan_android.cpp +++ b/platform/android/rendering_context_driver_vulkan_android.cpp @@ -32,11 +32,7 @@ #ifdef VULKAN_ENABLED -#ifdef USE_VOLK -#include <volk.h> -#else -#include <vulkan/vulkan.h> -#endif +#include "drivers/vulkan/godot_vulkan.h" const char *RenderingContextDriverVulkanAndroid::_get_platform_surface_extension() const { return VK_KHR_ANDROID_SURFACE_EXTENSION_NAME; diff --git a/platform/ios/SCsub b/platform/ios/SCsub index cff7dcc1fd..959a657aac 100644 --- a/platform/ios/SCsub +++ b/platform/ios/SCsub @@ -1,4 +1,5 @@ #!/usr/bin/env python +from misc.utility.scons_hints import * Import("env") diff --git a/platform/ios/detect.py b/platform/ios/detect.py index 989a7f21f3..20a3a996bc 100644 --- a/platform/ios/detect.py +++ b/platform/ios/detect.py @@ -2,7 +2,7 @@ import os import sys from typing import TYPE_CHECKING -from methods import detect_darwin_sdk_path, print_error +from methods import detect_darwin_sdk_path, print_error, print_warning if TYPE_CHECKING: from SCons.Script.SConscript import SConsEnvironment @@ -156,7 +156,7 @@ def configure(env: "SConsEnvironment"): env.Append(CPPDEFINES=["IOS_ENABLED", "UNIX_ENABLED", "COREAUDIO_ENABLED"]) if env["metal"] and env["arch"] != "arm64": - # Only supported on arm64, so skip it for x86_64 builds. + print_warning("Target architecture '{}' does not support the Metal rendering driver".format(env["arch"])) env["metal"] = False if env["metal"]: diff --git a/platform/ios/display_server_ios.h b/platform/ios/display_server_ios.h index bbb758074d..7f199db997 100644 --- a/platform/ios/display_server_ios.h +++ b/platform/ios/display_server_ios.h @@ -41,11 +41,7 @@ #if defined(VULKAN_ENABLED) #import "rendering_context_driver_vulkan_ios.h" -#ifdef USE_VOLK -#include <volk.h> -#else -#include <vulkan/vulkan.h> -#endif +#include "drivers/vulkan/godot_vulkan.h" #endif // VULKAN_ENABLED #if defined(METAL_ENABLED) @@ -228,6 +224,7 @@ public: void virtual_keyboard_set_height(int height); virtual int virtual_keyboard_get_height() const override; + virtual bool has_hardware_keyboard() const override; virtual void clipboard_set(const String &p_text) override; virtual String clipboard_get() const override; diff --git a/platform/ios/display_server_ios.mm b/platform/ios/display_server_ios.mm index e51d43bd89..dcc6ce9218 100644 --- a/platform/ios/display_server_ios.mm +++ b/platform/ios/display_server_ios.mm @@ -45,6 +45,8 @@ #import <sys/utsname.h> +#import <GameController/GameController.h> + static const float kDisplayServerIOSAcceleration = 1.f; DisplayServerIOS *DisplayServerIOS::get_singleton() { @@ -756,6 +758,14 @@ int DisplayServerIOS::virtual_keyboard_get_height() const { return virtual_keyboard_height; } +bool DisplayServerIOS::has_hardware_keyboard() const { + if (@available(iOS 14.0, *)) { + return [GCKeyboard coalescedKeyboard]; + } else { + return false; + } +} + void DisplayServerIOS::clipboard_set(const String &p_text) { [UIPasteboard generalPasteboard].string = [NSString stringWithUTF8String:p_text.utf8()]; } diff --git a/platform/ios/doc_classes/EditorExportPlatformIOS.xml b/platform/ios/doc_classes/EditorExportPlatformIOS.xml index 1d4a944dc4..9e6f191faa 100644 --- a/platform/ios/doc_classes/EditorExportPlatformIOS.xml +++ b/platform/ios/doc_classes/EditorExportPlatformIOS.xml @@ -96,39 +96,156 @@ <member name="icons/app_store_1024x1024" type="String" setter="" getter=""> App Store application icon file. 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="icons/ipad_76x76" type="String" setter="" getter=""> - Home screen application icon file on iPad (1x 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 name="icons/app_store_1024x1024_dark" type="String" setter="" getter=""> + App Store application icon file, dark version. See [url=https://developer.apple.com/design/human-interface-guidelines/foundations/app-icons]App icons[/url]. + </member> + <member name="icons/app_store_1024x1024_tinted" type="String" setter="" getter=""> + App Store application icon file, tinted version. See [url=https://developer.apple.com/design/human-interface-guidelines/foundations/app-icons]App icons[/url]. + </member> + <member name="icons/icon_1024x1024" type="String" setter="" getter=""> + Base application icon used to generate other icons. 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="icons/icon_1024x1024_dark" type="String" setter="" getter=""> + Base application icon used to generate other icons, dark version. See [url=https://developer.apple.com/design/human-interface-guidelines/foundations/app-icons]App icons[/url]. + </member> + <member name="icons/icon_1024x1024_tinted" type="String" setter="" getter=""> + Base application icon used to generate other icons, tinted version. See [url=https://developer.apple.com/design/human-interface-guidelines/foundations/app-icons]App icons[/url]. + </member> + <member name="icons/ios_128x128" type="String" setter="" getter=""> + iOS application 64x64 icon file (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="icons/ios_128x128_dark" type="String" setter="" getter=""> + iOS application 64x64 icon file (2x DPI), dark version. See [url=https://developer.apple.com/design/human-interface-guidelines/foundations/app-icons]App icons[/url]. + </member> + <member name="icons/ios_128x128_tinted" type="String" setter="" getter=""> + iOS application 64x64 icon file (2x DPI), tinted version. See [url=https://developer.apple.com/design/human-interface-guidelines/foundations/app-icons]App icons[/url]. + </member> + <member name="icons/ios_136x136" type="String" setter="" getter=""> + iOS application 68x68 icon file (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="icons/ios_136x136_dark" type="String" setter="" getter=""> + iOS application 68x68 icon file (2x DPI), dark version. See [url=https://developer.apple.com/design/human-interface-guidelines/foundations/app-icons]App icons[/url]. + </member> + <member name="icons/ios_136x136_tinted" type="String" setter="" getter=""> + iOS application 68x68 icon file (2x DPI), tinted version. See [url=https://developer.apple.com/design/human-interface-guidelines/foundations/app-icons]App icons[/url]. + </member> + <member name="icons/ios_192x192" type="String" setter="" getter=""> + iOS application 64x64 icon file (3x 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="icons/ios_192x192_dark" type="String" setter="" getter=""> + iOS application 64x64 icon file (3x DPI), dark version. See [url=https://developer.apple.com/design/human-interface-guidelines/foundations/app-icons]App icons[/url]. + </member> + <member name="icons/ios_192x192_tinted" type="String" setter="" getter=""> + iOS application 64x64 icon file (3x DPI), tinted version. See [url=https://developer.apple.com/design/human-interface-guidelines/foundations/app-icons]App icons[/url]. </member> <member name="icons/ipad_152x152" type="String" setter="" getter=""> Home screen application icon file on iPad (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="icons/ipad_152x152_dark" type="String" setter="" getter=""> + Home screen application icon file on iPad (2x DPI), dark version. See [url=https://developer.apple.com/design/human-interface-guidelines/foundations/app-icons]App icons[/url]. + </member> + <member name="icons/ipad_152x152_tinted" type="String" setter="" getter=""> + Home screen application icon file on iPad (2x DPI), tinted version. See [url=https://developer.apple.com/design/human-interface-guidelines/foundations/app-icons]App icons[/url]. + </member> <member name="icons/ipad_167x167" type="String" setter="" getter=""> Home screen application icon file on iPad (3x 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="icons/ipad_167x167_dark" type="String" setter="" getter=""> + Home screen application icon file on iPad (3x DPI), dark version. See [url=https://developer.apple.com/design/human-interface-guidelines/foundations/app-icons]App icons[/url]. + </member> + <member name="icons/ipad_167x167_tinted" type="String" setter="" getter=""> + Home screen application icon file on iPad (3x DPI), tinted version. See [url=https://developer.apple.com/design/human-interface-guidelines/foundations/app-icons]App icons[/url]. + </member> <member name="icons/iphone_120x120" type="String" setter="" getter=""> Home screen application icon file on 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="icons/iphone_120x120_dark" type="String" setter="" getter=""> + Home screen application icon file on iPhone (2x DPI), dark version. See [url=https://developer.apple.com/design/human-interface-guidelines/foundations/app-icons]App icons[/url]. + </member> + <member name="icons/iphone_120x120_tinted" type="String" setter="" getter=""> + Home screen application icon file on iPhone (2x DPI), tinted version. See [url=https://developer.apple.com/design/human-interface-guidelines/foundations/app-icons]App icons[/url]. + </member> <member name="icons/iphone_180x180" type="String" setter="" getter=""> Home screen application icon file on iPhone (3x 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="icons/iphone_180x180_dark" type="String" setter="" getter=""> + Home screen application icon file on iPhone (3x DPI), dark version. See [url=https://developer.apple.com/design/human-interface-guidelines/foundations/app-icons]App icons[/url]. + </member> + <member name="icons/iphone_180x180_tinted" type="String" setter="" getter=""> + Home screen application icon file on iPhone (3x DPI), tinted version. See [url=https://developer.apple.com/design/human-interface-guidelines/foundations/app-icons]App icons[/url]. + </member> <member name="icons/notification_40x40" type="String" setter="" getter=""> Notification 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="icons/notification_40x40_dark" type="String" setter="" getter=""> + Notification icon file on iPad and iPhone (2x DPI), dark version. See [url=https://developer.apple.com/design/human-interface-guidelines/foundations/app-icons]App icons[/url]. + </member> + <member name="icons/notification_40x40_tinted" type="String" setter="" getter=""> + Notification icon file on iPad and iPhone (2x DPI), tinted version. See [url=https://developer.apple.com/design/human-interface-guidelines/foundations/app-icons]App icons[/url]. + </member> <member name="icons/notification_60x60" type="String" setter="" getter=""> Notification icon file on iPhone (3x 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="icons/notification_60x60_dark" type="String" setter="" getter=""> + Notification icon file on iPhone (3x DPI), dark version. See [url=https://developer.apple.com/design/human-interface-guidelines/foundations/app-icons]App icons[/url]. + </member> + <member name="icons/notification_60x60_tinted" type="String" setter="" getter=""> + Notification icon file on iPhone (3x DPI), tinted version. See [url=https://developer.apple.com/design/human-interface-guidelines/foundations/app-icons]App icons[/url]. + </member> + <member name="icons/notification_76x76" type="String" setter="" getter=""> + Notification 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="icons/notification_76x76_dark" type="String" setter="" getter=""> + Notification icon file on iPad and iPhone (2x DPI), dark version. See [url=https://developer.apple.com/design/human-interface-guidelines/foundations/app-icons]App icons[/url]. + </member> + <member name="icons/notification_76x76_tinted" type="String" setter="" getter=""> + Notification icon file on iPad and iPhone (2x DPI), tinted version. See [url=https://developer.apple.com/design/human-interface-guidelines/foundations/app-icons]App icons[/url]. + </member> + <member name="icons/notification_114x114" type="String" setter="" getter=""> + Notification icon file on iPad and iPhone (3x 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="icons/notification_114x114_dark" type="String" setter="" getter=""> + Notification icon file on iPad and iPhone (3x DPI), dark version. See [url=https://developer.apple.com/design/human-interface-guidelines/foundations/app-icons]App icons[/url]. + </member> + <member name="icons/notification_114x114_tinted" type="String" setter="" getter=""> + Notification icon file on iPad and iPhone (3x DPI), tinted version. See [url=https://developer.apple.com/design/human-interface-guidelines/foundations/app-icons]App icons[/url]. + </member> <member name="icons/settings_58x58" type="String" setter="" getter=""> Application settings 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="icons/settings_58x58_dark" type="String" setter="" getter=""> + Application settings icon file on iPad and iPhone (2x DPI), dark version. See [url=https://developer.apple.com/design/human-interface-guidelines/foundations/app-icons]App icons[/url]. + </member> + <member name="icons/settings_58x58_tinted" type="String" setter="" getter=""> + Application settings icon file on iPad and iPhone (2x DPI), tinted version. See [url=https://developer.apple.com/design/human-interface-guidelines/foundations/app-icons]App icons[/url]. + </member> <member name="icons/settings_87x87" type="String" setter="" getter=""> Application settings icon file on iPhone (3x 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="icons/spotlight_40x40" type="String" setter="" getter=""> - Spotlight icon file on iPad (1x 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 name="icons/settings_87x87_dark" type="String" setter="" getter=""> + Application settings icon file on iPhone (3x DPI), dark version. See [url=https://developer.apple.com/design/human-interface-guidelines/foundations/app-icons]App icons[/url]. + </member> + <member name="icons/settings_87x87_tinted" type="String" setter="" getter=""> + Application settings icon file on iPhone (3x DPI), tinted version. See [url=https://developer.apple.com/design/human-interface-guidelines/foundations/app-icons]App icons[/url]. </member> <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="icons/spotlight_80x80_dark" type="String" setter="" getter=""> + Spotlight icon file on iPad and iPhone (2x DPI), dark version. See [url=https://developer.apple.com/design/human-interface-guidelines/foundations/app-icons]App icons[/url]. + </member> + <member name="icons/spotlight_80x80_tinted" type="String" setter="" getter=""> + Spotlight icon file on iPad and iPhone (2x DPI), tinted version. See [url=https://developer.apple.com/design/human-interface-guidelines/foundations/app-icons]App icons[/url]. + </member> + <member name="icons/spotlight_120x120" type="String" setter="" getter=""> + Spotlight icon file on iPad and iPhone (3x 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="icons/spotlight_120x120_dark" type="String" setter="" getter=""> + Spotlight icon file on iPad and iPhone (3x DPI), dark version. See [url=https://developer.apple.com/design/human-interface-guidelines/foundations/app-icons]App icons[/url]. + </member> + <member name="icons/spotlight_120x120_tinted" type="String" setter="" getter=""> + Spotlight icon file on iPad and iPhone (3x DPI), tinted version. See [url=https://developer.apple.com/design/human-interface-guidelines/foundations/app-icons]App icons[/url]. + </member> <member name="privacy/active_keyboard_access_reasons" type="int" setter="" getter=""> The reasons your app use active keyboard API. See [url=https://developer.apple.com/documentation/bundleresources/privacy_manifest_files/describing_use_of_required_reason_api]Describing use of required reason API[/url]. </member> diff --git a/platform/ios/export/export_plugin.cpp b/platform/ios/export/export_plugin.cpp index b99e825540..d6cd2e0f3c 100644 --- a/platform/ios/export/export_plugin.cpp +++ b/platform/ios/export/export_plugin.cpp @@ -79,33 +79,37 @@ struct IconInfo { }; static const IconInfo icon_infos[] = { - // Home screen on iPhone - { PNAME("icons/iphone_120x120"), "iphone", "Icon-120.png", "120", "2x", "60x60", false }, - { PNAME("icons/iphone_120x120"), "iphone", "Icon-120.png", "120", "3x", "40x40", false }, - { PNAME("icons/iphone_180x180"), "iphone", "Icon-180.png", "180", "3x", "60x60", false }, + // Settings on iPhone, iPad Pro, iPad, iPad mini + { PNAME("icons/settings_58x58"), "universal", "Icon-58", "58", "2x", "29x29", false }, + { PNAME("icons/settings_87x87"), "universal", "Icon-87", "87", "3x", "29x29", false }, - // Home screen on iPad - { PNAME("icons/ipad_76x76"), "ipad", "Icon-76.png", "76", "1x", "76x76", false }, - { PNAME("icons/ipad_152x152"), "ipad", "Icon-152.png", "152", "2x", "76x76", false }, - { PNAME("icons/ipad_167x167"), "ipad", "Icon-167.png", "167", "2x", "83.5x83.5", false }, + // Notifications on iPhone, iPad Pro, iPad, iPad mini + { PNAME("icons/notification_40x40"), "universal", "Icon-40", "40", "2x", "20x20", false }, + { PNAME("icons/notification_60x60"), "universal", "Icon-60", "60", "3x", "20x20", false }, + { PNAME("icons/notification_76x76"), "universal", "Icon-76", "76", "2x", "38x38", false }, + { PNAME("icons/notification_114x114"), "universal", "Icon-114", "114", "3x", "38x38", false }, + + // Spotlight on iPhone, iPad Pro, iPad, iPad mini + { PNAME("icons/spotlight_80x80"), "universal", "Icon-80", "80", "2x", "40x40", false }, + { PNAME("icons/spotlight_120x120"), "universal", "Icon-120", "120", "3x", "40x40", false }, + + // Home Screen on iPhone + { PNAME("icons/iphone_120x120"), "universal", "Icon-120-1", "120", "2x", "60x60", false }, + { PNAME("icons/iphone_180x180"), "universal", "Icon-180", "180", "3x", "60x60", false }, + + // Home Screen on iPad Pro + { PNAME("icons/ipad_167x167"), "universal", "Icon-167", "167", "2x", "83.5x83.5", false }, + + // Home Screen on iPad, iPad mini + { PNAME("icons/ipad_152x152"), "universal", "Icon-152", "152", "2x", "76x76", false }, + + { PNAME("icons/ios_128x128"), "universal", "Icon-128", "128", "2x", "64x64", false }, + { PNAME("icons/ios_192x192"), "universal", "Icon-192", "192", "3x", "64x64", false }, + + { PNAME("icons/ios_136x136"), "universal", "Icon-136", "136", "2x", "68x68", false }, // App Store - { PNAME("icons/app_store_1024x1024"), "ios-marketing", "Icon-1024.png", "1024", "1x", "1024x1024", true }, - - // Spotlight - { PNAME("icons/spotlight_40x40"), "ipad", "Icon-40.png", "40", "1x", "40x40", false }, - { PNAME("icons/spotlight_80x80"), "iphone", "Icon-80.png", "80", "2x", "40x40", false }, - { PNAME("icons/spotlight_80x80"), "ipad", "Icon-80.png", "80", "2x", "40x40", false }, - - // Settings - { PNAME("icons/settings_58x58"), "iphone", "Icon-58.png", "58", "2x", "29x29", false }, - { PNAME("icons/settings_58x58"), "ipad", "Icon-58.png", "58", "2x", "29x29", false }, - { PNAME("icons/settings_87x87"), "iphone", "Icon-87.png", "87", "3x", "29x29", false }, - - // Notification - { PNAME("icons/notification_40x40"), "iphone", "Icon-40.png", "40", "2x", "20x20", false }, - { PNAME("icons/notification_40x40"), "ipad", "Icon-40.png", "40", "2x", "20x20", false }, - { PNAME("icons/notification_60x60"), "iphone", "Icon-60.png", "60", "3x", "20x20", false } + { PNAME("icons/app_store_1024x1024"), "universal", "Icon-1024", "1024", "1x", "1024x1024", true }, }; struct APIAccessInfo { @@ -250,7 +254,7 @@ bool EditorExportPlatformIOS::get_export_option_visibility(const EditorExportPre } bool advanced_options_enabled = p_preset->are_advanced_options_enabled(); - if (p_option.begins_with("privacy") || p_option == "application/generate_simulator_library_if_missing") { + if (p_option.begins_with("privacy") || p_option == "application/generate_simulator_library_if_missing" || (p_option.begins_with("icons/") && !p_option.begins_with("icons/icon") && !p_option.begins_with("icons/app_store"))) { return advanced_options_enabled; } @@ -368,11 +372,17 @@ void EditorExportPlatformIOS::get_export_options(List<ExportOption> *r_options) } } + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "icons/icon_1024x1024", PROPERTY_HINT_FILE, "*.svg,*.png,*.webp,*.jpg,*.jpeg"), "")); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "icons/icon_1024x1024_dark", PROPERTY_HINT_FILE, "*.svg,*.png,*.webp,*.jpg,*.jpeg"), "")); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "icons/icon_1024x1024_tinted", PROPERTY_HINT_FILE, "*.svg,*.png,*.webp,*.jpg,*.jpeg"), "")); + HashSet<String> used_names; for (uint64_t i = 0; i < sizeof(icon_infos) / sizeof(icon_infos[0]); ++i) { if (!used_names.has(icon_infos[i].preset_key)) { used_names.insert(icon_infos[i].preset_key); - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, icon_infos[i].preset_key, PROPERTY_HINT_FILE, "*.png,*.jpg,*.jpeg"), "")); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, String(icon_infos[i].preset_key), PROPERTY_HINT_FILE, "*.png,*.jpg,*.jpeg"), "")); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, String(icon_infos[i].preset_key) + "_dark", PROPERTY_HINT_FILE, "*.png,*.jpg,*.jpeg"), "")); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, String(icon_infos[i].preset_key) + "_tinted", PROPERTY_HINT_FILE, "*.png,*.jpg,*.jpeg"), "")); } } 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)); @@ -883,72 +893,127 @@ Error EditorExportPlatformIOS::_export_icons(const Ref<EditorExportPreset> &p_pr Color boot_bg_color = GLOBAL_GET("application/boot_splash/bg_color"); + enum IconColorMode { + ICON_NORMAL, + ICON_DARK, + ICON_TINTED, + ICON_MAX, + }; + + bool first_icon = true; for (uint64_t i = 0; i < (sizeof(icon_infos) / sizeof(icon_infos[0])); ++i) { - IconInfo info = icon_infos[i]; - int side_size = String(info.actual_size_side).to_int(); - String icon_path = p_preset->get(info.preset_key); - if (icon_path.length() == 0) { - // Resize main app icon - icon_path = GLOBAL_GET("application/config/icon"); - Ref<Image> img = memnew(Image); - Error err = ImageLoader::load_image(icon_path, img); - if (err != OK) { - add_message(EXPORT_MESSAGE_ERROR, TTR("Export Icons"), vformat("Invalid icon (%s): '%s'.", info.preset_key, icon_path)); - return ERR_UNCONFIGURED; - } else if (info.force_opaque && img->detect_alpha() != Image::ALPHA_NONE) { - add_message(EXPORT_MESSAGE_WARNING, TTR("Export Icons"), vformat("Icon (%s) must be opaque.", info.preset_key)); - img->resize(side_size, side_size, (Image::Interpolation)(p_preset->get("application/icon_interpolation").operator int())); - Ref<Image> new_img = Image::create_empty(side_size, side_size, false, Image::FORMAT_RGBA8); - new_img->fill(boot_bg_color); - _blend_and_rotate(new_img, img, false); - err = new_img->save_png(p_iconset_dir + info.export_name); - } else { - img->resize(side_size, side_size, (Image::Interpolation)(p_preset->get("application/icon_interpolation").operator int())); - err = img->save_png(p_iconset_dir + info.export_name); + for (int color_mode = ICON_NORMAL; color_mode < ICON_MAX; color_mode++) { + IconInfo info = icon_infos[i]; + int side_size = String(info.actual_size_side).to_int(); + String key = info.preset_key; + String exp_name = info.export_name; + if (color_mode == ICON_DARK) { + key += "_dark"; + exp_name += "_dark"; + } else if (color_mode == ICON_TINTED) { + key += "_tinted"; + exp_name += "_tinted"; + } + exp_name += ".png"; + String icon_path = p_preset->get(key); + bool resize_waning = true; + if (icon_path.is_empty()) { + // Load and resize base icon. + key = "icons/icon_1024x1024"; + if (color_mode == ICON_DARK) { + key += "_dark"; + } else if (color_mode == ICON_TINTED) { + key += "_tinted"; + } + icon_path = p_preset->get(key); + resize_waning = false; } - if (err) { - add_message(EXPORT_MESSAGE_ERROR, TTR("Export Icons"), vformat("Failed to export icon (%s): '%s'.", info.preset_key, icon_path)); - return err; + if (icon_path.is_empty()) { + if (color_mode != ICON_NORMAL) { + continue; + } + // Resize main app icon. + icon_path = GLOBAL_GET("application/config/icon"); + Ref<Image> img = memnew(Image); + Error err = ImageLoader::load_image(icon_path, img); + if (err != OK) { + add_message(EXPORT_MESSAGE_ERROR, TTR("Export Icons"), vformat("Invalid icon (%s): '%s'.", info.preset_key, icon_path)); + return ERR_UNCONFIGURED; + } else if (info.force_opaque && img->detect_alpha() != Image::ALPHA_NONE) { + img->resize(side_size, side_size, (Image::Interpolation)(p_preset->get("application/icon_interpolation").operator int())); + Ref<Image> new_img = Image::create_empty(side_size, side_size, false, Image::FORMAT_RGBA8); + new_img->fill(boot_bg_color); + _blend_and_rotate(new_img, img, false); + err = new_img->save_png(p_iconset_dir + exp_name); + } else { + img->resize(side_size, side_size, (Image::Interpolation)(p_preset->get("application/icon_interpolation").operator int())); + err = img->save_png(p_iconset_dir + exp_name); + } + if (err) { + add_message(EXPORT_MESSAGE_ERROR, TTR("Export Icons"), vformat("Failed to export icon (%s): '%s'.", info.preset_key, icon_path)); + return err; + } + } else { + // Load custom icon and resize if required. + Ref<Image> img = memnew(Image); + Error err = ImageLoader::load_image(icon_path, img); + if (err != OK) { + add_message(EXPORT_MESSAGE_ERROR, TTR("Export Icons"), vformat("Invalid icon (%s): '%s'.", info.preset_key, icon_path)); + return ERR_UNCONFIGURED; + } else if (info.force_opaque && img->detect_alpha() != Image::ALPHA_NONE) { + if (resize_waning) { + add_message(EXPORT_MESSAGE_WARNING, TTR("Export Icons"), vformat("Icon (%s) must be opaque.", info.preset_key)); + } + img->resize(side_size, side_size, (Image::Interpolation)(p_preset->get("application/icon_interpolation").operator int())); + Ref<Image> new_img = Image::create_empty(side_size, side_size, false, Image::FORMAT_RGBA8); + new_img->fill(boot_bg_color); + _blend_and_rotate(new_img, img, false); + err = new_img->save_png(p_iconset_dir + exp_name); + } else if (img->get_width() != side_size || img->get_height() != side_size) { + if (resize_waning) { + add_message(EXPORT_MESSAGE_WARNING, TTR("Export Icons"), vformat("Icon (%s): '%s' has incorrect size %s and was automatically resized to %s.", info.preset_key, icon_path, img->get_size(), Vector2i(side_size, side_size))); + } + img->resize(side_size, side_size, (Image::Interpolation)(p_preset->get("application/icon_interpolation").operator int())); + err = img->save_png(p_iconset_dir + exp_name); + } else if (!icon_path.ends_with(".png")) { + err = img->save_png(p_iconset_dir + exp_name); + } else { + err = da->copy(icon_path, p_iconset_dir + exp_name); + } + + if (err) { + add_message(EXPORT_MESSAGE_ERROR, TTR("Export Icons"), vformat("Failed to export icon (%s): '%s'.", info.preset_key, icon_path)); + return err; + } } - } else { - // Load custom icon and resize if required - Ref<Image> img = memnew(Image); - Error err = ImageLoader::load_image(icon_path, img); - if (err != OK) { - add_message(EXPORT_MESSAGE_ERROR, TTR("Export Icons"), vformat("Invalid icon (%s): '%s'.", info.preset_key, icon_path)); - return ERR_UNCONFIGURED; - } else if (info.force_opaque && img->detect_alpha() != Image::ALPHA_NONE) { - add_message(EXPORT_MESSAGE_WARNING, TTR("Export Icons"), vformat("Icon (%s) must be opaque.", info.preset_key)); - img->resize(side_size, side_size, (Image::Interpolation)(p_preset->get("application/icon_interpolation").operator int())); - Ref<Image> new_img = Image::create_empty(side_size, side_size, false, Image::FORMAT_RGBA8); - new_img->fill(boot_bg_color); - _blend_and_rotate(new_img, img, false); - err = new_img->save_png(p_iconset_dir + info.export_name); - } else if (img->get_width() != side_size || img->get_height() != side_size) { - add_message(EXPORT_MESSAGE_WARNING, TTR("Export Icons"), vformat("Icon (%s): '%s' has incorrect size %s and was automatically resized to %s.", info.preset_key, icon_path, img->get_size(), Vector2i(side_size, side_size))); - img->resize(side_size, side_size, (Image::Interpolation)(p_preset->get("application/icon_interpolation").operator int())); - err = img->save_png(p_iconset_dir + info.export_name); + sizes += String(info.actual_size_side) + "\n"; + if (first_icon) { + first_icon = false; } else { - err = da->copy(icon_path, p_iconset_dir + info.export_name); + json_description += ","; + } + json_description += String("{"); + if (color_mode != ICON_NORMAL) { + json_description += String("\"appearances\":[{"); + json_description += String("\"appearance\":\"luminosity\","); + if (color_mode == ICON_DARK) { + json_description += String("\"value\":\"dark\""); + } else if (color_mode == ICON_TINTED) { + json_description += String("\"value\":\"tinted\""); + } + json_description += String("}],"); } - - if (err) { - add_message(EXPORT_MESSAGE_ERROR, TTR("Export Icons"), vformat("Failed to export icon (%s): '%s'.", info.preset_key, icon_path)); - return err; + json_description += String("\"idiom\":") + "\"" + info.idiom + "\","; + json_description += String("\"platform\":\"ios\","); + json_description += String("\"size\":") + "\"" + info.unscaled_size + "\","; + if (String(info.scale) != "1x") { + json_description += String("\"scale\":") + "\"" + info.scale + "\","; } + json_description += String("\"filename\":") + "\"" + exp_name + "\""; + json_description += String("}"); } - sizes += String(info.actual_size_side) + "\n"; - if (i > 0) { - json_description += ","; - } - json_description += String("{"); - json_description += String("\"idiom\":") + "\"" + info.idiom + "\","; - json_description += String("\"size\":") + "\"" + info.unscaled_size + "\","; - json_description += String("\"scale\":") + "\"" + info.scale + "\","; - json_description += String("\"filename\":") + "\"" + info.export_name + "\""; - json_description += String("}"); } - json_description += "]}"; + json_description += "],\"info\":{\"author\":\"xcode\",\"version\":1}}"; Ref<FileAccess> json_file = FileAccess::open(p_iconset_dir + "Contents.json", FileAccess::WRITE); if (json_file.is_null()) { diff --git a/platform/ios/os_ios.mm b/platform/ios/os_ios.mm index 35b87ea647..590238be77 100644 --- a/platform/ios/os_ios.mm +++ b/platform/ios/os_ios.mm @@ -56,11 +56,7 @@ #import <QuartzCore/CAMetalLayer.h> #if defined(VULKAN_ENABLED) -#ifdef USE_VOLK -#include <volk.h> -#else -#include <vulkan/vulkan.h> -#endif +#include "drivers/vulkan/godot_vulkan.h" #endif // VULKAN_ENABLED #endif diff --git a/platform/linuxbsd/SCsub b/platform/linuxbsd/SCsub index 0802b528f4..4def765e9c 100644 --- a/platform/linuxbsd/SCsub +++ b/platform/linuxbsd/SCsub @@ -1,4 +1,5 @@ #!/usr/bin/env python +from misc.utility.scons_hints import * Import("env") diff --git a/platform/linuxbsd/detect.py b/platform/linuxbsd/detect.py index d1de760f34..a67434527c 100644 --- a/platform/linuxbsd/detect.py +++ b/platform/linuxbsd/detect.py @@ -256,10 +256,6 @@ def configure(env: "SConsEnvironment"): if not env["builtin_enet"]: env.ParseConfig("pkg-config libenet --cflags --libs") - if not env["builtin_squish"]: - # libsquish doesn't reliably install its .pc file, so some distros lack it. - env.Append(LIBS=["libsquish"]) - if not env["builtin_zstd"]: env.ParseConfig("pkg-config libzstd --cflags --libs") diff --git a/platform/linuxbsd/wayland/SCsub b/platform/linuxbsd/wayland/SCsub index 89b586845c..1a8e243728 100644 --- a/platform/linuxbsd/wayland/SCsub +++ b/platform/linuxbsd/wayland/SCsub @@ -1,4 +1,5 @@ #!/usr/bin/env python +from misc.utility.scons_hints import * Import("env") diff --git a/platform/linuxbsd/wayland/rendering_context_driver_vulkan_wayland.cpp b/platform/linuxbsd/wayland/rendering_context_driver_vulkan_wayland.cpp index 0417ba95eb..8abcc464ba 100644 --- a/platform/linuxbsd/wayland/rendering_context_driver_vulkan_wayland.cpp +++ b/platform/linuxbsd/wayland/rendering_context_driver_vulkan_wayland.cpp @@ -32,11 +32,7 @@ #include "rendering_context_driver_vulkan_wayland.h" -#ifdef USE_VOLK -#include <volk.h> -#else -#include <vulkan/vulkan.h> -#endif +#include "drivers/vulkan/godot_vulkan.h" const char *RenderingContextDriverVulkanWayland::_get_platform_surface_extension() const { return VK_KHR_WAYLAND_SURFACE_EXTENSION_NAME; diff --git a/platform/linuxbsd/wayland/wayland_thread.h b/platform/linuxbsd/wayland/wayland_thread.h index ad2e843a74..819a1205d6 100644 --- a/platform/linuxbsd/wayland/wayland_thread.h +++ b/platform/linuxbsd/wayland/wayland_thread.h @@ -665,7 +665,7 @@ private: .preferred_buffer_transform = _wl_surface_on_preferred_buffer_transform, }; - static constexpr struct wl_callback_listener frame_wl_callback_listener { + static constexpr struct wl_callback_listener frame_wl_callback_listener = { .done = _frame_wl_callback_on_done, }; @@ -683,7 +683,7 @@ private: .name = _wl_seat_on_name, }; - static constexpr struct wl_callback_listener cursor_frame_callback_listener { + static constexpr struct wl_callback_listener cursor_frame_callback_listener = { .done = _cursor_frame_callback_on_done, }; diff --git a/platform/linuxbsd/x11/SCsub b/platform/linuxbsd/x11/SCsub index 75fe584ad5..b76b98447f 100644 --- a/platform/linuxbsd/x11/SCsub +++ b/platform/linuxbsd/x11/SCsub @@ -1,4 +1,5 @@ #!/usr/bin/env python +from misc.utility.scons_hints import * Import("env") diff --git a/platform/linuxbsd/x11/display_server_x11.cpp b/platform/linuxbsd/x11/display_server_x11.cpp index 7949f80f24..293623e594 100644 --- a/platform/linuxbsd/x11/display_server_x11.cpp +++ b/platform/linuxbsd/x11/display_server_x11.cpp @@ -2055,7 +2055,7 @@ void DisplayServerX11::window_set_current_screen(int p_screen, WindowID p_window return; } - if (window_get_mode(p_window) == WINDOW_MODE_FULLSCREEN) { + if (window_get_mode(p_window) == WINDOW_MODE_FULLSCREEN || window_get_mode(p_window) == WINDOW_MODE_MAXIMIZED) { Point2i position = screen_get_position(p_screen); Size2i size = screen_get_size(p_screen); @@ -2998,11 +2998,7 @@ bool DisplayServerX11::window_is_focused(WindowID p_window) const { const WindowData &wd = windows[p_window]; - Window focused_window; - int focus_ret_state; - XGetInputFocus(x11_display, &focused_window, &focus_ret_state); - - return wd.x11_window == focused_window; + return wd.focused; } bool DisplayServerX11::window_can_draw(WindowID p_window) const { @@ -3050,7 +3046,7 @@ void DisplayServerX11::window_set_ime_active(const bool p_active, WindowID p_win XWindowAttributes xwa; XSync(x11_display, False); XGetWindowAttributes(x11_display, wd.x11_xim_window, &xwa); - if (xwa.map_state == IsViewable) { + if (xwa.map_state == IsViewable && _window_focus_check()) { _set_input_focus(wd.x11_xim_window, RevertToParent); } XSetICFocus(wd.xic); @@ -4319,7 +4315,7 @@ bool DisplayServerX11::_window_focus_check() { bool has_focus = false; for (const KeyValue<int, DisplayServerX11::WindowData> &wid : windows) { - if (wid.value.x11_window == focused_window) { + if (wid.value.x11_window == focused_window || (wid.value.xic && wid.value.ime_active && wid.value.x11_xim_window == focused_window)) { has_focus = true; break; } diff --git a/platform/linuxbsd/x11/rendering_context_driver_vulkan_x11.cpp b/platform/linuxbsd/x11/rendering_context_driver_vulkan_x11.cpp index 3f505d000c..cbcf07852b 100644 --- a/platform/linuxbsd/x11/rendering_context_driver_vulkan_x11.cpp +++ b/platform/linuxbsd/x11/rendering_context_driver_vulkan_x11.cpp @@ -32,11 +32,7 @@ #include "rendering_context_driver_vulkan_x11.h" -#ifdef USE_VOLK -#include <volk.h> -#else -#include <vulkan/vulkan.h> -#endif +#include "drivers/vulkan/godot_vulkan.h" const char *RenderingContextDriverVulkanX11::_get_platform_surface_extension() const { return VK_KHR_XLIB_SURFACE_EXTENSION_NAME; diff --git a/platform/macos/SCsub b/platform/macos/SCsub index a10262c524..3924e79fb6 100644 --- a/platform/macos/SCsub +++ b/platform/macos/SCsub @@ -1,4 +1,5 @@ #!/usr/bin/env python +from misc.utility.scons_hints import * Import("env") diff --git a/platform/macos/detect.py b/platform/macos/detect.py index e35423d41f..a8968b592e 100644 --- a/platform/macos/detect.py +++ b/platform/macos/detect.py @@ -2,12 +2,16 @@ import os import sys from typing import TYPE_CHECKING -from methods import detect_darwin_sdk_path, get_compiler_version, is_vanilla_clang, print_error +from methods import detect_darwin_sdk_path, get_compiler_version, is_vanilla_clang, print_error, print_warning from platform_methods import detect_arch, detect_mvk if TYPE_CHECKING: from SCons.Script.SConscript import SConsEnvironment +# To match other platforms +STACK_SIZE = 8388608 +STACK_SIZE_SANITIZERS = 30 * 1024 * 1024 + def get_name(): return "macOS" @@ -183,6 +187,10 @@ def configure(env: "SConsEnvironment"): env.Append(CCFLAGS=["-fsanitize=thread"]) env.Append(LINKFLAGS=["-fsanitize=thread"]) + env.Append(LINKFLAGS=["-Wl,-stack_size," + hex(STACK_SIZE_SANITIZERS)]) + else: + env.Append(LINKFLAGS=["-Wl,-stack_size," + hex(STACK_SIZE)]) + if env["use_coverage"]: env.Append(CCFLAGS=["-ftest-coverage", "-fprofile-arcs"]) env.Append(LINKFLAGS=["-ftest-coverage", "-fprofile-arcs"]) @@ -241,7 +249,7 @@ def configure(env: "SConsEnvironment"): env.Append(LINKFLAGS=["-rpath", "@executable_path/../Frameworks", "-rpath", "@executable_path"]) if env["metal"] and env["arch"] != "arm64": - # Only supported on arm64, so skip it for x86_64 builds. + print_warning("Target architecture '{}' does not support the Metal rendering driver".format(env["arch"])) env["metal"] = False extra_frameworks = set() diff --git a/platform/macos/display_server_macos.mm b/platform/macos/display_server_macos.mm index f6c1d11028..48cc7bbba3 100644 --- a/platform/macos/display_server_macos.mm +++ b/platform/macos/display_server_macos.mm @@ -1893,6 +1893,12 @@ void DisplayServerMacOS::window_set_current_screen(int p_screen, WindowID p_wind was_fullscreen = true; } + bool was_maximized = false; + if (!was_fullscreen && NSEqualRects([wd.window_object frame], [[wd.window_object screen] visibleFrame])) { + [wd.window_object zoom:nil]; + was_maximized = true; + } + Rect2i srect = screen_get_usable_rect(p_screen); Point2i wpos = window_get_position(p_window) - screen_get_position(window_get_current_screen(p_window)); Size2i wsize = window_get_size(p_window); @@ -1901,6 +1907,10 @@ void DisplayServerMacOS::window_set_current_screen(int p_screen, WindowID p_wind wpos = wpos.clamp(srect.position, srect.position + srect.size - wsize / 3); window_set_position(wpos, p_window); + if (was_maximized) { + [wd.window_object zoom:nil]; + } + if (was_fullscreen) { // Re-enter fullscreen mode. [wd.window_object toggleFullScreen:nil]; diff --git a/platform/macos/godot_menu_delegate.mm b/platform/macos/godot_menu_delegate.mm index 3f7dfac3de..16fdd0d189 100644 --- a/platform/macos/godot_menu_delegate.mm +++ b/platform/macos/godot_menu_delegate.mm @@ -102,7 +102,11 @@ } else { // Otherwise redirect event to the engine. if (DisplayServer::get_singleton()) { - [[[NSApplication sharedApplication] keyWindow] sendEvent:event]; + if ([[NSApplication sharedApplication] keyWindow].sheet) { + [[[[NSApplication sharedApplication] keyWindow] sheetParent] sendEvent:event]; + } else { + [[[NSApplication sharedApplication] keyWindow] sendEvent:event]; + } } } diff --git a/platform/web/SCsub b/platform/web/SCsub index e81f2ec516..b30bf20f26 100644 --- a/platform/web/SCsub +++ b/platform/web/SCsub @@ -1,4 +1,5 @@ #!/usr/bin/env python +from misc.utility.scons_hints import * from methods import print_error diff --git a/platform/web/detect.py b/platform/web/detect.py index bf75c2f9fc..735e2eaf4f 100644 --- a/platform/web/detect.py +++ b/platform/web/detect.py @@ -199,6 +199,11 @@ def configure(env: "SConsEnvironment"): cc_version = get_compiler_version(env) cc_semver = (cc_version["major"], cc_version["minor"], cc_version["patch"]) + # Minimum emscripten requirements. + if cc_semver < (3, 1, 62): + print_error("The minimum emscripten version to build Godot is 3.1.62, detected: %s.%s.%s" % cc_semver) + sys.exit(255) + env.Prepend(CPPPATH=["#platform/web"]) env.Append(CPPDEFINES=["WEB_ENABLED", "UNIX_ENABLED"]) @@ -210,14 +215,12 @@ def configure(env: "SConsEnvironment"): env.Append(LINKFLAGS=["-sOFFSCREEN_FRAMEBUFFER=1"]) # Disables the use of *glGetProcAddress() which is inefficient. # See https://emscripten.org/docs/tools_reference/settings_reference.html#gl-enable-get-proc-address - if cc_semver >= (3, 1, 51): - env.Append(LINKFLAGS=["-sGL_ENABLE_GET_PROC_ADDRESS=0"]) + env.Append(LINKFLAGS=["-sGL_ENABLE_GET_PROC_ADDRESS=0"]) if env["javascript_eval"]: env.Append(CPPDEFINES=["JAVASCRIPT_EVAL_ENABLED"]) - 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"])]) + env.Append(LINKFLAGS=["-s%s=%sKB" % ("STACK_SIZE", env["stack_size"])]) if env["threads"]: # Thread support (via SharedArrayBuffer). @@ -237,30 +240,21 @@ def configure(env: "SConsEnvironment"): env["proxy_to_pthread"] = False if env["lto"] != "none": - # Workaround https://github.com/emscripten-core/emscripten/issues/19781. - if cc_semver >= (3, 1, 42) and cc_semver < (3, 1, 46): - env.Append(LINKFLAGS=["-Wl,-u,scalbnf"]) # Workaround https://github.com/emscripten-core/emscripten/issues/16836. - if cc_semver >= (3, 1, 47): - env.Append(LINKFLAGS=["-Wl,-u,_emscripten_run_callback_on_thread"]) + env.Append(LINKFLAGS=["-Wl,-u,_emscripten_run_callback_on_thread"]) if env["dlink_enabled"]: if env["proxy_to_pthread"]: print_warning("GDExtension support requires proxy_to_pthread=no, disabling proxy to pthread.") env["proxy_to_pthread"] = False - if cc_semver < (3, 1, 14): - print_error("GDExtension support requires emscripten >= 3.1.14, detected: %s.%s.%s" % cc_semver) - sys.exit(255) - env.Append(CCFLAGS=["-sSIDE_MODULE=2"]) env.Append(LINKFLAGS=["-sSIDE_MODULE=2"]) env.Append(CCFLAGS=["-fvisibility=hidden"]) env.Append(LINKFLAGS=["-fvisibility=hidden"]) env.extra_suffix = ".dlink" + env.extra_suffix - # WASM_BIGINT is needed since emscripten ≥ 3.1.41 - needs_wasm_bigint = cc_semver >= (3, 1, 41) + env.Append(LINKFLAGS=["-sWASM_BIGINT"]) # Run the main application in a web worker if env["proxy_to_pthread"]: @@ -269,11 +263,6 @@ def configure(env: "SConsEnvironment"): env.Append(LINKFLAGS=["-sEXPORTED_RUNTIME_METHODS=['_emscripten_proxy_main']"]) # https://github.com/emscripten-core/emscripten/issues/18034#issuecomment-1277561925 env.Append(LINKFLAGS=["-sTEXTDECODER=0"]) - # BigInt support to pass object pointers between contexts - needs_wasm_bigint = True - - if needs_wasm_bigint: - env.Append(LINKFLAGS=["-sWASM_BIGINT"]) # Reduce code size by generating less support code (e.g. skip NodeJS support). env.Append(LINKFLAGS=["-sENVIRONMENT=web,worker"]) diff --git a/platform/web/export/export_plugin.cpp b/platform/web/export/export_plugin.cpp index 5faab74d7b..efe3c95496 100644 --- a/platform/web/export/export_plugin.cpp +++ b/platform/web/export/export_plugin.cpp @@ -214,6 +214,9 @@ Error EditorExportPlatformWeb::_add_manifest_icon(const String &p_path, const St } Error EditorExportPlatformWeb::_build_pwa(const Ref<EditorExportPreset> &p_preset, const String p_path, const Vector<SharedObject> &p_shared_objects) { + List<String> preset_features; + get_preset_features(p_preset, &preset_features); + String proj_name = GLOBAL_GET("application/config/name"); if (proj_name.is_empty()) { proj_name = "Godot Game"; @@ -239,7 +242,10 @@ Error EditorExportPlatformWeb::_build_pwa(const Ref<EditorExportPreset> &p_prese cache_files.push_back(name + ".icon.png"); cache_files.push_back(name + ".apple-touch-icon.png"); } - cache_files.push_back(name + ".worker.js"); + + if (preset_features.find("threads")) { + cache_files.push_back(name + ".worker.js"); + } cache_files.push_back(name + ".audio.worklet.js"); cache_files.push_back(name + ".audio.position.worklet.js"); replaces["___GODOT_CACHE___"] = Variant(cache_files).to_json_string(); diff --git a/platform/web/js/libs/library_godot_audio.js b/platform/web/js/libs/library_godot_audio.js index 40fb0c356c..aaf986b966 100644 --- a/platform/web/js/libs/library_godot_audio.js +++ b/platform/web/js/libs/library_godot_audio.js @@ -868,7 +868,10 @@ class Bus { * @returns {void} */ static move(fromIndex, toIndex) { - const movedBus = GodotAudio.Bus.getBus(fromIndex); + const movedBus = GodotAudio.Bus.getBusOrNull(fromIndex); + if (movedBus == null) { + return; + } const buses = GodotAudio.buses.filter((_, i) => i !== fromIndex); // Inserts at index. buses.splice(toIndex - 1, 0, movedBus); @@ -1424,7 +1427,10 @@ const _GodotAudio = { * @returns {void} */ remove_sample_bus: function (index) { - const bus = GodotAudio.Bus.getBus(index); + const bus = GodotAudio.Bus.getBusOrNull(index); + if (bus == null) { + return; + } bus.clear(); }, @@ -1454,8 +1460,17 @@ const _GodotAudio = { * @returns {void} */ set_sample_bus_send: function (busIndex, sendIndex) { - const bus = GodotAudio.Bus.getBus(busIndex); - bus.setSend(GodotAudio.Bus.getBus(sendIndex)); + const bus = GodotAudio.Bus.getBusOrNull(busIndex); + if (bus == null) { + // Cannot send from an invalid bus. + return; + } + let targetBus = GodotAudio.Bus.getBusOrNull(sendIndex); + if (targetBus == null) { + // Send to master. + targetBus = GodotAudio.Bus.getBus(0); + } + bus.setSend(targetBus); }, /** @@ -1465,7 +1480,10 @@ const _GodotAudio = { * @returns {void} */ set_sample_bus_volume_db: function (busIndex, volumeDb) { - const bus = GodotAudio.Bus.getBus(busIndex); + const bus = GodotAudio.Bus.getBusOrNull(busIndex); + if (bus == null) { + return; + } bus.setVolumeDb(volumeDb); }, @@ -1476,7 +1494,10 @@ const _GodotAudio = { * @returns {void} */ set_sample_bus_solo: function (busIndex, enable) { - const bus = GodotAudio.Bus.getBus(busIndex); + const bus = GodotAudio.Bus.getBusOrNull(busIndex); + if (bus == null) { + return; + } bus.solo(enable); }, @@ -1487,7 +1508,10 @@ const _GodotAudio = { * @returns {void} */ set_sample_bus_mute: function (busIndex, enable) { - const bus = GodotAudio.Bus.getBus(busIndex); + const bus = GodotAudio.Bus.getBusOrNull(busIndex); + if (bus == null) { + return; + } bus.mute(enable); }, }, diff --git a/platform/windows/SCsub b/platform/windows/SCsub index f8ed8b73f5..1d17e7b325 100644 --- a/platform/windows/SCsub +++ b/platform/windows/SCsub @@ -1,4 +1,5 @@ #!/usr/bin/env python +from misc.utility.scons_hints import * Import("env") diff --git a/platform/windows/detect.py b/platform/windows/detect.py index db4c743595..0ee52a09a7 100644 --- a/platform/windows/detect.py +++ b/platform/windows/detect.py @@ -390,8 +390,6 @@ def configure_msvc(env: "SConsEnvironment", vcvars_msvc_config): env.AppendUnique(CPPDEFINES=["R128_STDC_ONLY"]) env.extra_suffix = ".llvm" + env.extra_suffix - env["MAXLINELENGTH"] = 8192 # Windows Vista and beyond, so always applicable. - if env["silence_msvc"] and not env.GetOption("clean"): from tempfile import mkstemp @@ -620,18 +618,16 @@ def configure_msvc(env: "SConsEnvironment", vcvars_msvc_config): print("ThinLTO is only compatible with LLVM, use `use_llvm=yes` or `lto=full`.") sys.exit(255) - env.Append(CCFLAGS=["-flto=thin"]) - env.Append(LINKFLAGS=["-flto=thin"]) + env.AppendUnique(CCFLAGS=["-flto=thin"]) elif env["use_llvm"]: - env.Append(CCFLAGS=["-flto"]) - env.Append(LINKFLAGS=["-flto"]) + env.AppendUnique(CCFLAGS=["-flto"]) else: env.AppendUnique(CCFLAGS=["/GL"]) - env.AppendUnique(ARFLAGS=["/LTCG"]) - if env["progress"]: - env.AppendUnique(LINKFLAGS=["/LTCG:STATUS"]) - else: - env.AppendUnique(LINKFLAGS=["/LTCG"]) + if env["progress"]: + env.AppendUnique(LINKFLAGS=["/LTCG:STATUS"]) + else: + env.AppendUnique(LINKFLAGS=["/LTCG"]) + env.AppendUnique(ARFLAGS=["/LTCG"]) if vcvars_msvc_config: env.Prepend(CPPPATH=[p for p in str(os.getenv("INCLUDE")).split(";")]) @@ -666,7 +662,7 @@ def get_ar_version(env): print_warning("Couldn't check version of `ar`.") return ret - match = re.search(r"GNU ar \(GNU Binutils\) (\d+)\.(\d+)(?:\.(\d+))?", output) + match = re.search(r"GNU ar(?: \(GNU Binutils\)| version) (\d+)\.(\d+)(?:\.(\d+))?", output) if match: ret["major"] = int(match[1]) ret["minor"] = int(match[2]) @@ -792,8 +788,9 @@ def configure_mingw(env: "SConsEnvironment"): env["CXX"] = mingw_bin_prefix + "g++" if try_cmd("as --version", env["mingw_prefix"], env["arch"]): env["AS"] = mingw_bin_prefix + "as" - if try_cmd("gcc-ar --version", env["mingw_prefix"], env["arch"]): - env["AR"] = mingw_bin_prefix + "gcc-ar" + ar = "ar" if os.name == "nt" else "gcc-ar" + if try_cmd(f"{ar} --version", env["mingw_prefix"], env["arch"]): + env["AR"] = mingw_bin_prefix + ar if try_cmd("gcc-ranlib --version", env["mingw_prefix"], env["arch"]): env["RANLIB"] = mingw_bin_prefix + "gcc-ranlib" diff --git a/platform/windows/display_server_windows.cpp b/platform/windows/display_server_windows.cpp index ed9d5244a3..ffa3840181 100644 --- a/platform/windows/display_server_windows.cpp +++ b/platform/windows/display_server_windows.cpp @@ -1520,6 +1520,7 @@ DisplayServer::WindowID DisplayServerWindows::create_sub_window(WindowMode p_mod rendering_device->screen_create(window_id); } #endif + wd.initialized = true; return window_id; } @@ -1813,6 +1814,13 @@ void DisplayServerWindows::window_set_current_screen(int p_screen, WindowID p_wi Size2 size = screen_get_size(p_screen); MoveWindow(wd.hWnd, pos.x, pos.y, size.width, size.height, TRUE); + } else if (wd.maximized) { + Point2 pos = screen_get_position(p_screen) + _get_screens_origin(); + Size2 size = screen_get_size(p_screen); + + ShowWindow(wd.hWnd, SW_RESTORE); + MoveWindow(wd.hWnd, pos.x, pos.y, size.width, size.height, TRUE); + ShowWindow(wd.hWnd, SW_MAXIMIZE); } else { Rect2i srect = screen_get_usable_rect(p_screen); Point2i wpos = window_get_position(p_window) - screen_get_position(window_get_current_screen(p_window)); @@ -2058,7 +2066,7 @@ Size2i DisplayServerWindows::window_get_size_with_decorations(WindowID p_window) return Size2(); } -void DisplayServerWindows::_get_window_style(bool p_main_window, bool p_fullscreen, bool p_multiwindow_fs, bool p_borderless, bool p_resizable, bool p_maximized, bool p_maximized_fs, bool p_no_activate_focus, DWORD &r_style, DWORD &r_style_ex) { +void DisplayServerWindows::_get_window_style(bool p_main_window, bool p_initialized, bool p_fullscreen, bool p_multiwindow_fs, bool p_borderless, bool p_resizable, bool p_minimized, bool p_maximized, bool p_maximized_fs, bool p_no_activate_focus, DWORD &r_style, DWORD &r_style_ex) { // Windows docs for window styles: // https://docs.microsoft.com/en-us/windows/win32/winmsg/window-styles // https://docs.microsoft.com/en-us/windows/win32/winmsg/extended-window-styles @@ -2067,12 +2075,16 @@ void DisplayServerWindows::_get_window_style(bool p_main_window, bool p_fullscre r_style_ex = WS_EX_WINDOWEDGE; if (p_main_window) { r_style_ex |= WS_EX_APPWINDOW; - r_style |= WS_VISIBLE; + if (p_initialized) { + r_style |= WS_VISIBLE; + } } if (p_fullscreen || p_borderless) { r_style |= WS_POPUP; // p_borderless was WS_EX_TOOLWINDOW in the past. - if (p_maximized) { + if (p_minimized) { + r_style |= WS_MINIMIZE; + } else if (p_maximized) { r_style |= WS_MAXIMIZE; } if (!p_fullscreen) { @@ -2087,13 +2099,19 @@ void DisplayServerWindows::_get_window_style(bool p_main_window, bool p_fullscre } } else { if (p_resizable) { - if (p_maximized) { + if (p_minimized) { + r_style = WS_OVERLAPPEDWINDOW | WS_MINIMIZE; + } else if (p_maximized) { r_style = WS_OVERLAPPEDWINDOW | WS_MAXIMIZE; } else { r_style = WS_OVERLAPPEDWINDOW; } } else { - r_style = WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX; + if (p_minimized) { + r_style = WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX | WS_MINIMIZE; + } else { + r_style = WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX; + } } } @@ -2101,7 +2119,7 @@ void DisplayServerWindows::_get_window_style(bool p_main_window, bool p_fullscre r_style_ex |= WS_EX_TOPMOST | WS_EX_NOACTIVATE; } - if (!p_borderless && !p_no_activate_focus) { + if (!p_borderless && !p_no_activate_focus && p_initialized) { r_style |= WS_VISIBLE; } @@ -2118,7 +2136,7 @@ void DisplayServerWindows::_update_window_style(WindowID p_window, bool p_repain DWORD style = 0; DWORD style_ex = 0; - _get_window_style(p_window == MAIN_WINDOW_ID, wd.fullscreen, wd.multiwindow_fs, wd.borderless, wd.resizable, wd.maximized, wd.maximized_fs, wd.no_focus || wd.is_popup, style, style_ex); + _get_window_style(p_window == MAIN_WINDOW_ID, wd.initialized, wd.fullscreen, wd.multiwindow_fs, wd.borderless, wd.resizable, wd.minimized, wd.maximized, wd.maximized_fs, wd.no_focus || wd.is_popup, style, style_ex); SetWindowLongPtr(wd.hWnd, GWL_STYLE, style); SetWindowLongPtr(wd.hWnd, GWL_EXSTYLE, style_ex); @@ -3962,9 +3980,9 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA } } - // WARNING: we get called with events before the window is registered in our collection + // WARNING: We get called with events before the window is registered in our collection // specifically, even the call to CreateWindowEx already calls here while still on the stack, - // so there is no way to store the window handle in our collection before we get here + // so there is no way to store the window handle in our collection before we get here. if (!window_created) { // don't let code below operate on incompletely initialized window objects or missing window_id return _handle_early_window_message(hWnd, uMsg, wParam, lParam); @@ -5529,7 +5547,7 @@ DisplayServer::WindowID DisplayServerWindows::_create_window(WindowMode p_mode, DWORD dwExStyle; DWORD dwStyle; - _get_window_style(window_id_counter == MAIN_WINDOW_ID, (p_mode == WINDOW_MODE_FULLSCREEN || p_mode == WINDOW_MODE_EXCLUSIVE_FULLSCREEN), p_mode != WINDOW_MODE_EXCLUSIVE_FULLSCREEN, p_flags & WINDOW_FLAG_BORDERLESS_BIT, !(p_flags & WINDOW_FLAG_RESIZE_DISABLED_BIT), p_mode == WINDOW_MODE_MAXIMIZED, false, (p_flags & WINDOW_FLAG_NO_FOCUS_BIT) | (p_flags & WINDOW_FLAG_POPUP), dwStyle, dwExStyle); + _get_window_style(window_id_counter == MAIN_WINDOW_ID, false, (p_mode == WINDOW_MODE_FULLSCREEN || p_mode == WINDOW_MODE_EXCLUSIVE_FULLSCREEN), p_mode != WINDOW_MODE_EXCLUSIVE_FULLSCREEN, p_flags & WINDOW_FLAG_BORDERLESS_BIT, !(p_flags & WINDOW_FLAG_RESIZE_DISABLED_BIT), p_mode == WINDOW_MODE_MINIMIZED, p_mode == WINDOW_MODE_MAXIMIZED, false, (p_flags & WINDOW_FLAG_NO_FOCUS_BIT) | (p_flags & WINDOW_FLAG_POPUP), dwStyle, dwExStyle); RECT WindowRect; @@ -6361,6 +6379,7 @@ DisplayServerWindows::DisplayServerWindows(const String &p_rendering_driver, Win } } + windows[MAIN_WINDOW_ID].initialized = true; show_window(MAIN_WINDOW_ID); #if defined(RD_ENABLED) diff --git a/platform/windows/display_server_windows.h b/platform/windows/display_server_windows.h index 54e1c9681d..7d6a3e96a6 100644 --- a/platform/windows/display_server_windows.h +++ b/platform/windows/display_server_windows.h @@ -524,6 +524,8 @@ class DisplayServerWindows : public DisplayServer { bool is_popup = false; Rect2i parent_safe_rect; + + bool initialized = false; }; JoypadWindows *joypad = nullptr; @@ -591,7 +593,7 @@ class DisplayServerWindows : public DisplayServer { HashMap<int64_t, Vector2> pointer_last_pos; void _send_window_event(const WindowData &wd, WindowEvent p_event); - void _get_window_style(bool p_main_window, bool p_fullscreen, bool p_multiwindow_fs, bool p_borderless, bool p_resizable, bool p_maximized, bool p_maximized_fs, bool p_no_activate_focus, DWORD &r_style, DWORD &r_style_ex); + void _get_window_style(bool p_main_window, bool p_initialized, bool p_fullscreen, bool p_multiwindow_fs, bool p_borderless, bool p_resizable, bool p_minimized, bool p_maximized, bool p_maximized_fs, bool p_no_activate_focus, DWORD &r_style, DWORD &r_style_ex); MouseMode mouse_mode; int restore_mouse_trails = 0; diff --git a/platform/windows/gl_manager_windows_native.cpp b/platform/windows/gl_manager_windows_native.cpp index 8590c46d12..af703f4dfa 100644 --- a/platform/windows/gl_manager_windows_native.cpp +++ b/platform/windows/gl_manager_windows_native.cpp @@ -452,8 +452,8 @@ Error GLManagerNative_Windows::window_create(DisplayServer::WindowID p_window_id return FAILED; } - // WARNING: p_window_id is an eternally growing integer since popup windows keep coming and going - // and each of them has a higher id than the previous, so it must be used in a map not a vector + // WARNING: `p_window_id` is an eternally growing integer since popup windows keep coming and going + // and each of them has a higher id than the previous, so it must be used in a map not a vector. _windows[p_window_id] = win; // make current diff --git a/platform/windows/rendering_context_driver_vulkan_windows.cpp b/platform/windows/rendering_context_driver_vulkan_windows.cpp index 445388af89..8ca677fe64 100644 --- a/platform/windows/rendering_context_driver_vulkan_windows.cpp +++ b/platform/windows/rendering_context_driver_vulkan_windows.cpp @@ -34,11 +34,7 @@ #include "rendering_context_driver_vulkan_windows.h" -#ifdef USE_VOLK -#include <volk.h> -#else -#include <vulkan/vulkan.h> -#endif +#include "drivers/vulkan/godot_vulkan.h" const char *RenderingContextDriverVulkanWindows::_get_platform_surface_extension() const { return VK_KHR_WIN32_SURFACE_EXTENSION_NAME; |