summaryrefslogtreecommitdiffstats
path: root/platform
diff options
context:
space:
mode:
Diffstat (limited to 'platform')
-rw-r--r--platform/android/detect.py12
-rw-r--r--platform/android/display_server_android.cpp7
-rw-r--r--platform/android/display_server_android.h1
-rw-r--r--platform/android/doc_classes/EditorExportPlatformAndroid.xml2
-rw-r--r--platform/android/export/export_plugin.h1
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/GodotIO.java8
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/input/GodotEditText.java2
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/utils/PermissionsUtil.java6
-rw-r--r--platform/android/java_godot_io_wrapper.cpp11
-rw-r--r--platform/android/java_godot_io_wrapper.h2
-rw-r--r--platform/ios/detect.py14
-rw-r--r--platform/ios/display_server_ios.h1
-rw-r--r--platform/ios/display_server_ios.mm10
-rw-r--r--platform/ios/doc_classes/EditorExportPlatformIOS.xml125
-rw-r--r--platform/ios/export/export_plugin.cpp233
-rw-r--r--platform/linuxbsd/detect.py9
-rw-r--r--platform/linuxbsd/wayland/display_server_wayland.cpp2
-rw-r--r--platform/linuxbsd/x11/display_server_x11.cpp2
-rw-r--r--platform/macos/detect.py21
-rw-r--r--platform/macos/display_server_macos.mm10
-rw-r--r--platform/macos/doc_classes/EditorExportPlatformMacOS.xml7
-rw-r--r--platform/macos/export/export_plugin.cpp16
-rw-r--r--platform/macos/export/export_plugin.h1
-rw-r--r--platform/macos/godot_menu_delegate.mm6
-rw-r--r--platform/web/detect.py8
-rw-r--r--platform/web/js/libs/library_godot_fetch.js16
-rw-r--r--platform/windows/detect.py61
-rw-r--r--platform/windows/display_server_windows.cpp71
-rw-r--r--platform/windows/display_server_windows.h6
-rw-r--r--platform/windows/native_menu_windows.h1
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]&lt;dict&gt;[/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]
+ &lt;key&gt;key_name&lt;/key&gt;
+ &lt;string&gt;value&lt;/string&gt;
+ [/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"