diff options
Diffstat (limited to 'platform')
30 files changed, 489 insertions, 183 deletions
diff --git a/platform/android/detect.py b/platform/android/detect.py index 0a10754e24..937bdbaa07 100644 --- a/platform/android/detect.py +++ b/platform/android/detect.py @@ -5,6 +5,7 @@ import sys from typing import TYPE_CHECKING from methods import print_error, print_warning +from platform_methods import validate_arch if TYPE_CHECKING: from SCons.Script.SConscript import SConsEnvironment @@ -98,12 +99,7 @@ def install_ndk_if_needed(env: "SConsEnvironment"): def configure(env: "SConsEnvironment"): # Validate arch. supported_arches = ["x86_32", "x86_64", "arm32", "arm64"] - if env["arch"] not in supported_arches: - print_error( - 'Unsupported CPU architecture "%s" for Android. Supported architectures are: %s.' - % (env["arch"], ", ".join(supported_arches)) - ) - sys.exit(255) + validate_arch(env["arch"], get_name(), supported_arches) if get_min_sdk_version(env["ndk_platform"]) < get_min_target_api(): print_warning( @@ -171,9 +167,7 @@ def configure(env: "SConsEnvironment"): env["AS"] = compiler_path + "/clang" env.Append( - CCFLAGS=( - "-fpic -ffunction-sections -funwind-tables -fstack-protector-strong -fvisibility=hidden -fno-strict-aliasing".split() - ) + CCFLAGS=("-fpic -ffunction-sections -funwind-tables -fstack-protector-strong -fvisibility=hidden".split()) ) if get_min_sdk_version(env["ndk_platform"]) >= 24: diff --git a/platform/android/display_server_android.cpp b/platform/android/display_server_android.cpp index 5922c70584..e3ee1dd631 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 2fe5539e56..8c8bca2b7c 100644 --- a/platform/android/doc_classes/EditorExportPlatformAndroid.xml +++ b/platform/android/doc_classes/EditorExportPlatformAndroid.xml @@ -577,7 +577,7 @@ Allows an application to write to the user dictionary. </member> <member name="screen/immersive_mode" type="bool" setter="" getter=""> - If [code]true[/code], hides navigation and status bar. + If [code]true[/code], hides navigation and status bar. See [method DisplayServer.window_set_mode] to toggle it at runtime. </member> <member name="screen/support_large" type="bool" setter="" getter=""> Indicates whether the application supports larger screen form-factors. diff --git a/platform/android/export/export_plugin.h b/platform/android/export/export_plugin.h index 7e1d626486..15e80f824d 100644 --- a/platform/android/export/export_plugin.h +++ b/platform/android/export/export_plugin.h @@ -35,6 +35,7 @@ #include "godot_plugin_config.h" #endif // DISABLE_DEPRECATED +#include "core/io/image.h" #include "core/io/zip_io.h" #include "core/os/os.h" #include "editor/export/editor_export_platform.h" 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/utils/PermissionsUtil.java b/platform/android/java/lib/src/org/godotengine/godot/utils/PermissionsUtil.java index 4e8e82a70a..c44a6dd472 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/utils/PermissionsUtil.java +++ b/platform/android/java/lib/src/org/godotengine/godot/utils/PermissionsUtil.java @@ -108,7 +108,7 @@ public final class PermissionsUtil { } else { PermissionInfo permissionInfo = getPermissionInfo(activity, permission); int protectionLevel = Build.VERSION.SDK_INT >= Build.VERSION_CODES.P ? permissionInfo.getProtection() : permissionInfo.protectionLevel; - if (protectionLevel == PermissionInfo.PROTECTION_DANGEROUS && ContextCompat.checkSelfPermission(activity, permission) != PackageManager.PERMISSION_GRANTED) { + if ((protectionLevel & PermissionInfo.PROTECTION_DANGEROUS) == PermissionInfo.PROTECTION_DANGEROUS && ContextCompat.checkSelfPermission(activity, permission) != PackageManager.PERMISSION_GRANTED) { Log.d(TAG, "Requesting permission " + permission); requestedPermissions.add(permission); } @@ -174,7 +174,7 @@ public final class PermissionsUtil { try { PermissionInfo permissionInfo = getPermissionInfo(activity, permissionName); int protectionLevel = Build.VERSION.SDK_INT >= Build.VERSION_CODES.P ? permissionInfo.getProtection() : permissionInfo.protectionLevel; - if (protectionLevel == PermissionInfo.PROTECTION_DANGEROUS && ContextCompat.checkSelfPermission(activity, permissionName) != PackageManager.PERMISSION_GRANTED) { + if ((protectionLevel & PermissionInfo.PROTECTION_DANGEROUS) == PermissionInfo.PROTECTION_DANGEROUS && ContextCompat.checkSelfPermission(activity, permissionName) != PackageManager.PERMISSION_GRANTED) { activity.requestPermissions(new String[] { permissionName }, REQUEST_SINGLE_PERMISSION_REQ_CODE); return false; } @@ -259,7 +259,7 @@ public final class PermissionsUtil { } else { PermissionInfo permissionInfo = getPermissionInfo(context, manifestPermission); int protectionLevel = Build.VERSION.SDK_INT >= Build.VERSION_CODES.P ? permissionInfo.getProtection() : permissionInfo.protectionLevel; - if (protectionLevel == PermissionInfo.PROTECTION_DANGEROUS && ContextCompat.checkSelfPermission(context, manifestPermission) == PackageManager.PERMISSION_GRANTED) { + if ((protectionLevel & PermissionInfo.PROTECTION_DANGEROUS) == PermissionInfo.PROTECTION_DANGEROUS && ContextCompat.checkSelfPermission(context, manifestPermission) == PackageManager.PERMISSION_GRANTED) { grantedPermissions.add(manifestPermission); } } 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/ios/detect.py b/platform/ios/detect.py index 989a7f21f3..0f7f938852 100644 --- a/platform/ios/detect.py +++ b/platform/ios/detect.py @@ -2,7 +2,8 @@ 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 +from platform_methods import validate_arch if TYPE_CHECKING: from SCons.Script.SConscript import SConsEnvironment @@ -60,12 +61,7 @@ def get_flags(): def configure(env: "SConsEnvironment"): # Validate arch. supported_arches = ["x86_64", "arm64"] - if env["arch"] not in supported_arches: - print_error( - 'Unsupported CPU architecture "%s" for iOS. Supported architectures are: %s.' - % (env["arch"], ", ".join(supported_arches)) - ) - sys.exit(255) + validate_arch(env["arch"], get_name(), supported_arches) ## LTO @@ -134,7 +130,7 @@ def configure(env: "SConsEnvironment"): elif env["arch"] == "arm64": env.Append( CCFLAGS=( - "-fobjc-arc -arch arm64 -fmessage-length=0 -fno-strict-aliasing" + "-fobjc-arc -arch arm64 -fmessage-length=0" " -fdiagnostics-print-source-range-info -fdiagnostics-show-category=id -fdiagnostics-parseable-fixits" " -fpascal-strings -fblocks -fvisibility=hidden -MMD -MT dependencies" " -isysroot $IOS_SDK_PATH".split() @@ -156,7 +152,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 0631b50f0a..7f199db997 100644 --- a/platform/ios/display_server_ios.h +++ b/platform/ios/display_server_ios.h @@ -224,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/linuxbsd/detect.py b/platform/linuxbsd/detect.py index a67434527c..2fd573da75 100644 --- a/platform/linuxbsd/detect.py +++ b/platform/linuxbsd/detect.py @@ -4,7 +4,7 @@ import sys from typing import TYPE_CHECKING from methods import get_compiler_version, print_error, print_warning, using_gcc -from platform_methods import detect_arch +from platform_methods import detect_arch, validate_arch if TYPE_CHECKING: from SCons.Script.SConscript import SConsEnvironment @@ -74,12 +74,7 @@ def get_flags(): def configure(env: "SConsEnvironment"): # Validate arch. supported_arches = ["x86_32", "x86_64", "arm32", "arm64", "rv64", "ppc32", "ppc64"] - if env["arch"] not in supported_arches: - print_error( - 'Unsupported CPU architecture "%s" for Linux / *BSD. Supported architectures are: %s.' - % (env["arch"], ", ".join(supported_arches)) - ) - sys.exit(255) + validate_arch(env["arch"], get_name(), supported_arches) ## Build type diff --git a/platform/linuxbsd/wayland/display_server_wayland.cpp b/platform/linuxbsd/wayland/display_server_wayland.cpp index 7a5ebb951e..0a87c4a517 100644 --- a/platform/linuxbsd/wayland/display_server_wayland.cpp +++ b/platform/linuxbsd/wayland/display_server_wayland.cpp @@ -1491,12 +1491,12 @@ DisplayServerWayland::DisplayServerWayland(const String &p_rendering_driver, Win driver_found = true; } } +#endif // GLES3_ENABLED if (!driver_found) { r_error = ERR_UNAVAILABLE; ERR_FAIL_MSG("Video driver not found."); } -#endif // GLES3_ENABLED cursor_set_shape(CURSOR_BUSY); diff --git a/platform/linuxbsd/x11/display_server_x11.cpp b/platform/linuxbsd/x11/display_server_x11.cpp index 622a48e48a..f1be5d83dc 100644 --- a/platform/linuxbsd/x11/display_server_x11.cpp +++ b/platform/linuxbsd/x11/display_server_x11.cpp @@ -2067,7 +2067,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); diff --git a/platform/macos/detect.py b/platform/macos/detect.py index e35423d41f..cab91fd33c 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 platform_methods import detect_arch, detect_mvk +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, validate_arch 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" @@ -64,12 +68,7 @@ def get_flags(): def configure(env: "SConsEnvironment"): # Validate arch. supported_arches = ["x86_64", "arm64"] - if env["arch"] not in supported_arches: - print_error( - 'Unsupported CPU architecture "%s" for macOS. Supported architectures are: %s.' - % (env["arch"], ", ".join(supported_arches)) - ) - sys.exit(255) + validate_arch(env["arch"], get_name(), supported_arches) ## Build type @@ -183,6 +182,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 +244,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 4e59085798..43469d981b 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/doc_classes/EditorExportPlatformMacOS.xml b/platform/macos/doc_classes/EditorExportPlatformMacOS.xml index c261c252ba..dcaba9bbd2 100644 --- a/platform/macos/doc_classes/EditorExportPlatformMacOS.xml +++ b/platform/macos/doc_classes/EditorExportPlatformMacOS.xml @@ -75,6 +75,13 @@ <member name="codesign/custom_options" type="PackedStringArray" setter="" getter=""> Array of the additional command line arguments passed to the code signing tool. </member> + <member name="codesign/entitlements/additional" type="String" setter="" getter=""> + Additional data added to the root [code]<dict>[/code] section of the [url=https://developer.apple.com/documentation/bundleresources/entitlements].entitlements[/url] file. The value should be an XML section with pairs of key-value elements, e.g.: + [codeblock lang=text] + <key>key_name</key> + <string>value</string> + [/codeblock] + </member> <member name="codesign/entitlements/address_book" type="bool" setter="" getter=""> Enable to allow access to contacts in the user's address book, if it's enabled you should also provide usage message in the [member privacy/address_book_usage_description] option. See [url=https://developer.apple.com/documentation/bundleresources/entitlements/com_apple_security_personal-information_addressbook]com.apple.security.personal-information.addressbook[/url]. </member> diff --git a/platform/macos/export/export_plugin.cpp b/platform/macos/export/export_plugin.cpp index c99e9cdd0c..7887e5d0ab 100644 --- a/platform/macos/export/export_plugin.cpp +++ b/platform/macos/export/export_plugin.cpp @@ -327,7 +327,7 @@ bool EditorExportPlatformMacOS::get_export_option_visibility(const EditorExportP } bool advanced_options_enabled = p_preset->are_advanced_options_enabled(); - if (p_option.begins_with("privacy")) { + if (p_option.begins_with("privacy") || p_option == "codesign/entitlements/additional") { return advanced_options_enabled; } } @@ -501,6 +501,7 @@ void EditorExportPlatformMacOS::get_export_options(List<ExportOption> *r_options r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "codesign/entitlements/app_sandbox/files_movies", PROPERTY_HINT_ENUM, "No,Read-only,Read-write"), 0)); r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "codesign/entitlements/app_sandbox/files_user_selected", PROPERTY_HINT_ENUM, "No,Read-only,Read-write"), 0)); r_options->push_back(ExportOption(PropertyInfo(Variant::ARRAY, "codesign/entitlements/app_sandbox/helper_executables", PROPERTY_HINT_ARRAY_TYPE, itos(Variant::STRING) + "/" + itos(PROPERTY_HINT_GLOBAL_FILE) + ":"), Array())); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "codesign/entitlements/additional", PROPERTY_HINT_MULTILINE_TEXT), "")); r_options->push_back(ExportOption(PropertyInfo(Variant::PACKED_STRING_ARRAY, "codesign/custom_options"), PackedStringArray())); #ifdef MACOS_ENABLED @@ -2126,6 +2127,11 @@ Error EditorExportPlatformMacOS::export_project(const Ref<EditorExportPreset> &p } } + const String &additional_entitlements = p_preset->get("codesign/entitlements/additional"); + if (!additional_entitlements.is_empty()) { + ent_f->store_line(additional_entitlements); + } + ent_f->store_line("</dict>"); ent_f->store_line("</plist>"); } else { @@ -2288,6 +2294,14 @@ Error EditorExportPlatformMacOS::export_project(const Ref<EditorExportPreset> &p } } + if (FileAccess::exists(ent_path)) { + print_verbose("entitlements:\n" + FileAccess::get_file_as_string(ent_path)); + } + + if (FileAccess::exists(hlp_ent_path)) { + print_verbose("helper entitlements:\n" + FileAccess::get_file_as_string(hlp_ent_path)); + } + // Clean up temporary entitlements files. if (FileAccess::exists(hlp_ent_path)) { DirAccess::remove_file_or_error(hlp_ent_path); diff --git a/platform/macos/export/export_plugin.h b/platform/macos/export/export_plugin.h index d88d347359..4ded2f3301 100644 --- a/platform/macos/export/export_plugin.h +++ b/platform/macos/export/export_plugin.h @@ -34,6 +34,7 @@ #include "core/config/project_settings.h" #include "core/io/dir_access.h" #include "core/io/file_access.h" +#include "core/io/image.h" #include "core/io/marshalls.h" #include "core/io/resource_saver.h" #include "core/os/os.h" 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/detect.py b/platform/web/detect.py index 735e2eaf4f..26bbbccffa 100644 --- a/platform/web/detect.py +++ b/platform/web/detect.py @@ -14,6 +14,7 @@ from emscripten_helpers import ( from SCons.Util import WhereIs from methods import get_compiler_version, print_error, print_warning +from platform_methods import validate_arch if TYPE_CHECKING: from SCons.Script.SConscript import SConsEnvironment @@ -86,12 +87,7 @@ def get_flags(): def configure(env: "SConsEnvironment"): # Validate arch. supported_arches = ["wasm32"] - if env["arch"] not in supported_arches: - print_error( - 'Unsupported CPU architecture "%s" for Web. Supported architectures are: %s.' - % (env["arch"], ", ".join(supported_arches)) - ) - sys.exit(255) + validate_arch(env["arch"], get_name(), supported_arches) try: env["initial_memory"] = int(env["initial_memory"]) diff --git a/platform/web/js/libs/library_godot_fetch.js b/platform/web/js/libs/library_godot_fetch.js index 00616bc1a5..eeb3978256 100644 --- a/platform/web/js/libs/library_godot_fetch.js +++ b/platform/web/js/libs/library_godot_fetch.js @@ -59,7 +59,12 @@ const GodotFetch = { }); obj.status = response.status; obj.response = response; - obj.reader = response.body.getReader(); + // `body` can be null per spec (for example, in cases where the request method is HEAD). + // As of the time of writing, Chromium (127.0.6533.72) does not follow the spec but Firefox (131.0.3) does. + // See godotengine/godot#76825 for more information. + // See Chromium revert (of the change to follow the spec): + // https://chromium.googlesource.com/chromium/src/+/135354b7bdb554cd03c913af7c90aceead03c4d4 + obj.reader = response.body?.getReader(); obj.chunked = chunked; }, @@ -121,6 +126,10 @@ const GodotFetch = { } obj.reading = true; obj.reader.read().then(GodotFetch.onread.bind(null, id)).catch(GodotFetch.onerror.bind(null, id)); + } else if (obj.reader == null && obj.response.body == null) { + // Emulate a stream closure to maintain the request lifecycle. + obj.reading = true; + GodotFetch.onread(id, { value: undefined, done: true }); } }, }, @@ -159,7 +168,10 @@ const GodotFetch = { if (!obj.response) { return 0; } - if (obj.reader) { + // If the reader is nullish, but there is no body, and the request is not marked as done, + // the same status should be returned as though the request is currently being read + // so that the proper lifecycle closure can be handled in `read()`. + if (obj.reader || (obj.response.body == null && !obj.done)) { return 1; } if (obj.done) { diff --git a/platform/windows/detect.py b/platform/windows/detect.py index 4043f3a8c2..ddcd29adc9 100644 --- a/platform/windows/detect.py +++ b/platform/windows/detect.py @@ -6,7 +6,7 @@ from typing import TYPE_CHECKING import methods from methods import print_error, print_warning -from platform_methods import detect_arch +from platform_methods import detect_arch, validate_arch if TYPE_CHECKING: from SCons.Script.SConscript import SConsEnvironment @@ -483,9 +483,7 @@ def configure_msvc(env: "SConsEnvironment", vcvars_msvc_config): else: print_warning("Missing environment variable: WindowsSdkDir") - if int(env["target_win_version"], 16) < 0x0601: - print_error("`target_win_version` should be 0x0601 or higher (Windows 7).") - sys.exit(255) + validate_win_version(env) env.AppendUnique( CPPDEFINES=[ @@ -549,15 +547,7 @@ def configure_msvc(env: "SConsEnvironment", vcvars_msvc_config): LIBS += ["vulkan"] if env["d3d12"]: - # Check whether we have d3d12 dependencies installed. - if not os.path.exists(env["mesa_libs"]): - print_error( - "The Direct3D 12 rendering driver requires dependencies to be installed.\n" - "You can install them by running `python misc\\scripts\\install_d3d12_sdk_windows.py`.\n" - "See the documentation for more information:\n\t" - "https://docs.godotengine.org/en/latest/contributing/development/compiling/compiling_for_windows.html" - ) - sys.exit(255) + check_d3d12_installed(env) env.AppendUnique(CPPDEFINES=["D3D12_ENABLED", "RD_ENABLED"]) LIBS += ["dxgi", "dxguid"] @@ -662,7 +652,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]) @@ -788,8 +778,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" @@ -819,9 +810,7 @@ def configure_mingw(env: "SConsEnvironment"): ## Compile flags - if int(env["target_win_version"], 16) < 0x0601: - print_error("`target_win_version` should be 0x0601 or higher (Windows 7).") - sys.exit(255) + validate_win_version(env) if not env["use_llvm"]: env.Append(CCFLAGS=["-mwindows"]) @@ -899,15 +888,7 @@ def configure_mingw(env: "SConsEnvironment"): env.Append(LIBS=["vulkan"]) if env["d3d12"]: - # Check whether we have d3d12 dependencies installed. - if not os.path.exists(env["mesa_libs"]): - print_error( - "The Direct3D 12 rendering driver requires dependencies to be installed.\n" - "You can install them by running `python misc\\scripts\\install_d3d12_sdk_windows.py`.\n" - "See the documentation for more information:\n\t" - "https://docs.godotengine.org/en/latest/contributing/development/compiling/compiling_for_windows.html" - ) - sys.exit(255) + check_d3d12_installed(env) env.AppendUnique(CPPDEFINES=["D3D12_ENABLED", "RD_ENABLED"]) env.Append(LIBS=["dxgi", "dxguid"]) @@ -950,12 +931,7 @@ def configure_mingw(env: "SConsEnvironment"): def configure(env: "SConsEnvironment"): # Validate arch. supported_arches = ["x86_32", "x86_64", "arm32", "arm64"] - if env["arch"] not in supported_arches: - print_error( - 'Unsupported CPU architecture "%s" for Windows. Supported architectures are: %s.' - % (env["arch"], ", ".join(supported_arches)) - ) - sys.exit(255) + validate_arch(env["arch"], get_name(), supported_arches) # At this point the env has been set up with basic tools/compilers. env.Prepend(CPPPATH=["#platform/windows"]) @@ -983,3 +959,20 @@ def configure(env: "SConsEnvironment"): else: # MinGW configure_mingw(env) + + +def check_d3d12_installed(env): + if not os.path.exists(env["mesa_libs"]): + print_error( + "The Direct3D 12 rendering driver requires dependencies to be installed.\n" + "You can install them by running `python misc\\scripts\\install_d3d12_sdk_windows.py`.\n" + "See the documentation for more information:\n\t" + "https://docs.godotengine.org/en/latest/contributing/development/compiling/compiling_for_windows.html" + ) + sys.exit(255) + + +def validate_win_version(env): + if int(env["target_win_version"], 16) < 0x0601: + print_error("`target_win_version` should be 0x0601 or higher (Windows 7).") + sys.exit(255) diff --git a/platform/windows/display_server_windows.cpp b/platform/windows/display_server_windows.cpp index 3dbf81430b..fc49b63dde 100644 --- a/platform/windows/display_server_windows.cpp +++ b/platform/windows/display_server_windows.cpp @@ -67,6 +67,18 @@ #define DWMWA_USE_IMMERSIVE_DARK_MODE_BEFORE_20H1 19 #endif +#ifndef DWMWA_WINDOW_CORNER_PREFERENCE +#define DWMWA_WINDOW_CORNER_PREFERENCE 33 +#endif + +#ifndef DWMWCP_DEFAULT +#define DWMWCP_DEFAULT 0 +#endif + +#ifndef DWMWCP_DONOTROUND +#define DWMWCP_DONOTROUND 1 +#endif + #define WM_INDICATOR_CALLBACK_MESSAGE (WM_USER + 1) #if defined(__GNUC__) @@ -1483,6 +1495,9 @@ DisplayServer::WindowID DisplayServerWindows::create_sub_window(WindowMode p_mod if (p_flags & WINDOW_FLAG_ALWAYS_ON_TOP_BIT && p_mode != WINDOW_MODE_FULLSCREEN && p_mode != WINDOW_MODE_EXCLUSIVE_FULLSCREEN) { wd.always_on_top = true; } + if (p_flags & WINDOW_FLAG_SHARP_CORNERS_BIT) { + wd.sharp_corners = true; + } if (p_flags & WINDOW_FLAG_NO_FOCUS_BIT) { wd.no_focus = true; } @@ -1520,6 +1535,7 @@ DisplayServer::WindowID DisplayServerWindows::create_sub_window(WindowMode p_mod rendering_device->screen_create(window_id); } #endif + wd.initialized = true; return window_id; } @@ -1825,6 +1841,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)); @@ -2070,7 +2093,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 @@ -2079,12 +2102,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) { @@ -2099,13 +2126,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; + } } } @@ -2113,7 +2146,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; } @@ -2130,7 +2163,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); @@ -2291,6 +2324,12 @@ void DisplayServerWindows::window_set_flag(WindowFlags p_flag, bool p_enabled, W wd.always_on_top = p_enabled; _update_window_style(p_window); } break; + case WINDOW_FLAG_SHARP_CORNERS: { + wd.sharp_corners = p_enabled; + DWORD value = wd.sharp_corners ? DWMWCP_DONOTROUND : DWMWCP_DEFAULT; + ::DwmSetWindowAttribute(wd.hWnd, DWMWA_WINDOW_CORNER_PREFERENCE, &value, sizeof(value)); + _update_window_style(p_window); + } break; case WINDOW_FLAG_TRANSPARENT: { if (p_enabled) { // Enable per-pixel alpha. @@ -3988,6 +4027,10 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA native_menu->_menu_activate(HMENU(lParam), (int)wParam); } break; case WM_CREATE: { + { + DWORD value = windows[window_id].sharp_corners ? DWMWCP_DONOTROUND : DWMWCP_DEFAULT; + ::DwmSetWindowAttribute(windows[window_id].hWnd, DWMWA_WINDOW_CORNER_PREFERENCE, &value, sizeof(value)); + } if (is_dark_mode_supported() && dark_title_available) { BOOL value = is_dark_mode(); @@ -5541,7 +5584,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; @@ -5639,6 +5682,12 @@ DisplayServer::WindowID DisplayServerWindows::_create_window(WindowMode p_mode, wd_transient_parent->transient_children.insert(id); } + wd.sharp_corners = p_flags & WINDOW_FLAG_SHARP_CORNERS_BIT; + { + DWORD value = wd.sharp_corners ? DWMWCP_DONOTROUND : DWMWCP_DEFAULT; + ::DwmSetWindowAttribute(wd.hWnd, DWMWA_WINDOW_CORNER_PREFERENCE, &value, sizeof(value)); + } + if (is_dark_mode_supported() && dark_title_available) { BOOL value = is_dark_mode(); ::DwmSetWindowAttribute(wd.hWnd, use_legacy_dark_mode_before_20H1 ? DWMWA_USE_IMMERSIVE_DARK_MODE_BEFORE_20H1 : DWMWA_USE_IMMERSIVE_DARK_MODE, &value, sizeof(value)); @@ -6363,7 +6412,10 @@ DisplayServerWindows::DisplayServerWindows(const String &p_rendering_driver, Win } WindowID main_window = _create_window(p_mode, p_vsync_mode, p_flags, Rect2i(window_position, p_resolution), false, INVALID_WINDOW_ID); - ERR_FAIL_COND_MSG(main_window == INVALID_WINDOW_ID, "Failed to create main window."); + if (main_window == INVALID_WINDOW_ID) { + r_error = ERR_UNAVAILABLE; + ERR_FAIL_MSG("Failed to create main window."); + } joypad = new JoypadWindows(&windows[MAIN_WINDOW_ID].hWnd); @@ -6373,6 +6425,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..fc72e05b1d 100644 --- a/platform/windows/display_server_windows.h +++ b/platform/windows/display_server_windows.h @@ -38,6 +38,7 @@ #include "core/config/project_settings.h" #include "core/input/input.h" +#include "core/io/image.h" #include "core/os/os.h" #include "drivers/unix/ip_unix.h" #include "drivers/wasapi/audio_driver_wasapi.h" @@ -473,6 +474,7 @@ class DisplayServerWindows : public DisplayServer { bool exclusive = false; bool context_created = false; bool mpass = false; + bool sharp_corners = false; // Used to transfer data between events using timer. WPARAM saved_wparam; @@ -524,6 +526,8 @@ class DisplayServerWindows : public DisplayServer { bool is_popup = false; Rect2i parent_safe_rect; + + bool initialized = false; }; JoypadWindows *joypad = nullptr; @@ -591,7 +595,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/native_menu_windows.h b/platform/windows/native_menu_windows.h index 235a4b332a..09e4640b40 100644 --- a/platform/windows/native_menu_windows.h +++ b/platform/windows/native_menu_windows.h @@ -31,6 +31,7 @@ #ifndef NATIVE_MENU_WINDOWS_H #define NATIVE_MENU_WINDOWS_H +#include "core/io/image.h" #include "core/templates/hash_map.h" #include "core/templates/rid_owner.h" #include "servers/display/native_menu.h" |
