summaryrefslogtreecommitdiffstats
path: root/platform
diff options
context:
space:
mode:
Diffstat (limited to 'platform')
-rw-r--r--platform/android/detect.py4
-rw-r--r--platform/android/doc_classes/EditorExportPlatformAndroid.xml10
-rw-r--r--platform/android/export/export_plugin.cpp29
-rw-r--r--platform/android/export/gradle_export_util.cpp8
-rw-r--r--platform/android/file_access_android.h9
-rw-r--r--platform/android/file_access_filesystem_jandroid.h9
-rw-r--r--platform/android/java/app/AndroidManifest.xml1
-rw-r--r--platform/android/java/editor/src/main/AndroidManifest.xml1
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/gl/GLSurfaceView.java2
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/input/GodotInputHandler.java2
-rw-r--r--platform/ios/detect.py11
-rw-r--r--platform/ios/doc_classes/EditorExportPlatformIOS.xml4
-rw-r--r--platform/ios/export/export_plugin.cpp9
-rw-r--r--platform/linuxbsd/freedesktop_portal_desktop.cpp350
-rw-r--r--platform/linuxbsd/freedesktop_portal_desktop.h30
-rw-r--r--platform/linuxbsd/joypad_linux.cpp49
-rw-r--r--platform/linuxbsd/os_linuxbsd.cpp21
-rw-r--r--platform/linuxbsd/os_linuxbsd.h2
-rw-r--r--platform/linuxbsd/x11/display_server_x11.cpp105
-rw-r--r--platform/linuxbsd/x11/display_server_x11.h7
-rw-r--r--platform/linuxbsd/x11/gl_manager_x11.h8
-rw-r--r--platform/macos/display_server_macos.h3
-rw-r--r--platform/macos/display_server_macos.mm25
-rw-r--r--platform/macos/doc_classes/EditorExportPlatformMacOS.xml8
-rw-r--r--platform/macos/export/export_plugin.cpp13
-rw-r--r--platform/macos/godot_content_view.mm6
-rw-r--r--platform/macos/godot_window_delegate.mm1
-rw-r--r--platform/web/SCsub3
-rw-r--r--platform/web/detect.py11
-rw-r--r--platform/web/export/export_plugin.cpp5
-rw-r--r--platform/web/js/libs/library_godot_audio.js2
-rw-r--r--platform/web/js/libs/library_godot_display.js8
-rw-r--r--platform/windows/detect.py3
-rw-r--r--platform/windows/display_server_windows.cpp9
-rw-r--r--platform/windows/doc_classes/EditorExportPlatformWindows.xml6
-rw-r--r--platform/windows/export/export_plugin.cpp12
-rw-r--r--platform/windows/joypad_windows.cpp4
-rw-r--r--platform/windows/os_windows.cpp2
38 files changed, 621 insertions, 171 deletions
diff --git a/platform/android/detect.py b/platform/android/detect.py
index 2860898e5c..33c6565789 100644
--- a/platform/android/detect.py
+++ b/platform/android/detect.py
@@ -170,10 +170,6 @@ def configure(env: "Environment"):
env["RANLIB"] = compiler_path + "/llvm-ranlib"
env["AS"] = compiler_path + "/clang"
- # Disable exceptions on template builds
- if not env.editor_build:
- env.Append(CXXFLAGS=["-fno-exceptions"])
-
env.Append(
CCFLAGS=(
"-fpic -ffunction-sections -funwind-tables -fstack-protector-strong -fvisibility=hidden -fno-strict-aliasing".split()
diff --git a/platform/android/doc_classes/EditorExportPlatformAndroid.xml b/platform/android/doc_classes/EditorExportPlatformAndroid.xml
index d61d63d242..d270980d72 100644
--- a/platform/android/doc_classes/EditorExportPlatformAndroid.xml
+++ b/platform/android/doc_classes/EditorExportPlatformAndroid.xml
@@ -110,6 +110,10 @@
<member name="package/show_in_android_tv" type="bool" setter="" getter="">
If [code]true[/code], this app will show in Android TV launcher UI.
</member>
+ <member name="package/show_in_app_library" type="bool" setter="" getter="">
+ If [code]true[/code], this app will show in the device's app library.
+ [b]Note:[/b] This is [code]true[/code] by default.
+ </member>
<member name="package/signed" type="bool" setter="" getter="">
If [code]true[/code], package signing is enabled.
</member>
@@ -150,7 +154,7 @@
Allows an application to act as an AccountAuthenticator for the AccountManager.
</member>
<member name="permissions/battery_stats" type="bool" setter="" getter="">
- Allows an application to collect battery statistics. Sett [url=https://developer.android.com/reference/android/Manifest.permission#BATTERY_STATS]BATTERY_STATS[/url].
+ Allows an application to collect battery statistics. See [url=https://developer.android.com/reference/android/Manifest.permission#BATTERY_STATS]BATTERY_STATS[/url].
</member>
<member name="permissions/bind_accessibility_service" type="bool" setter="" getter="">
Must be required by an AccessibilityService, to ensure that only the system can bind to it. See [url=https://developer.android.com/reference/android/Manifest.permission#BIND_ACCESSIBILITY_SERVICE]BIND_ACCESSIBILITY_SERVICE[/url].
@@ -579,10 +583,10 @@
If [code]true[/code], allows the application to participate in the backup and restore infrastructure.
</member>
<member name="version/code" type="int" setter="" getter="">
- Machine-readable application version.
+ Machine-readable application version. This must be incremented for every new release pushed to the Play Store.
</member>
<member name="version/name" type="String" setter="" getter="">
- Application version visible to the user.
+ Application version visible to the user. Falls back to [member ProjectSettings.application/config/version] if left empty.
</member>
<member name="xr_features/xr_mode" type="int" setter="" getter="">
</member>
diff --git a/platform/android/export/export_plugin.cpp b/platform/android/export/export_plugin.cpp
index 4eb516fb63..21de46f4fc 100644
--- a/platform/android/export/export_plugin.cpp
+++ b/platform/android/export/export_plugin.cpp
@@ -47,6 +47,7 @@
#include "editor/editor_paths.h"
#include "editor/editor_scale.h"
#include "editor/editor_settings.h"
+#include "editor/import/resource_importer_texture_settings.h"
#include "main/splash.gen.h"
#include "scene/resources/image_texture.h"
@@ -904,7 +905,7 @@ void EditorExportPlatformAndroid::_fix_manifest(const Ref<EditorExportPreset> &p
uint32_t string_table_ends = 0;
Vector<uint8_t> stable_extra;
- String version_name = p_preset->get("version/name");
+ String version_name = p_preset->get_version("version/name");
int version_code = p_preset->get("version/code");
String package_name = p_preset->get("package/unique_name");
@@ -1829,7 +1830,7 @@ void EditorExportPlatformAndroid::get_export_options(List<ExportOption> *r_optio
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "keystore/release_password", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SECRET), ""));
r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "version/code", PROPERTY_HINT_RANGE, "1,4096,1,or_greater"), 1));
- r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "version/name"), "1.0"));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "version/name", PROPERTY_HINT_PLACEHOLDER_TEXT, "Leave empty to use project version"), ""));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "package/unique_name", PROPERTY_HINT_PLACEHOLDER_TEXT, "ext.domain.name"), "org.godotengine.$genname", false, true));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "package/name", PROPERTY_HINT_PLACEHOLDER_TEXT, "Game Name [default if blank]"), ""));
@@ -1838,6 +1839,7 @@ void EditorExportPlatformAndroid::get_export_options(List<ExportOption> *r_optio
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "package/retain_data_on_uninstall"), false));
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "package/exclude_from_recents"), false));
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "package/show_in_android_tv"), false));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "package/show_in_app_library"), true));
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "package/show_as_launcher_app"), false));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, launcher_icon_option, PROPERTY_HINT_FILE, "*.png"), ""));
@@ -1980,7 +1982,7 @@ Error EditorExportPlatformAndroid::run(const Ref<EditorExportPreset> &p_preset,
String output;
bool remove_prev = EDITOR_GET("export/android/one_click_deploy_clear_previous_install");
- String version_name = p_preset->get("version/name");
+ String version_name = p_preset->get_version("version/name");
String package_name = p_preset->get("package/unique_name");
if (remove_prev) {
@@ -2225,17 +2227,15 @@ String EditorExportPlatformAndroid::get_apksigner_path(int p_target_sdk, bool p_
}
bool EditorExportPlatformAndroid::has_valid_export_configuration(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates, bool p_debug) const {
-#ifdef MODULE_MONO_ENABLED
- // Don't check for additional errors, as this particular error cannot be resolved.
- r_error += TTR("Exporting to Android is currently not supported in Godot 4 when using C#/.NET. Use Godot 3 to target Android with C#/Mono instead.") + "\n";
- r_error += TTR("If this project does not use C#, use a non-C# editor build to export the project.") + "\n";
- return false;
-#else
-
String err;
bool valid = false;
const bool gradle_build_enabled = p_preset->get("gradle_build/use_gradle_build");
+#ifdef MODULE_MONO_ENABLED
+ // Android export is still a work in progress, keep a message as a warning.
+ err += TTR("Exporting to Android when using C#/.NET is experimental.") + "\n";
+#endif
+
// Look for export templates (first official, and if defined custom templates).
if (!gradle_build_enabled) {
@@ -2365,7 +2365,6 @@ bool EditorExportPlatformAndroid::has_valid_export_configuration(const Ref<Edito
}
return valid;
-#endif // !MODULE_MONO_ENABLED
}
bool EditorExportPlatformAndroid::has_valid_project_configuration(const Ref<EditorExportPreset> &p_preset, String &r_error) const {
@@ -2386,10 +2385,8 @@ bool EditorExportPlatformAndroid::has_valid_project_configuration(const Ref<Edit
}
}
- String etc_error = test_etc2();
- if (!etc_error.is_empty()) {
+ if (!ResourceImporterTextureSettings::should_import_etc2_astc()) {
valid = false;
- err += etc_error;
}
String min_sdk_str = p_preset->get("gradle_build/min_sdk");
@@ -2893,7 +2890,7 @@ Error EditorExportPlatformAndroid::export_project_helper(const Ref<EditorExportP
String package_name = get_package_name(p_preset->get("package/unique_name"));
String version_code = itos(p_preset->get("version/code"));
- String version_name = p_preset->get("version/name");
+ String version_name = p_preset->get_version("version/name");
String min_sdk_version = p_preset->get("gradle_build/min_sdk");
if (!min_sdk_version.is_valid_int()) {
min_sdk_version = itos(VULKAN_MIN_SDK_VERSION);
@@ -3111,7 +3108,7 @@ Error EditorExportPlatformAndroid::export_project_helper(const Ref<EditorExportP
String cmdline = p_preset->get("command_line/extra_args");
- String version_name = p_preset->get("version/name");
+ String version_name = p_preset->get_version("version/name");
String package_name = p_preset->get("package/unique_name");
String apk_expansion_pkey = p_preset->get("apk_expansion/public_key");
diff --git a/platform/android/export/gradle_export_util.cpp b/platform/android/export/gradle_export_util.cpp
index d0d0c34bb4..0915009235 100644
--- a/platform/android/export/gradle_export_util.cpp
+++ b/platform/android/export/gradle_export_util.cpp
@@ -269,7 +269,12 @@ String _get_activity_tag(const Ref<EditorExportPlatform> &p_export_platform, con
manifest_activity_text += " <intent-filter>\n"
" <action android:name=\"android.intent.action.MAIN\" />\n"
- " <category android:name=\"android.intent.category.LAUNCHER\" />\n";
+ " <category android:name=\"android.intent.category.DEFAULT\" />\n";
+
+ bool show_in_app_library = p_preset->get("package/show_in_app_library");
+ if (show_in_app_library) {
+ manifest_activity_text += " <category android:name=\"android.intent.category.LAUNCHER\" />\n";
+ }
bool uses_leanback_category = p_preset->get("package/show_in_android_tv");
if (uses_leanback_category) {
@@ -279,7 +284,6 @@ String _get_activity_tag(const Ref<EditorExportPlatform> &p_export_platform, con
bool uses_home_category = p_preset->get("package/show_as_launcher_app");
if (uses_home_category) {
manifest_activity_text += " <category android:name=\"android.intent.category.HOME\" />\n";
- manifest_activity_text += " <category android:name=\"android.intent.category.DEFAULT\" />\n";
}
manifest_activity_text += " </intent-filter>\n";
diff --git a/platform/android/file_access_android.h b/platform/android/file_access_android.h
index 7ff5c4d117..3aa4ca98fc 100644
--- a/platform/android/file_access_android.h
+++ b/platform/android/file_access_android.h
@@ -76,8 +76,13 @@ public:
virtual bool file_exists(const String &p_path) override; // return true if a file exists
virtual uint64_t _get_modified_time(const String &p_file) override { return 0; }
- virtual uint32_t _get_unix_permissions(const String &p_file) override { return 0; }
- virtual Error _set_unix_permissions(const String &p_file, uint32_t p_permissions) override { return FAILED; }
+ virtual BitField<FileAccess::UnixPermissionFlags> _get_unix_permissions(const String &p_file) override { return 0; }
+ virtual Error _set_unix_permissions(const String &p_file, BitField<FileAccess::UnixPermissionFlags> p_permissions) override { return FAILED; }
+
+ virtual bool _get_hidden_attribute(const String &p_file) override { return false; }
+ virtual Error _set_hidden_attribute(const String &p_file, bool p_hidden) override { return ERR_UNAVAILABLE; }
+ virtual bool _get_read_only_attribute(const String &p_file) override { return false; }
+ virtual Error _set_read_only_attribute(const String &p_file, bool p_ro) override { return ERR_UNAVAILABLE; }
virtual void close() override;
diff --git a/platform/android/file_access_filesystem_jandroid.h b/platform/android/file_access_filesystem_jandroid.h
index 739c8a0925..0c3f8d7259 100644
--- a/platform/android/file_access_filesystem_jandroid.h
+++ b/platform/android/file_access_filesystem_jandroid.h
@@ -91,8 +91,13 @@ public:
static void setup(jobject p_file_access_handler);
virtual uint64_t _get_modified_time(const String &p_file) override;
- virtual uint32_t _get_unix_permissions(const String &p_file) override { return 0; }
- virtual Error _set_unix_permissions(const String &p_file, uint32_t p_permissions) override { return FAILED; }
+ virtual BitField<FileAccess::UnixPermissionFlags> _get_unix_permissions(const String &p_file) override { return 0; }
+ virtual Error _set_unix_permissions(const String &p_file, BitField<FileAccess::UnixPermissionFlags> p_permissions) override { return FAILED; }
+
+ virtual bool _get_hidden_attribute(const String &p_file) override { return false; }
+ virtual Error _set_hidden_attribute(const String &p_file, bool p_hidden) override { return ERR_UNAVAILABLE; }
+ virtual bool _get_read_only_attribute(const String &p_file) override { return false; }
+ virtual Error _set_read_only_attribute(const String &p_file, bool p_ro) override { return ERR_UNAVAILABLE; }
virtual void close() override;
diff --git a/platform/android/java/app/AndroidManifest.xml b/platform/android/java/app/AndroidManifest.xml
index 542ab51660..56d403a263 100644
--- a/platform/android/java/app/AndroidManifest.xml
+++ b/platform/android/java/app/AndroidManifest.xml
@@ -45,6 +45,7 @@
<intent-filter>
<action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
diff --git a/platform/android/java/editor/src/main/AndroidManifest.xml b/platform/android/java/editor/src/main/AndroidManifest.xml
index c7b2c8ad67..cb89d6e1b0 100644
--- a/platform/android/java/editor/src/main/AndroidManifest.xml
+++ b/platform/android/java/editor/src/main/AndroidManifest.xml
@@ -45,6 +45,7 @@
<intent-filter>
<action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
diff --git a/platform/android/java/lib/src/org/godotengine/godot/gl/GLSurfaceView.java b/platform/android/java/lib/src/org/godotengine/godot/gl/GLSurfaceView.java
index 8449c08b88..56397bb2c2 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/gl/GLSurfaceView.java
+++ b/platform/android/java/lib/src/org/godotengine/godot/gl/GLSurfaceView.java
@@ -867,7 +867,7 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback
*/
public interface EGLConfigChooser {
/**
- * Choose a configuration from the list. Implementors typically
+ * Choose a configuration from the list. Implementers typically
* implement this method by calling
* {@link EGL10#eglChooseConfig} and iterating through the results. Please consult the
* EGL specification available from The Khronos Group to learn how to call eglChooseConfig.
diff --git a/platform/android/java/lib/src/org/godotengine/godot/input/GodotInputHandler.java b/platform/android/java/lib/src/org/godotengine/godot/input/GodotInputHandler.java
index 185d03fe39..1a25be0460 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/input/GodotInputHandler.java
+++ b/platform/android/java/lib/src/org/godotengine/godot/input/GodotInputHandler.java
@@ -306,7 +306,7 @@ public class GodotInputHandler implements InputManager.InputDeviceListener {
return;
}
- // Assign first available number. Re-use numbers where possible.
+ // Assign first available number. Reuse numbers where possible.
final int id = assignJoystickIdNumber(deviceId);
final Joystick joystick = new Joystick();
diff --git a/platform/ios/detect.py b/platform/ios/detect.py
index f5501e3d85..e11c0b7d91 100644
--- a/platform/ios/detect.py
+++ b/platform/ios/detect.py
@@ -30,7 +30,6 @@ def get_opts():
),
("IOS_SDK_PATH", "Path to the iOS SDK", ""),
BoolVariable("ios_simulator", "Build for iOS Simulator", False),
- BoolVariable("ios_exceptions", "Enable exceptions", False),
("ios_triple", "Triple for ios toolchain", ""),
]
@@ -85,19 +84,18 @@ def configure(env: "Environment"):
env["ENV"]["PATH"] = env["IOS_TOOLCHAIN_PATH"] + "/Developer/usr/bin/:" + env["ENV"]["PATH"]
compiler_path = "$IOS_TOOLCHAIN_PATH/usr/bin/${ios_triple}"
- s_compiler_path = "$IOS_TOOLCHAIN_PATH/Developer/usr/bin/"
ccache_path = os.environ.get("CCACHE")
if ccache_path is None:
env["CC"] = compiler_path + "clang"
env["CXX"] = compiler_path + "clang++"
- env["S_compiler"] = s_compiler_path + "gcc"
+ env["S_compiler"] = compiler_path + "clang"
else:
# there aren't any ccache wrappers available for iOS,
# to enable caching we need to prepend the path to the ccache binary
env["CC"] = ccache_path + " " + compiler_path + "clang"
env["CXX"] = ccache_path + " " + compiler_path + "clang++"
- env["S_compiler"] = ccache_path + " " + s_compiler_path + "gcc"
+ env["S_compiler"] = ccache_path + " " + compiler_path + "clang"
env["AR"] = compiler_path + "ar"
env["RANLIB"] = compiler_path + "ranlib"
@@ -139,11 +137,6 @@ def configure(env: "Environment"):
env.Append(ASFLAGS=["-arch", "arm64"])
env.Append(CPPDEFINES=["NEED_LONG_INT"])
- if env["ios_exceptions"]:
- env.Append(CCFLAGS=["-fexceptions"])
- else:
- env.Append(CCFLAGS=["-fno-exceptions"])
-
# Temp fix for ABS/MAX/MIN macros in iOS SDK blocking compilation
env.Append(CCFLAGS=["-Wno-ambiguous-macro"])
diff --git a/platform/ios/doc_classes/EditorExportPlatformIOS.xml b/platform/ios/doc_classes/EditorExportPlatformIOS.xml
index 84bc0e1277..563c057266 100644
--- a/platform/ios/doc_classes/EditorExportPlatformIOS.xml
+++ b/platform/ios/doc_classes/EditorExportPlatformIOS.xml
@@ -45,7 +45,7 @@
Can be overridden with the environment variable [code]GODOT_IOS_PROVISIONING_PROFILE_UUID_RELEASE[/code].
</member>
<member name="application/short_version" type="String" setter="" getter="">
- Application version visible to the user, can only contain numeric characters ([code]0-9[/code]) and periods ([code].[/code]).
+ Application version visible to the user, can only contain numeric characters ([code]0-9[/code]) and periods ([code].[/code]). Falls back to [member ProjectSettings.application/config/version] if left empty.
</member>
<member name="application/signature" type="String" setter="" getter="">
A four-character creator code that is specific to the bundle. Optional.
@@ -54,7 +54,7 @@
Supported device family.
</member>
<member name="application/version" type="String" setter="" getter="">
- Machine-readable application version, in the [code]major.minor.patch[/code] format, can only contain numeric characters ([code]0-9[/code]) and periods ([code].[/code]).
+ Machine-readable application version, in the [code]major.minor.patch[/code] format, can only contain numeric characters ([code]0-9[/code]) and periods ([code].[/code]). This must be incremented on every new release pushed to the App Store.
</member>
<member name="architectures/arm64" type="bool" setter="" getter="">
If [code]true[/code], [code]arm64[/code] binaries are included into exported project.
diff --git a/platform/ios/export/export_plugin.cpp b/platform/ios/export/export_plugin.cpp
index 544bfb71e0..b6320fb22b 100644
--- a/platform/ios/export/export_plugin.cpp
+++ b/platform/ios/export/export_plugin.cpp
@@ -39,6 +39,7 @@
#include "editor/editor_paths.h"
#include "editor/editor_scale.h"
#include "editor/export/editor_export.h"
+#include "editor/import/resource_importer_texture_settings.h"
#include "editor/plugins/script_editor_plugin.h"
#include "modules/modules_enabled.gen.h" // For mono and svg.
@@ -177,7 +178,7 @@ void EditorExportPlatformIOS::get_export_options(List<ExportOption> *r_options)
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/bundle_identifier", PROPERTY_HINT_PLACEHOLDER_TEXT, "com.example.game"), "", false, true));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/signature"), ""));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/short_version"), "1.0"));
- r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/version"), "1.0"));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/version", PROPERTY_HINT_PLACEHOLDER_TEXT, "Leave empty to use project version"), ""));
r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "application/icon_interpolation", PROPERTY_HINT_ENUM, "Nearest neighbor,Bilinear,Cubic,Trilinear,Lanczos"), 4));
r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "application/launch_screens_interpolation", PROPERTY_HINT_ENUM, "Nearest neighbor,Bilinear,Cubic,Trilinear,Lanczos"), 4));
@@ -284,7 +285,7 @@ void EditorExportPlatformIOS::_fix_config_file(const Ref<EditorExportPreset> &p_
} else if (lines[i].find("$short_version") != -1) {
strnew += lines[i].replace("$short_version", p_preset->get("application/short_version")) + "\n";
} else if (lines[i].find("$version") != -1) {
- strnew += lines[i].replace("$version", p_preset->get("application/version")) + "\n";
+ strnew += lines[i].replace("$version", p_preset->get_version("application/version")) + "\n";
} else if (lines[i].find("$signature") != -1) {
strnew += lines[i].replace("$signature", p_preset->get("application/signature")) + "\n";
} else if (lines[i].find("$team_id") != -1) {
@@ -1984,10 +1985,8 @@ bool EditorExportPlatformIOS::has_valid_project_configuration(const Ref<EditorEx
}
}
- const String etc_error = test_etc2();
- if (!etc_error.is_empty()) {
+ if (!ResourceImporterTextureSettings::should_import_etc2_astc()) {
valid = false;
- err += etc_error;
}
if (!err.is_empty()) {
diff --git a/platform/linuxbsd/freedesktop_portal_desktop.cpp b/platform/linuxbsd/freedesktop_portal_desktop.cpp
index c3cb0f411d..91c14e0e91 100644
--- a/platform/linuxbsd/freedesktop_portal_desktop.cpp
+++ b/platform/linuxbsd/freedesktop_portal_desktop.cpp
@@ -32,6 +32,7 @@
#ifdef DBUS_ENABLED
+#include "core/crypto/crypto_core.h"
#include "core/error/error_macros.h"
#include "core/os/os.h"
#include "core/string/ustring.h"
@@ -43,12 +44,15 @@
#include <dbus/dbus.h>
#endif
+#include <unistd.h>
+
#define BUS_OBJECT_NAME "org.freedesktop.portal.Desktop"
#define BUS_OBJECT_PATH "/org/freedesktop/portal/desktop"
#define BUS_INTERFACE_SETTINGS "org.freedesktop.portal.Settings"
+#define BUS_INTERFACE_FILE_CHOOSER "org.freedesktop.portal.FileChooser"
-static bool try_parse_variant(DBusMessage *p_reply_message, int p_type, void *r_value) {
+bool FreeDesktopPortalDesktop::try_parse_variant(DBusMessage *p_reply_message, int p_type, void *r_value) {
DBusMessageIter iter[3];
dbus_message_iter_init(p_reply_message, &iter[0]);
@@ -80,11 +84,11 @@ bool FreeDesktopPortalDesktop::read_setting(const char *p_namespace, const char
DBusConnection *bus = dbus_bus_get(DBUS_BUS_SESSION, &error);
if (dbus_error_is_set(&error)) {
- dbus_error_free(&error);
- unsupported = true;
if (OS::get_singleton()->is_stdout_verbose()) {
- ERR_PRINT(String() + "Error opening D-Bus connection: " + error.message);
+ ERR_PRINT(vformat("Error opening D-Bus connection: %s", error.message));
}
+ dbus_error_free(&error);
+ unsupported = true;
return false;
}
@@ -100,11 +104,11 @@ bool FreeDesktopPortalDesktop::read_setting(const char *p_namespace, const char
DBusMessage *reply = dbus_connection_send_with_reply_and_block(bus, message, 50, &error);
dbus_message_unref(message);
if (dbus_error_is_set(&error)) {
- dbus_error_free(&error);
- dbus_connection_unref(bus);
if (OS::get_singleton()->is_stdout_verbose()) {
- ERR_PRINT(String() + "Error on D-Bus communication: " + error.message);
+ ERR_PRINT(vformat("Error on D-Bus communication: %s", error.message));
}
+ dbus_error_free(&error);
+ dbus_connection_unref(bus);
return false;
}
@@ -126,6 +130,317 @@ uint32_t FreeDesktopPortalDesktop::get_appearance_color_scheme() {
return value;
}
+static const char *cs_empty = "";
+
+void FreeDesktopPortalDesktop::append_dbus_string(DBusMessageIter *p_iter, const String &p_string) {
+ CharString cs = p_string.utf8();
+ const char *cs_ptr = cs.ptr();
+ if (cs_ptr) {
+ dbus_message_iter_append_basic(p_iter, DBUS_TYPE_STRING, &cs_ptr);
+ } else {
+ dbus_message_iter_append_basic(p_iter, DBUS_TYPE_STRING, &cs_empty);
+ }
+}
+
+void FreeDesktopPortalDesktop::append_dbus_dict_filters(DBusMessageIter *p_iter, const Vector<String> &p_filters) {
+ DBusMessageIter dict_iter;
+ DBusMessageIter var_iter;
+ DBusMessageIter arr_iter;
+ const char *filters_key = "filters";
+
+ dbus_message_iter_open_container(p_iter, DBUS_TYPE_DICT_ENTRY, nullptr, &dict_iter);
+ dbus_message_iter_append_basic(&dict_iter, DBUS_TYPE_STRING, &filters_key);
+ dbus_message_iter_open_container(&dict_iter, DBUS_TYPE_VARIANT, "a(sa(us))", &var_iter);
+ dbus_message_iter_open_container(&var_iter, DBUS_TYPE_ARRAY, "(sa(us))", &arr_iter);
+ for (int i = 0; i < p_filters.size(); i++) {
+ Vector<String> tokens = p_filters[i].split(";");
+ if (tokens.size() == 2) {
+ DBusMessageIter struct_iter;
+ DBusMessageIter array_iter;
+ DBusMessageIter array_struct_iter;
+ dbus_message_iter_open_container(&arr_iter, DBUS_TYPE_STRUCT, nullptr, &struct_iter);
+ append_dbus_string(&struct_iter, tokens[0]);
+
+ dbus_message_iter_open_container(&struct_iter, DBUS_TYPE_ARRAY, "(us)", &array_iter);
+ dbus_message_iter_open_container(&array_iter, DBUS_TYPE_STRUCT, nullptr, &array_struct_iter);
+ {
+ const unsigned nil = 0;
+ dbus_message_iter_append_basic(&array_struct_iter, DBUS_TYPE_UINT32, &nil);
+ }
+ append_dbus_string(&array_struct_iter, tokens[1]);
+ dbus_message_iter_close_container(&array_iter, &array_struct_iter);
+ dbus_message_iter_close_container(&struct_iter, &array_iter);
+ dbus_message_iter_close_container(&arr_iter, &struct_iter);
+ }
+ }
+ dbus_message_iter_close_container(&var_iter, &arr_iter);
+ dbus_message_iter_close_container(&dict_iter, &var_iter);
+ dbus_message_iter_close_container(p_iter, &dict_iter);
+}
+
+void FreeDesktopPortalDesktop::append_dbus_dict_string(DBusMessageIter *p_iter, const String &p_key, const String &p_value, bool p_as_byte_array) {
+ DBusMessageIter dict_iter;
+ DBusMessageIter var_iter;
+ dbus_message_iter_open_container(p_iter, DBUS_TYPE_DICT_ENTRY, nullptr, &dict_iter);
+ append_dbus_string(&dict_iter, p_key);
+
+ if (p_as_byte_array) {
+ DBusMessageIter arr_iter;
+ dbus_message_iter_open_container(&dict_iter, DBUS_TYPE_VARIANT, "ay", &var_iter);
+ dbus_message_iter_open_container(&var_iter, DBUS_TYPE_ARRAY, "y", &arr_iter);
+ CharString cs = p_value.utf8();
+ const char *cs_ptr = cs.get_data();
+ do {
+ dbus_message_iter_append_basic(&arr_iter, DBUS_TYPE_BYTE, cs_ptr);
+ } while (*cs_ptr++);
+ dbus_message_iter_close_container(&var_iter, &arr_iter);
+ } else {
+ dbus_message_iter_open_container(&dict_iter, DBUS_TYPE_VARIANT, "s", &var_iter);
+ append_dbus_string(&var_iter, p_value);
+ }
+
+ dbus_message_iter_close_container(&dict_iter, &var_iter);
+ dbus_message_iter_close_container(p_iter, &dict_iter);
+}
+
+void FreeDesktopPortalDesktop::append_dbus_dict_bool(DBusMessageIter *p_iter, const String &p_key, bool p_value) {
+ DBusMessageIter dict_iter;
+ DBusMessageIter var_iter;
+ dbus_message_iter_open_container(p_iter, DBUS_TYPE_DICT_ENTRY, nullptr, &dict_iter);
+ append_dbus_string(&dict_iter, p_key);
+
+ dbus_message_iter_open_container(&dict_iter, DBUS_TYPE_VARIANT, "b", &var_iter);
+ {
+ int val = p_value;
+ dbus_message_iter_append_basic(&var_iter, DBUS_TYPE_BOOLEAN, &val);
+ }
+
+ dbus_message_iter_close_container(&dict_iter, &var_iter);
+ dbus_message_iter_close_container(p_iter, &dict_iter);
+}
+
+bool FreeDesktopPortalDesktop::file_chooser_parse_response(DBusMessageIter *p_iter, bool &r_cancel, Vector<String> &r_urls) {
+ ERR_FAIL_COND_V(dbus_message_iter_get_arg_type(p_iter) != DBUS_TYPE_UINT32, false);
+
+ dbus_uint32_t resp_code;
+ dbus_message_iter_get_basic(p_iter, &resp_code);
+ if (resp_code != 0) {
+ r_cancel = true;
+ } else {
+ r_cancel = false;
+ ERR_FAIL_COND_V(!dbus_message_iter_next(p_iter), false);
+ ERR_FAIL_COND_V(dbus_message_iter_get_arg_type(p_iter) != DBUS_TYPE_ARRAY, false);
+
+ DBusMessageIter dict_iter;
+ dbus_message_iter_recurse(p_iter, &dict_iter);
+ while (dbus_message_iter_get_arg_type(&dict_iter) == DBUS_TYPE_DICT_ENTRY) {
+ DBusMessageIter iter;
+ dbus_message_iter_recurse(&dict_iter, &iter);
+ if (dbus_message_iter_get_arg_type(&iter) == DBUS_TYPE_STRING) {
+ const char *key;
+ dbus_message_iter_get_basic(&iter, &key);
+ dbus_message_iter_next(&iter);
+
+ DBusMessageIter var_iter;
+ dbus_message_iter_recurse(&iter, &var_iter);
+ if (strcmp(key, "uris") == 0) {
+ if (dbus_message_iter_get_arg_type(&var_iter) == DBUS_TYPE_ARRAY) {
+ DBusMessageIter uri_iter;
+ dbus_message_iter_recurse(&var_iter, &uri_iter);
+ while (dbus_message_iter_get_arg_type(&uri_iter) == DBUS_TYPE_STRING) {
+ const char *value;
+ dbus_message_iter_get_basic(&uri_iter, &value);
+ r_urls.push_back(String::utf8(value).trim_prefix("file://").uri_decode());
+ if (!dbus_message_iter_next(&uri_iter)) {
+ break;
+ }
+ }
+ }
+ }
+ }
+ if (!dbus_message_iter_next(&dict_iter)) {
+ break;
+ }
+ }
+ }
+ return true;
+}
+
+Error FreeDesktopPortalDesktop::file_dialog_show(const String &p_xid, const String &p_title, const String &p_current_directory, const String &p_filename, DisplayServer::FileDialogMode p_mode, const Vector<String> &p_filters, const Callable &p_callback) {
+ if (unsupported) {
+ return FAILED;
+ }
+
+ DBusError err;
+ dbus_error_init(&err);
+
+ // Open connection and add signal handler.
+ FileDialogData fd;
+ fd.callback = p_callback;
+
+ CryptoCore::RandomGenerator rng;
+ ERR_FAIL_COND_V_MSG(rng.init(), FAILED, "Failed to initialize random number generator.");
+ uint8_t uuid[64];
+ Error rng_err = rng.get_random_bytes(uuid, 64);
+ ERR_FAIL_COND_V_MSG(rng_err, rng_err, "Failed to generate unique token.");
+
+ fd.connection = dbus_bus_get(DBUS_BUS_SESSION, &err);
+ if (dbus_error_is_set(&err)) {
+ ERR_PRINT(vformat("Failed to open DBus connection: %s", err.message));
+ dbus_error_free(&err);
+ unsupported = true;
+ return FAILED;
+ }
+
+ String dbus_unique_name = String::utf8(dbus_bus_get_unique_name(fd.connection));
+ String token = String::hex_encode_buffer(uuid, 64);
+ String path = vformat("/org/freedesktop/portal/desktop/request/%s/%s", dbus_unique_name.replace(".", "_").replace(":", ""), token);
+
+ fd.path = vformat("type='signal',sender='org.freedesktop.portal.Desktop',path='%s',interface='org.freedesktop.portal.Request',member='Response',destination='%s'", path, dbus_unique_name);
+ dbus_bus_add_match(fd.connection, fd.path.utf8().get_data(), &err);
+ if (dbus_error_is_set(&err)) {
+ ERR_PRINT(vformat("Failed to add DBus match: %s", err.message));
+ dbus_error_free(&err);
+ dbus_connection_unref(fd.connection);
+ return FAILED;
+ }
+
+ // Generate FileChooser message.
+ const char *method = nullptr;
+ switch (p_mode) {
+ case DisplayServer::FILE_DIALOG_MODE_SAVE_FILE: {
+ method = "SaveFile";
+ } break;
+ case DisplayServer::FILE_DIALOG_MODE_OPEN_ANY:
+ case DisplayServer::FILE_DIALOG_MODE_OPEN_FILE:
+ case DisplayServer::FILE_DIALOG_MODE_OPEN_DIR:
+ case DisplayServer::FILE_DIALOG_MODE_OPEN_FILES: {
+ method = "OpenFile";
+ } break;
+ }
+
+ DBusMessage *message = dbus_message_new_method_call(BUS_OBJECT_NAME, BUS_OBJECT_PATH, BUS_INTERFACE_FILE_CHOOSER, method);
+ {
+ DBusMessageIter iter;
+ dbus_message_iter_init_append(message, &iter);
+
+ append_dbus_string(&iter, p_xid);
+ append_dbus_string(&iter, p_title);
+
+ DBusMessageIter arr_iter;
+ dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "{sv}", &arr_iter);
+
+ append_dbus_dict_string(&arr_iter, "handle_token", token);
+ append_dbus_dict_bool(&arr_iter, "multiple", p_mode == DisplayServer::FILE_DIALOG_MODE_OPEN_FILES);
+ append_dbus_dict_bool(&arr_iter, "directory", p_mode == DisplayServer::FILE_DIALOG_MODE_OPEN_DIR);
+ append_dbus_dict_filters(&arr_iter, p_filters);
+ append_dbus_dict_string(&arr_iter, "current_folder", p_current_directory, true);
+ if (p_mode == DisplayServer::FILE_DIALOG_MODE_SAVE_FILE) {
+ append_dbus_dict_string(&arr_iter, "current_name", p_filename);
+ }
+
+ dbus_message_iter_close_container(&iter, &arr_iter);
+ }
+
+ DBusMessage *reply = dbus_connection_send_with_reply_and_block(fd.connection, message, DBUS_TIMEOUT_INFINITE, &err);
+ dbus_message_unref(message);
+
+ if (!reply || dbus_error_is_set(&err)) {
+ ERR_PRINT(vformat("Failed to send DBus message: %s", err.message));
+ dbus_error_free(&err);
+ dbus_bus_remove_match(fd.connection, fd.path.utf8().get_data(), &err);
+ dbus_connection_unref(fd.connection);
+ return FAILED;
+ }
+
+ // Update signal path.
+ {
+ DBusMessageIter iter;
+ if (dbus_message_iter_init(reply, &iter)) {
+ if (dbus_message_iter_get_arg_type(&iter) == DBUS_TYPE_OBJECT_PATH) {
+ const char *new_path = nullptr;
+ dbus_message_iter_get_basic(&iter, &new_path);
+ if (String::utf8(new_path) != path) {
+ dbus_bus_remove_match(fd.connection, fd.path.utf8().get_data(), &err);
+ if (dbus_error_is_set(&err)) {
+ ERR_PRINT(vformat("Failed to remove DBus match: %s", err.message));
+ dbus_error_free(&err);
+ dbus_connection_unref(fd.connection);
+ return FAILED;
+ }
+ fd.path = String::utf8(new_path);
+ dbus_bus_add_match(fd.connection, fd.path.utf8().get_data(), &err);
+ if (dbus_error_is_set(&err)) {
+ ERR_PRINT(vformat("Failed to add DBus match: %s", err.message));
+ dbus_error_free(&err);
+ dbus_connection_unref(fd.connection);
+ return FAILED;
+ }
+ }
+ }
+ }
+ }
+ dbus_message_unref(reply);
+
+ MutexLock lock(file_dialog_mutex);
+ file_dialogs.push_back(fd);
+
+ return OK;
+}
+
+void FreeDesktopPortalDesktop::_thread_file_dialog_monitor(void *p_ud) {
+ FreeDesktopPortalDesktop *portal = (FreeDesktopPortalDesktop *)p_ud;
+
+ while (!portal->file_dialog_thread_abort.is_set()) {
+ {
+ MutexLock lock(portal->file_dialog_mutex);
+ for (int i = portal->file_dialogs.size() - 1; i >= 0; i--) {
+ bool remove = false;
+ {
+ FreeDesktopPortalDesktop::FileDialogData &fd = portal->file_dialogs.write[i];
+ if (fd.connection) {
+ while (true) {
+ DBusMessage *msg = dbus_connection_pop_message(fd.connection);
+ if (!msg) {
+ break;
+ } else if (dbus_message_is_signal(msg, "org.freedesktop.portal.Request", "Response")) {
+ DBusMessageIter iter;
+ if (dbus_message_iter_init(msg, &iter)) {
+ bool cancel = false;
+ Vector<String> uris;
+ file_chooser_parse_response(&iter, cancel, uris);
+
+ if (fd.callback.is_valid()) {
+ Variant v_status = !cancel;
+ Variant v_files = uris;
+ Variant *v_args[2] = { &v_status, &v_files };
+ fd.callback.call_deferredp((const Variant **)&v_args, 2);
+ }
+ }
+ dbus_message_unref(msg);
+
+ DBusError err;
+ dbus_error_init(&err);
+ dbus_bus_remove_match(fd.connection, fd.path.utf8().get_data(), &err);
+ dbus_error_free(&err);
+ dbus_connection_unref(fd.connection);
+ remove = true;
+ break;
+ }
+ dbus_message_unref(msg);
+ }
+ dbus_connection_read_write(fd.connection, 0);
+ }
+ }
+ if (remove) {
+ portal->file_dialogs.remove_at(i);
+ }
+ }
+ }
+ usleep(50000);
+ }
+}
+
FreeDesktopPortalDesktop::FreeDesktopPortalDesktop() {
#ifdef SOWRAP_ENABLED
#ifdef DEBUG_ENABLED
@@ -153,6 +468,27 @@ FreeDesktopPortalDesktop::FreeDesktopPortalDesktop() {
print_verbose("PortalDesktop: Unsupported DBus library version!");
unsupported = true;
}
+
+ if (!unsupported) {
+ file_dialog_thread_abort.clear();
+ file_dialog_thread.start(FreeDesktopPortalDesktop::_thread_file_dialog_monitor, this);
+ }
+}
+
+FreeDesktopPortalDesktop::~FreeDesktopPortalDesktop() {
+ file_dialog_thread_abort.set();
+ if (file_dialog_thread.is_started()) {
+ file_dialog_thread.wait_to_finish();
+ }
+ for (FreeDesktopPortalDesktop::FileDialogData &fd : file_dialogs) {
+ if (fd.connection) {
+ DBusError err;
+ dbus_error_init(&err);
+ dbus_bus_remove_match(fd.connection, fd.path.utf8().get_data(), &err);
+ dbus_error_free(&err);
+ dbus_connection_unref(fd.connection);
+ }
+ }
}
#endif // DBUS_ENABLED
diff --git a/platform/linuxbsd/freedesktop_portal_desktop.h b/platform/linuxbsd/freedesktop_portal_desktop.h
index 1520ab3a1f..a9b83b3844 100644
--- a/platform/linuxbsd/freedesktop_portal_desktop.h
+++ b/platform/linuxbsd/freedesktop_portal_desktop.h
@@ -33,20 +33,48 @@
#ifdef DBUS_ENABLED
-#include <stdint.h>
+#include "core/os/thread.h"
+#include "servers/display_server.h"
+
+struct DBusMessage;
+struct DBusConnection;
+struct DBusMessageIter;
class FreeDesktopPortalDesktop {
private:
bool unsupported = false;
+ static bool try_parse_variant(DBusMessage *p_reply_message, int p_type, void *r_value);
// Read a setting from org.freekdesktop.portal.Settings
bool read_setting(const char *p_namespace, const char *p_key, int p_type, void *r_value);
+ static void append_dbus_string(DBusMessageIter *p_iter, const String &p_string);
+ static void append_dbus_dict_filters(DBusMessageIter *p_iter, const Vector<String> &p_filters);
+ static void append_dbus_dict_string(DBusMessageIter *p_iter, const String &p_key, const String &p_value, bool p_as_byte_array = false);
+ static void append_dbus_dict_bool(DBusMessageIter *p_iter, const String &p_key, bool p_value);
+ static bool file_chooser_parse_response(DBusMessageIter *p_iter, bool &r_cancel, Vector<String> &r_urls);
+
+ struct FileDialogData {
+ DBusConnection *connection = nullptr;
+ Callable callback;
+ String path;
+ };
+
+ Mutex file_dialog_mutex;
+ Vector<FileDialogData> file_dialogs;
+ Thread file_dialog_thread;
+ SafeFlag file_dialog_thread_abort;
+
+ static void _thread_file_dialog_monitor(void *p_ud);
+
public:
FreeDesktopPortalDesktop();
+ ~FreeDesktopPortalDesktop();
bool is_supported() { return !unsupported; }
+ Error file_dialog_show(const String &p_xid, const String &p_title, const String &p_current_directory, const String &p_filename, DisplayServer::FileDialogMode p_mode, const Vector<String> &p_filters, const Callable &p_callback);
+
// Retrieve the system's preferred color scheme.
// 0: No preference or unknown.
// 1: Prefer dark appearance.
diff --git a/platform/linuxbsd/joypad_linux.cpp b/platform/linuxbsd/joypad_linux.cpp
index 342cff82e9..827c567785 100644
--- a/platform/linuxbsd/joypad_linux.cpp
+++ b/platform/linuxbsd/joypad_linux.cpp
@@ -56,6 +56,14 @@
static const char *ignore_str = "/dev/input/js";
#endif
+// On Linux with Steam Input Xbox 360 devices have an index appended to their device name, this index is
+// the Steam Input gamepad index
+#define VALVE_GAMEPAD_NAME_PREFIX "Microsoft X-Box 360 pad "
+// IDs used by Steam Input virtual controllers.
+// See https://partner.steamgames.com/doc/features/steam_controller/steam_input_gamepad_emulation_bestpractices
+#define VALVE_GAMEPAD_VID 0x28DE
+#define VALVE_GAMEPAD_PID 0x11FF
+
JoypadLinux::Joypad::~Joypad() {
for (int i = 0; i < MAX_ABS; i++) {
if (abs_info[i]) {
@@ -74,31 +82,9 @@ void JoypadLinux::Joypad::reset() {
events.clear();
}
-#ifdef UDEV_ENABLED
-// This function is derived from SDL:
-// https://github.com/libsdl-org/SDL/blob/main/src/core/linux/SDL_sandbox.c#L28-L45
-static bool detect_sandbox() {
- if (access("/.flatpak-info", F_OK) == 0) {
- return true;
- }
-
- // For Snap, we check multiple variables because they might be set for
- // unrelated reasons. This is the same thing WebKitGTK does.
- if (OS::get_singleton()->has_environment("SNAP") && OS::get_singleton()->has_environment("SNAP_NAME") && OS::get_singleton()->has_environment("SNAP_REVISION")) {
- return true;
- }
-
- if (access("/run/host/container-manager", F_OK) == 0) {
- return true;
- }
-
- return false;
-}
-#endif // UDEV_ENABLED
-
JoypadLinux::JoypadLinux(Input *in) {
#ifdef UDEV_ENABLED
- if (detect_sandbox()) {
+ if (OS::get_singleton()->is_sandboxed()) {
// Linux binaries in sandboxes / containers need special handling because
// libudev doesn't work there. So we need to fallback to manual parsing
// of /dev/input in such case.
@@ -411,8 +397,23 @@ void JoypadLinux::open_joypad(const char *p_path) {
setup_joypad_properties(joypad);
sprintf(uid, "%04x%04x", BSWAP16(inpid.bustype), 0);
if (inpid.vendor && inpid.product && inpid.version) {
+ Dictionary joypad_info;
+ joypad_info["vendor_id"] = inpid.vendor;
+ joypad_info["product_id"] = inpid.product;
+ joypad_info["raw_name"] = name;
+
sprintf(uid + String(uid).length(), "%04x%04x%04x%04x%04x%04x", vendor, 0, product, 0, version, 0);
- input->joy_connection_changed(joy_num, true, name, uid);
+
+ if (inpid.vendor == VALVE_GAMEPAD_VID && inpid.product == VALVE_GAMEPAD_PID) {
+ if (name.begins_with(VALVE_GAMEPAD_NAME_PREFIX)) {
+ String idx_str = name.substr(strlen(VALVE_GAMEPAD_NAME_PREFIX));
+ if (idx_str.is_valid_int()) {
+ joypad_info["steam_input_index"] = idx_str.to_int();
+ }
+ }
+ }
+
+ input->joy_connection_changed(joy_num, true, name, uid, joypad_info);
} else {
String uidname = uid;
int uidlen = MIN(name.length(), 11);
diff --git a/platform/linuxbsd/os_linuxbsd.cpp b/platform/linuxbsd/os_linuxbsd.cpp
index 14d02a73c8..d22d398a67 100644
--- a/platform/linuxbsd/os_linuxbsd.cpp
+++ b/platform/linuxbsd/os_linuxbsd.cpp
@@ -164,6 +164,27 @@ String OS_LinuxBSD::get_processor_name() const {
ERR_FAIL_V_MSG("", String("Couldn't get the CPU model name from `/proc/cpuinfo`. Returning an empty string."));
}
+bool OS_LinuxBSD::is_sandboxed() const {
+ // This function is derived from SDL:
+ // https://github.com/libsdl-org/SDL/blob/main/src/core/linux/SDL_sandbox.c#L28-L45
+
+ if (access("/.flatpak-info", F_OK) == 0) {
+ return true;
+ }
+
+ // For Snap, we check multiple variables because they might be set for
+ // unrelated reasons. This is the same thing WebKitGTK does.
+ if (has_environment("SNAP") && has_environment("SNAP_NAME") && has_environment("SNAP_REVISION")) {
+ return true;
+ }
+
+ if (access("/run/host/container-manager", F_OK) == 0) {
+ return true;
+ }
+
+ return false;
+}
+
void OS_LinuxBSD::finalize() {
if (main_loop) {
memdelete(main_loop);
diff --git a/platform/linuxbsd/os_linuxbsd.h b/platform/linuxbsd/os_linuxbsd.h
index 007b90b82b..6917ea5ae7 100644
--- a/platform/linuxbsd/os_linuxbsd.h
+++ b/platform/linuxbsd/os_linuxbsd.h
@@ -123,6 +123,8 @@ public:
virtual String get_unique_id() const override;
virtual String get_processor_name() const override;
+ virtual bool is_sandboxed() const override;
+
virtual void alert(const String &p_alert, const String &p_title = "ALERT!") override;
virtual bool _check_internal_feature_support(const String &p_feature) override;
diff --git a/platform/linuxbsd/x11/display_server_x11.cpp b/platform/linuxbsd/x11/display_server_x11.cpp
index 2643cd3b1a..f38a9dd278 100644
--- a/platform/linuxbsd/x11/display_server_x11.cpp
+++ b/platform/linuxbsd/x11/display_server_x11.cpp
@@ -122,6 +122,9 @@ bool DisplayServerX11::has_feature(Feature p_feature) const {
case FEATURE_WINDOW_TRANSPARENCY:
//case FEATURE_HIDPI:
case FEATURE_ICON:
+#ifdef DBUS_ENABLED
+ case FEATURE_NATIVE_DIALOG:
+#endif
//case FEATURE_NATIVE_ICON:
case FEATURE_SWAP_BUFFERS:
#ifdef DBUS_ENABLED
@@ -360,6 +363,17 @@ bool DisplayServerX11::is_dark_mode() const {
}
}
+Error DisplayServerX11::file_dialog_show(const String &p_title, const String &p_current_directory, const String &p_filename, bool p_show_hidden, FileDialogMode p_mode, const Vector<String> &p_filters, const Callable &p_callback) {
+ WindowID window_id = _get_focused_window_or_popup();
+
+ if (!windows.has(window_id)) {
+ window_id = MAIN_WINDOW_ID;
+ }
+
+ String xid = vformat("x11:%x", (uint64_t)windows[window_id].x11_window);
+ return portal_desktop->file_dialog_show(xid, p_title, p_current_directory, p_filename, p_mode, p_filters, p_callback);
+}
+
#endif
void DisplayServerX11::mouse_set_mode(MouseMode p_mode) {
@@ -379,7 +393,11 @@ void DisplayServerX11::mouse_set_mode(MouseMode p_mode) {
if (show_cursor && !previously_shown) {
WindowID window_id = get_window_at_screen_position(mouse_get_position());
- if (window_id != INVALID_WINDOW_ID) {
+ if (window_id != INVALID_WINDOW_ID && window_mouseover_id != window_id) {
+ if (window_mouseover_id != INVALID_WINDOW_ID) {
+ _send_window_event(windows[window_mouseover_id], WINDOW_EVENT_MOUSE_EXIT);
+ }
+ window_mouseover_id = window_id;
_send_window_event(windows[window_id], WINDOW_EVENT_MOUSE_ENTER);
}
}
@@ -744,7 +762,7 @@ int DisplayServerX11::get_screen_count() const {
// Using Xinerama Extension
int event_base, error_base;
- if (XineramaQueryExtension(x11_display, &event_base, &error_base)) {
+ if (xinerama_ext_ok && XineramaQueryExtension(x11_display, &event_base, &error_base)) {
XineramaScreenInfo *xsi = XineramaQueryScreens(x11_display, &count);
XFree(xsi);
} else {
@@ -756,7 +774,7 @@ int DisplayServerX11::get_screen_count() const {
int DisplayServerX11::get_primary_screen() const {
int event_base, error_base;
- if (XineramaQueryExtension(x11_display, &event_base, &error_base)) {
+ if (xinerama_ext_ok && XineramaQueryExtension(x11_display, &event_base, &error_base)) {
return 0;
} else {
return XDefaultScreen(x11_display);
@@ -809,7 +827,7 @@ Rect2i DisplayServerX11::_screen_get_rect(int p_screen) const {
// Using Xinerama Extension.
int event_base, error_base;
- if (XineramaQueryExtension(x11_display, &event_base, &error_base)) {
+ if (xinerama_ext_ok && XineramaQueryExtension(x11_display, &event_base, &error_base)) {
int count;
XineramaScreenInfo *xsi = XineramaQueryScreens(x11_display, &count);
@@ -1244,7 +1262,7 @@ Ref<Image> DisplayServerX11::screen_get_image(int p_screen) const {
XImage *image = nullptr;
int event_base, error_base;
- if (XineramaQueryExtension(x11_display, &event_base, &error_base)) {
+ if (xinerama_ext_ok && XineramaQueryExtension(x11_display, &event_base, &error_base)) {
int xin_count;
XineramaScreenInfo *xsi = XineramaQueryScreens(x11_display, &xin_count);
if (p_screen < xin_count) {
@@ -1449,6 +1467,11 @@ void DisplayServerX11::delete_sub_window(WindowID p_id) {
DEBUG_LOG_X11("delete_sub_window: %lu (%u) \n", wd.x11_window, p_id);
+ if (window_mouseover_id == p_id) {
+ window_mouseover_id = INVALID_WINDOW_ID;
+ _send_window_event(windows[p_id], WINDOW_EVENT_MOUSE_EXIT);
+ }
+
window_set_rect_changed_callback(Callable(), p_id);
window_set_window_event_callback(Callable(), p_id);
window_set_input_event_callback(Callable(), p_id);
@@ -1586,6 +1609,7 @@ void DisplayServerX11::window_set_mouse_passthrough(const Vector<Vector2> &p_reg
void DisplayServerX11::_update_window_mouse_passthrough(WindowID p_window) {
ERR_FAIL_COND(!windows.has(p_window));
+ ERR_FAIL_COND(!xshaped_ext_ok);
const Vector<Vector2> region_path = windows[p_window].mpath;
@@ -2102,9 +2126,10 @@ bool DisplayServerX11::_window_maximize_check(WindowID p_window, const char *p_a
bool DisplayServerX11::_window_minimize_check(WindowID p_window) const {
const WindowData &wd = windows[p_window];
- // Using ICCCM -- Inter-Client Communication Conventions Manual
- Atom property = XInternAtom(x11_display, "WM_STATE", True);
- if (property == None) {
+ // Using EWMH instead of ICCCM, might work better for Wayland users.
+ Atom property = XInternAtom(x11_display, "_NET_WM_STATE", True);
+ Atom hidden = XInternAtom(x11_display, "_NET_WM_STATE_HIDDEN", True);
+ if (property == None || hidden == None) {
return false;
}
@@ -2112,7 +2137,7 @@ bool DisplayServerX11::_window_minimize_check(WindowID p_window) const {
int format;
unsigned long len;
unsigned long remaining;
- unsigned char *data = nullptr;
+ Atom *atoms = nullptr;
int result = XGetWindowProperty(
x11_display,
@@ -2121,20 +2146,21 @@ bool DisplayServerX11::_window_minimize_check(WindowID p_window) const {
0,
32,
False,
- AnyPropertyType,
+ XA_ATOM,
&type,
&format,
&len,
&remaining,
- &data);
+ (unsigned char **)&atoms);
- if (result == Success && data) {
- long *state = (long *)data;
- if (state[0] == WM_IconicState) {
- XFree(data);
- return true;
+ if (result == Success && atoms) {
+ for (unsigned int i = 0; i < len; i++) {
+ if (atoms[i] == hidden) {
+ XFree(atoms);
+ return true;
+ }
}
- XFree(data);
+ XFree(atoms);
}
return false;
@@ -3957,7 +3983,7 @@ bool DisplayServerX11::mouse_process_popups() {
// Find top popup to close.
while (E) {
// Popup window area.
- Rect2i win_rect = Rect2i(window_get_position(E->get()), window_get_size(E->get()));
+ Rect2i win_rect = Rect2i(window_get_position_with_decorations(E->get()), window_get_size_with_decorations(E->get()));
// Area of the parent window, which responsible for opening sub-menu.
Rect2i safe_rect = window_get_popup_safe_rect(E->get());
if (win_rect.has_point(pos)) {
@@ -4291,7 +4317,8 @@ void DisplayServerX11::process_events() {
break;
}
- if (!mouse_mode_grab) {
+ if (!mouse_mode_grab && window_mouseover_id == window_id) {
+ window_mouseover_id = INVALID_WINDOW_ID;
_send_window_event(windows[window_id], WINDOW_EVENT_MOUSE_EXIT);
}
@@ -4303,7 +4330,11 @@ void DisplayServerX11::process_events() {
break;
}
- if (!mouse_mode_grab) {
+ if (!mouse_mode_grab && window_mouseover_id != window_id) {
+ if (window_mouseover_id != INVALID_WINDOW_ID) {
+ _send_window_event(windows[window_mouseover_id], WINDOW_EVENT_MOUSE_EXIT);
+ }
+ window_mouseover_id = window_id;
_send_window_event(windows[window_id], WINDOW_EVENT_MOUSE_ENTER);
}
} break;
@@ -5461,13 +5492,11 @@ DisplayServerX11::DisplayServerX11(const String &p_rendering_driver, WindowMode
}
if (initialize_xinerama(dylibloader_verbose) != 0) {
- r_error = ERR_UNAVAILABLE;
- ERR_FAIL_MSG("Can't load Xinerama dynamically.");
+ xinerama_ext_ok = false;
}
if (initialize_xrandr(dylibloader_verbose) != 0) {
- r_error = ERR_UNAVAILABLE;
- ERR_FAIL_MSG("Can't load Xrandr dynamically.");
+ xrandr_ext_ok = false;
}
if (initialize_xrender(dylibloader_verbose) != 0) {
@@ -5537,42 +5566,36 @@ DisplayServerX11::DisplayServerX11(const String &p_rendering_driver, WindowMode
return;
}
- {
+ if (xshaped_ext_ok) {
int version_major = 0;
int version_minor = 0;
int rc = XShapeQueryVersion(x11_display, &version_major, &version_minor);
print_verbose(vformat("Xshape %d.%d detected.", version_major, version_minor));
if (rc != 1 || version_major < 1) {
- ERR_PRINT("Unsupported Xshape library version.");
- r_error = ERR_UNAVAILABLE;
- XCloseDisplay(x11_display);
- return;
+ xshaped_ext_ok = false;
+ print_verbose("Unsupported Xshape library version.");
}
}
- {
+ if (xinerama_ext_ok) {
int version_major = 0;
int version_minor = 0;
int rc = XineramaQueryVersion(x11_display, &version_major, &version_minor);
print_verbose(vformat("Xinerama %d.%d detected.", version_major, version_minor));
if (rc != 1 || version_major < 1) {
- ERR_PRINT("Unsupported Xinerama library version.");
- r_error = ERR_UNAVAILABLE;
- XCloseDisplay(x11_display);
- return;
+ xinerama_ext_ok = false;
+ print_verbose("Unsupported Xinerama library version.");
}
}
- {
+ if (xrandr_ext_ok) {
int version_major = 0;
int version_minor = 0;
int rc = XRRQueryVersion(x11_display, &version_major, &version_minor);
print_verbose(vformat("Xrandr %d.%d detected.", version_major, version_minor));
if (rc != 1 || (version_major == 1 && version_minor < 3) || (version_major < 1)) {
- ERR_PRINT("Unsupported Xrandr library version.");
- r_error = ERR_UNAVAILABLE;
- XCloseDisplay(x11_display);
- return;
+ xrandr_ext_ok = false;
+ print_verbose("Unsupported Xrandr library version.");
}
}
@@ -5638,7 +5661,9 @@ DisplayServerX11::DisplayServerX11(const String &p_rendering_driver, WindowMode
if (!xrandr_handle) {
fprintf(stderr, "could not load libXrandr.so.2, Error: %s\n", err);
}
- } else {
+ }
+
+ if (xrandr_handle) {
XRRQueryVersion(x11_display, &xrandr_major, &xrandr_minor);
if (((xrandr_major << 8) | xrandr_minor) >= 0x0105) {
xrr_get_monitors = (xrr_get_monitors_t)dlsym(xrandr_handle, "XRRGetMonitors");
diff --git a/platform/linuxbsd/x11/display_server_x11.h b/platform/linuxbsd/x11/display_server_x11.h
index 70703d42c3..71beddce76 100644
--- a/platform/linuxbsd/x11/display_server_x11.h
+++ b/platform/linuxbsd/x11/display_server_x11.h
@@ -225,6 +225,7 @@ class DisplayServerX11 : public DisplayServer {
List<WindowID> popup_list;
+ WindowID window_mouseover_id = INVALID_WINDOW_ID;
WindowID last_focused_window = INVALID_WINDOW_ID;
WindowID window_id_counter = MAIN_WINDOW_ID;
@@ -322,7 +323,9 @@ class DisplayServerX11 : public DisplayServer {
xrr_get_monitors_t xrr_get_monitors = nullptr;
xrr_free_monitors_t xrr_free_monitors = nullptr;
void *xrandr_handle = nullptr;
- Bool xrandr_ext_ok;
+ bool xrandr_ext_ok = true;
+ bool xinerama_ext_ok = true;
+ bool xshaped_ext_ok = true;
struct Property {
unsigned char *data;
@@ -390,6 +393,8 @@ public:
#if defined(DBUS_ENABLED)
virtual bool is_dark_mode_supported() const override;
virtual bool is_dark_mode() const override;
+
+ virtual Error file_dialog_show(const String &p_title, const String &p_current_directory, const String &p_filename, bool p_show_hidden, FileDialogMode p_mode, const Vector<String> &p_filters, const Callable &p_callback) override;
#endif
virtual void mouse_set_mode(MouseMode p_mode) override;
diff --git a/platform/linuxbsd/x11/gl_manager_x11.h b/platform/linuxbsd/x11/gl_manager_x11.h
index 59e20fec45..d3a25506a8 100644
--- a/platform/linuxbsd/x11/gl_manager_x11.h
+++ b/platform/linuxbsd/x11/gl_manager_x11.h
@@ -74,17 +74,17 @@ private:
};
struct GLDisplay {
- GLDisplay() { context = nullptr; }
+ GLDisplay() {}
~GLDisplay();
GLManager_X11_Private *context = nullptr;
- ::Display *x11_display;
- XVisualInfo x_vi;
+ ::Display *x11_display = nullptr;
+ XVisualInfo x_vi = {};
};
// just for convenience, window and display struct
struct XWinDisp {
::Window x11_window;
- ::Display *x11_display;
+ ::Display *x11_display = nullptr;
} _x_windisp;
LocalVector<GLWindow> _windows;
diff --git a/platform/macos/display_server_macos.h b/platform/macos/display_server_macos.h
index e5e0e53bfb..69f6008043 100644
--- a/platform/macos/display_server_macos.h
+++ b/platform/macos/display_server_macos.h
@@ -171,6 +171,7 @@ private:
int current_layout = 0;
bool keyboard_layout_dirty = true;
+ WindowID window_mouseover_id = INVALID_WINDOW_ID;
WindowID last_focused_window = INVALID_WINDOW_ID;
WindowID window_id_counter = MAIN_WINDOW_ID;
float display_max_scale = 1.f;
@@ -240,6 +241,8 @@ public:
bool get_is_resizing() const;
void reparent_check(WindowID p_window);
WindowID _get_focused_window_or_popup() const;
+ void mouse_enter_window(WindowID p_window);
+ void mouse_exit_window(WindowID p_window);
void window_update(WindowID p_window);
void window_destroy(WindowID p_window);
diff --git a/platform/macos/display_server_macos.mm b/platform/macos/display_server_macos.mm
index d64bb5211e..e79d6acc3f 100644
--- a/platform/macos/display_server_macos.mm
+++ b/platform/macos/display_server_macos.mm
@@ -366,6 +366,25 @@ DisplayServer::WindowID DisplayServerMacOS::_get_focused_window_or_popup() const
return last_focused_window;
}
+void DisplayServerMacOS::mouse_enter_window(WindowID p_window) {
+ if (window_mouseover_id != p_window) {
+ if (window_mouseover_id != INVALID_WINDOW_ID) {
+ send_window_event(windows[window_mouseover_id], WINDOW_EVENT_MOUSE_EXIT);
+ }
+ window_mouseover_id = p_window;
+ if (p_window != INVALID_WINDOW_ID) {
+ send_window_event(windows[p_window], WINDOW_EVENT_MOUSE_ENTER);
+ }
+ }
+}
+
+void DisplayServerMacOS::mouse_exit_window(WindowID p_window) {
+ if (window_mouseover_id == p_window && p_window != INVALID_WINDOW_ID) {
+ send_window_event(windows[p_window], WINDOW_EVENT_MOUSE_EXIT);
+ }
+ window_mouseover_id = INVALID_WINDOW_ID;
+}
+
void DisplayServerMacOS::_dispatch_input_events(const Ref<InputEvent> &p_event) {
((DisplayServerMacOS *)(get_singleton()))->_dispatch_input_event(p_event);
}
@@ -2069,9 +2088,7 @@ void DisplayServerMacOS::mouse_set_mode(MouseMode p_mode) {
if (show_cursor && !previously_shown) {
window_id = get_window_at_screen_position(mouse_get_position());
- if (window_id != INVALID_WINDOW_ID) {
- send_window_event(windows[window_id], WINDOW_EVENT_MOUSE_ENTER);
- }
+ mouse_enter_window(window_id);
}
if (p_mode == MOUSE_MODE_CAPTURED) {
@@ -4026,7 +4043,7 @@ bool DisplayServerMacOS::mouse_process_popups(bool p_close) {
// Find top popup to close.
while (E) {
// Popup window area.
- Rect2i win_rect = Rect2i(window_get_position(E->get()), window_get_size(E->get()));
+ Rect2i win_rect = Rect2i(window_get_position_with_decorations(E->get()), window_get_size_with_decorations(E->get()));
// Area of the parent window, which responsible for opening sub-menu.
Rect2i safe_rect = window_get_popup_safe_rect(E->get());
if (win_rect.has_point(pos)) {
diff --git a/platform/macos/doc_classes/EditorExportPlatformMacOS.xml b/platform/macos/doc_classes/EditorExportPlatformMacOS.xml
index 6af816989d..c24ff4cb73 100644
--- a/platform/macos/doc_classes/EditorExportPlatformMacOS.xml
+++ b/platform/macos/doc_classes/EditorExportPlatformMacOS.xml
@@ -23,7 +23,7 @@
Copyright notice for the bundle visible to the user (localized).
</member>
<member name="application/icon" type="String" setter="" getter="">
- Application icon file. If left empty, it will fallback to [member ProjectSettings.application/config/macos_native_icon], and then to [member ProjectSettings.application/config/icon].
+ Application icon file. If left empty, it will fallback to [member ProjectSettings.application/config/macos_native_icon], and then to [member ProjectSettings.application/config/icon].
</member>
<member name="application/icon_interpolation" type="int" setter="" getter="">
Interpolation method used to resize application icon.
@@ -32,13 +32,13 @@
Minimum version of macOS required for this application to run in the [code]major.minor.patch[/code] or [code]major.minor[/code] format, can only contain numeric characters ([code]0-9[/code]) and periods ([code].[/code]).
</member>
<member name="application/short_version" type="String" setter="" getter="">
- Application version visible to the user, can only contain numeric characters ([code]0-9[/code]) and periods ([code].[/code]).
+ Application version visible to the user, can only contain numeric characters ([code]0-9[/code]) and periods ([code].[/code]). Falls back to [member ProjectSettings.application/config/version] if left empty.
</member>
<member name="application/signature" type="String" setter="" getter="">
A four-character creator code that is specific to the bundle. Optional.
</member>
<member name="application/version" type="String" setter="" getter="">
- Machine-readable application version, in the [code]major.minor.patch[/code] format, can only contain numeric characters ([code]0-9[/code]) and periods ([code].[/code]).
+ Machine-readable application version, in the [code]major.minor.patch[/code] format, can only contain numeric characters ([code]0-9[/code]) and periods ([code].[/code]). This must be incremented on every new release pushed to the App Store.
</member>
<member name="binary_format/architecture" type="String" setter="" getter="">
Application executable architecture.
@@ -63,7 +63,7 @@
Array of the additional command line arguments passed to the code signing tool.
</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 [code]privacy/address_book_usage_description[/code] option. See [url=https://developer.apple.com/documentation/bundleresources/entitlements/com_apple_security_personal-information_addressbook]com.apple.security.personal-information.addressbook[/url].
+ Enable to allow access to contacts in the user's address book, if it's enabled you should also provide usage message in the [code]privacy/address_book_usage_description[/code] option. See [url=https://developer.apple.com/documentation/bundleresources/entitlements/com_apple_security_personal-information_addressbook]com.apple.security.personal-information.addressbook[/url].
</member>
<member name="codesign/entitlements/allow_dyld_environment_variables" type="bool" setter="" getter="">
Allows app to use dynamic linker environment variables to inject code. If you are using add-ons with dynamic or self-modifying native code, enable them according to the add-on documentation. See [url=https://developer.apple.com/documentation/bundleresources/entitlements/com_apple_security_cs_allow-dyld-environment-variables]com.apple.security.cs.allow-dyld-environment-variables[/url].
diff --git a/platform/macos/export/export_plugin.cpp b/platform/macos/export/export_plugin.cpp
index 1afcedc576..6586fe7f82 100644
--- a/platform/macos/export/export_plugin.cpp
+++ b/platform/macos/export/export_plugin.cpp
@@ -41,6 +41,7 @@
#include "editor/editor_node.h"
#include "editor/editor_paths.h"
#include "editor/editor_scale.h"
+#include "editor/import/resource_importer_texture_settings.h"
#include "scene/resources/image_texture.h"
#include "modules/modules_enabled.gen.h" // For svg and regex.
@@ -378,7 +379,7 @@ void EditorExportPlatformMacOS::get_export_options(List<ExportOption> *r_options
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/signature"), ""));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/app_category", PROPERTY_HINT_ENUM, "Business,Developer-tools,Education,Entertainment,Finance,Games,Action-games,Adventure-games,Arcade-games,Board-games,Card-games,Casino-games,Dice-games,Educational-games,Family-games,Kids-games,Music-games,Puzzle-games,Racing-games,Role-playing-games,Simulation-games,Sports-games,Strategy-games,Trivia-games,Word-games,Graphics-design,Healthcare-fitness,Lifestyle,Medical,Music,News,Photography,Productivity,Reference,Social-networking,Sports,Travel,Utilities,Video,Weather"), "Games"));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/short_version"), "1.0"));
- r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/version"), "1.0"));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/version", PROPERTY_HINT_PLACEHOLDER_TEXT, "Leave empty to use project version"), ""));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/copyright"), ""));
r_options->push_back(ExportOption(PropertyInfo(Variant::DICTIONARY, "application/copyright_localized", PROPERTY_HINT_LOCALIZABLE_STRING), Dictionary()));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/min_macos_version"), "10.12"));
@@ -672,7 +673,7 @@ void EditorExportPlatformMacOS::_fix_plist(const Ref<EditorExportPreset> &p_pres
} else if (lines[i].find("$short_version") != -1) {
strnew += lines[i].replace("$short_version", p_preset->get("application/short_version")) + "\n";
} else if (lines[i].find("$version") != -1) {
- strnew += lines[i].replace("$version", p_preset->get("application/version")) + "\n";
+ strnew += lines[i].replace("$version", p_preset->get_version("application/version")) + "\n";
} else if (lines[i].find("$signature") != -1) {
strnew += lines[i].replace("$signature", p_preset->get("application/signature")) + "\n";
} else if (lines[i].find("$app_category") != -1) {
@@ -2124,16 +2125,12 @@ bool EditorExportPlatformMacOS::has_valid_export_configuration(const Ref<EditorE
// Check the texture formats, which vary depending on the target architecture.
String architecture = p_preset->get("binary_format/architecture");
if (architecture == "universal" || architecture == "x86_64") {
- const String bc_error = test_bc();
- if (!bc_error.is_empty()) {
+ if (!ResourceImporterTextureSettings::should_import_s3tc_bptc()) {
valid = false;
- err += bc_error;
}
} else if (architecture == "arm64") {
- const String etc_error = test_etc2();
- if (!etc_error.is_empty()) {
+ if (!ResourceImporterTextureSettings::should_import_etc2_astc()) {
valid = false;
- err += etc_error;
}
} else {
ERR_PRINT("Invalid architecture");
diff --git a/platform/macos/godot_content_view.mm b/platform/macos/godot_content_view.mm
index 3467bf90a1..231be83a03 100644
--- a/platform/macos/godot_content_view.mm
+++ b/platform/macos/godot_content_view.mm
@@ -505,9 +505,8 @@
return;
}
- DisplayServerMacOS::WindowData &wd = ds->get_window(window_id);
if (ds->mouse_get_mode() != DisplayServer::MOUSE_MODE_CAPTURED) {
- ds->send_window_event(wd, DisplayServerMacOS::WINDOW_EVENT_MOUSE_EXIT);
+ ds->mouse_exit_window(window_id);
}
}
@@ -517,9 +516,8 @@
return;
}
- DisplayServerMacOS::WindowData &wd = ds->get_window(window_id);
if (ds->mouse_get_mode() != DisplayServer::MOUSE_MODE_CAPTURED) {
- ds->send_window_event(wd, DisplayServerMacOS::WINDOW_EVENT_MOUSE_ENTER);
+ ds->mouse_enter_window(window_id);
}
ds->cursor_update_shape();
diff --git a/platform/macos/godot_window_delegate.mm b/platform/macos/godot_window_delegate.mm
index 1c6dbb1981..46355b4ae8 100644
--- a/platform/macos/godot_window_delegate.mm
+++ b/platform/macos/godot_window_delegate.mm
@@ -67,6 +67,7 @@
ds->window_set_transient(window_id, DisplayServerMacOS::INVALID_WINDOW_ID);
}
+ ds->mouse_exit_window(window_id);
ds->window_destroy(window_id);
}
diff --git a/platform/web/SCsub b/platform/web/SCsub
index e44e59bfb9..1af0642554 100644
--- a/platform/web/SCsub
+++ b/platform/web/SCsub
@@ -70,6 +70,9 @@ if env["dlink_enabled"]:
sys_env.Append(LINKFLAGS=["-s", "WARN_ON_UNDEFINED_SYMBOLS=0"])
# Force exporting the standard library (printf, malloc, etc.)
sys_env["ENV"]["EMCC_FORCE_STDLIBS"] = "libc,libc++,libc++abi"
+ sys_env["CCFLAGS"].remove("-fvisibility=hidden")
+ sys_env["LINKFLAGS"].remove("-fvisibility=hidden")
+
# The main emscripten runtime, with exported standard libraries.
sys = sys_env.Program(build_targets, ["web_runtime.cpp"])
diff --git a/platform/web/detect.py b/platform/web/detect.py
index 2685cbcd63..4015c8ff16 100644
--- a/platform/web/detect.py
+++ b/platform/web/detect.py
@@ -97,12 +97,9 @@ def configure(env: "Environment"):
if env["use_assertions"]:
env.Append(LINKFLAGS=["-s", "ASSERTIONS=1"])
- if env.editor_build:
- if env["initial_memory"] < 64:
- print('Note: Forcing "initial_memory=64" as it is required for the web editor.')
- env["initial_memory"] = 64
- else:
- env.Append(CPPFLAGS=["-fno-exceptions"])
+ if env.editor_build and env["initial_memory"] < 64:
+ print('Note: Forcing "initial_memory=64" as it is required for the web editor.')
+ env["initial_memory"] = 64
env.Append(LINKFLAGS=["-s", "INITIAL_MEMORY=%sMB" % env["initial_memory"]])
@@ -211,6 +208,8 @@ def configure(env: "Environment"):
env.Append(CCFLAGS=["-s", "SIDE_MODULE=2"])
env.Append(LINKFLAGS=["-s", "SIDE_MODULE=2"])
+ env.Append(CCFLAGS=["-fvisibility=hidden"])
+ env.Append(LINKFLAGS=["-fvisibility=hidden"])
env.extra_suffix = ".dlink" + env.extra_suffix
# Reduce code size by generating less support code (e.g. skip NodeJS support).
diff --git a/platform/web/export/export_plugin.cpp b/platform/web/export/export_plugin.cpp
index 38e2714d9f..993abd2cee 100644
--- a/platform/web/export/export_plugin.cpp
+++ b/platform/web/export/export_plugin.cpp
@@ -37,6 +37,7 @@
#include "editor/editor_scale.h"
#include "editor/editor_settings.h"
#include "editor/export/editor_export.h"
+#include "editor/import/resource_importer_texture_settings.h"
#include "scene/resources/image_texture.h"
#include "modules/modules_enabled.gen.h" // For mono and svg.
@@ -406,10 +407,8 @@ bool EditorExportPlatformWeb::has_valid_project_configuration(const Ref<EditorEx
// Validate the project configuration.
if (p_preset->get("vram_texture_compression/for_mobile")) {
- String etc_error = test_etc2();
- if (!etc_error.is_empty()) {
+ if (!ResourceImporterTextureSettings::should_import_etc2_astc()) {
valid = false;
- err += etc_error;
}
}
diff --git a/platform/web/js/libs/library_godot_audio.js b/platform/web/js/libs/library_godot_audio.js
index 1993d66310..cc86c81096 100644
--- a/platform/web/js/libs/library_godot_audio.js
+++ b/platform/web/js/libs/library_godot_audio.js
@@ -88,7 +88,7 @@ const GodotAudio = {
GodotAudio.input = GodotAudio.ctx.createMediaStreamSource(stream);
callback(GodotAudio.input);
} catch (e) {
- GodotRuntime.error('Failed creaating input.', e);
+ GodotRuntime.error('Failed creating input.', e);
}
}
if (navigator.mediaDevices && navigator.mediaDevices.getUserMedia) {
diff --git a/platform/web/js/libs/library_godot_display.js b/platform/web/js/libs/library_godot_display.js
index 746f858923..c60e6899f2 100644
--- a/platform/web/js/libs/library_godot_display.js
+++ b/platform/web/js/libs/library_godot_display.js
@@ -289,11 +289,11 @@ const GodotDisplayScreen = {
const isFullscreen = GodotDisplayScreen.isFullscreen();
const wantsFullWindow = GodotConfig.canvas_resize_policy === 2;
const noResize = GodotConfig.canvas_resize_policy === 0;
- const wwidth = GodotDisplayScreen.desired_size[0];
- const wheight = GodotDisplayScreen.desired_size[1];
+ const dWidth = GodotDisplayScreen.desired_size[0];
+ const dHeight = GodotDisplayScreen.desired_size[1];
const canvas = GodotConfig.canvas;
- let width = wwidth;
- let height = wheight;
+ let width = dWidth;
+ let height = dHeight;
if (noResize) {
// Don't resize canvas, just update GL if needed.
if (canvas.width !== width || canvas.height !== height) {
diff --git a/platform/windows/detect.py b/platform/windows/detect.py
index bec1fd2cb6..9548939695 100644
--- a/platform/windows/detect.py
+++ b/platform/windows/detect.py
@@ -355,6 +355,9 @@ def configure_msvc(env, vcvars_msvc_config):
else:
env.AppendUnique(CCFLAGS=["/MD"])
+ # MSVC incremental linking is broken and _increases_ link time (GH-77968).
+ env.Append(LINKFLAGS=["/INCREMENTAL:NO"])
+
if env["arch"] == "x86_32":
env["x86_libtheora_opt_vc"] = True
diff --git a/platform/windows/display_server_windows.cpp b/platform/windows/display_server_windows.cpp
index b1dccdcefe..5863a75324 100644
--- a/platform/windows/display_server_windows.cpp
+++ b/platform/windows/display_server_windows.cpp
@@ -278,7 +278,12 @@ Error DisplayServerWindows::file_dialog_show(const String &p_title, const String
pfd->SetFileTypes(filters.size(), filters.ptr());
pfd->SetFileTypeIndex(0);
- hr = pfd->Show(nullptr);
+ WindowID window_id = _get_focused_window_or_popup();
+ if (!windows.has(window_id)) {
+ window_id = MAIN_WINDOW_ID;
+ }
+
+ hr = pfd->Show(windows[window_id].hWnd);
if (SUCCEEDED(hr)) {
Vector<String> file_names;
@@ -2735,7 +2740,7 @@ LRESULT DisplayServerWindows::MouseProc(int code, WPARAM wParam, LPARAM lParam)
// Find top popup to close.
while (E) {
// Popup window area.
- Rect2i win_rect = Rect2i(window_get_position(E->get()), window_get_size(E->get()));
+ Rect2i win_rect = Rect2i(window_get_position_with_decorations(E->get()), window_get_size_with_decorations(E->get()));
// Area of the parent window, which responsible for opening sub-menu.
Rect2i safe_rect = window_get_popup_safe_rect(E->get());
if (win_rect.has_point(pos)) {
diff --git a/platform/windows/doc_classes/EditorExportPlatformWindows.xml b/platform/windows/doc_classes/EditorExportPlatformWindows.xml
index 80928d7418..fc068efc75 100644
--- a/platform/windows/doc_classes/EditorExportPlatformWindows.xml
+++ b/platform/windows/doc_classes/EditorExportPlatformWindows.xml
@@ -22,10 +22,10 @@
File description to be presented to users. Required. See [url=https://learn.microsoft.com/en-us/windows/win32/menurc/stringfileinfo-block]StringFileInfo[/url].
</member>
<member name="application/file_version" type="String" setter="" getter="">
- Version number of the file. Required. See [url=https://learn.microsoft.com/en-us/windows/win32/menurc/stringfileinfo-block]StringFileInfo[/url].
+ Version number of the file. Falls back to [member ProjectSettings.application/config/version] if left empty. See [url=https://learn.microsoft.com/en-us/windows/win32/menurc/stringfileinfo-block]StringFileInfo[/url].
</member>
<member name="application/icon" type="String" setter="" getter="">
- Application icon file. If left empty, it will fallback to [member ProjectSettings.application/config/windows_native_icon], and then to [member ProjectSettings.application/config/icon].
+ Application icon file. If left empty, it will fallback to [member ProjectSettings.application/config/windows_native_icon], and then to [member ProjectSettings.application/config/icon].
</member>
<member name="application/icon_interpolation" type="int" setter="" getter="">
Interpolation method used to resize application icon.
@@ -37,7 +37,7 @@
Name of the application. Required. See [url=https://learn.microsoft.com/en-us/windows/win32/menurc/stringfileinfo-block]StringFileInfo[/url].
</member>
<member name="application/product_version" type="String" setter="" getter="">
- Application version visible to the user. Required. See [url=https://learn.microsoft.com/en-us/windows/win32/menurc/stringfileinfo-block]StringFileInfo[/url].
+ Application version visible to the user. Falls back to [member ProjectSettings.application/config/version] if left empty. See [url=https://learn.microsoft.com/en-us/windows/win32/menurc/stringfileinfo-block]StringFileInfo[/url].
</member>
<member name="application/trademarks" type="String" setter="" getter="">
Trademarks and registered trademarks that apply to the file. Optional. See [url=https://learn.microsoft.com/en-us/windows/win32/menurc/stringfileinfo-block]StringFileInfo[/url].
diff --git a/platform/windows/export/export_plugin.cpp b/platform/windows/export/export_plugin.cpp
index 0ef07c3275..c4be1821bd 100644
--- a/platform/windows/export/export_plugin.cpp
+++ b/platform/windows/export/export_plugin.cpp
@@ -168,10 +168,10 @@ Error EditorExportPlatformWindows::sign_shared_object(const Ref<EditorExportPres
Error EditorExportPlatformWindows::modify_template(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags) {
if (p_preset->get("application/modify_resources")) {
- _rcedit_add_data(p_preset, p_path, true);
+ _rcedit_add_data(p_preset, p_path, false);
String wrapper_path = p_path.get_basename() + ".console.exe";
if (FileAccess::exists(wrapper_path)) {
- _rcedit_add_data(p_preset, wrapper_path, false);
+ _rcedit_add_data(p_preset, wrapper_path, true);
}
}
return OK;
@@ -342,8 +342,8 @@ void EditorExportPlatformWindows::get_export_options(List<ExportOption> *r_optio
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/icon", PROPERTY_HINT_FILE, "*.ico,*.png,*.webp,*.svg"), "", false, true));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/console_wrapper_icon", PROPERTY_HINT_FILE, "*.ico,*.png,*.webp,*.svg"), ""));
r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "application/icon_interpolation", PROPERTY_HINT_ENUM, "Nearest neighbor,Bilinear,Cubic,Trilinear,Lanczos"), 4));
- r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/file_version", PROPERTY_HINT_PLACEHOLDER_TEXT, "1.0.0.0"), "", false, true));
- r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/product_version", PROPERTY_HINT_PLACEHOLDER_TEXT, "1.0.0.0"), "", false, true));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/file_version", PROPERTY_HINT_PLACEHOLDER_TEXT, "Leave empty to use project version"), ""));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/product_version", PROPERTY_HINT_PLACEHOLDER_TEXT, "Leave empty to use project version"), ""));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/company_name", PROPERTY_HINT_PLACEHOLDER_TEXT, "Company Name"), ""));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/product_name", PROPERTY_HINT_PLACEHOLDER_TEXT, "Game Name"), ""));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/file_description"), ""));
@@ -425,8 +425,8 @@ Error EditorExportPlatformWindows::_rcedit_add_data(const Ref<EditorExportPreset
}
}
- String file_verion = p_preset->get("application/file_version");
- String product_version = p_preset->get("application/product_version");
+ String file_verion = p_preset->get_version("application/file_version", true);
+ String product_version = p_preset->get_version("application/product_version", true);
String company_name = p_preset->get("application/company_name");
String product_name = p_preset->get("application/product_name");
String file_description = p_preset->get("application/file_description");
diff --git a/platform/windows/joypad_windows.cpp b/platform/windows/joypad_windows.cpp
index 487cb56ba0..0ac6c2c8b0 100644
--- a/platform/windows/joypad_windows.cpp
+++ b/platform/windows/joypad_windows.cpp
@@ -318,7 +318,9 @@ void JoypadWindows::probe_joypads() {
x_joypads[i].ff_end_timestamp = 0;
x_joypads[i].vibrating = false;
attached_joypads[id] = true;
- input->joy_connection_changed(id, true, "XInput Gamepad", "__XINPUT_DEVICE__");
+ Dictionary joypad_info;
+ joypad_info["xinput_index"] = (int)i;
+ input->joy_connection_changed(id, true, "XInput Gamepad", "__XINPUT_DEVICE__", joypad_info);
}
} else if (x_joypads[i].attached) {
x_joypads[i].attached = false;
diff --git a/platform/windows/os_windows.cpp b/platform/windows/os_windows.cpp
index df93631ef0..7c94e38e14 100644
--- a/platform/windows/os_windows.cpp
+++ b/platform/windows/os_windows.cpp
@@ -1714,7 +1714,7 @@ String OS_Windows::get_system_ca_certificates() {
OS_Windows::OS_Windows(HINSTANCE _hInstance) {
hInstance = _hInstance;
- CoInitializeEx(nullptr, COINIT_MULTITHREADED);
+ CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED);
#ifdef WASAPI_ENABLED
AudioDriverManager::add_driver(&driver_wasapi);