summaryrefslogtreecommitdiffstats
path: root/platform
diff options
context:
space:
mode:
Diffstat (limited to 'platform')
-rw-r--r--platform/android/doc_classes/EditorExportPlatformAndroid.xml12
-rw-r--r--platform/android/export/export_plugin.cpp132
-rw-r--r--platform/android/export/export_plugin.h11
-rw-r--r--platform/android/export/gradle_export_util.cpp8
-rw-r--r--platform/android/export/gradle_export_util.h3
-rw-r--r--platform/linuxbsd/doc_classes/EditorExportPlatformLinuxBSD.xml14
-rw-r--r--platform/linuxbsd/os_linuxbsd.cpp8
-rw-r--r--platform/linuxbsd/os_linuxbsd.h2
-rw-r--r--platform/linuxbsd/wayland/display_server_wayland.cpp31
-rw-r--r--platform/linuxbsd/wayland/display_server_wayland.h2
-rw-r--r--platform/linuxbsd/wayland/wayland_thread.cpp116
-rw-r--r--platform/linuxbsd/wayland/wayland_thread.h4
-rw-r--r--platform/windows/doc_classes/EditorExportPlatformWindows.xml14
13 files changed, 257 insertions, 100 deletions
diff --git a/platform/android/doc_classes/EditorExportPlatformAndroid.xml b/platform/android/doc_classes/EditorExportPlatformAndroid.xml
index a6f92158f9..976d64fd49 100644
--- a/platform/android/doc_classes/EditorExportPlatformAndroid.xml
+++ b/platform/android/doc_classes/EditorExportPlatformAndroid.xml
@@ -37,14 +37,22 @@
A list of additional command line arguments, exported project will receive when started.
</member>
<member name="custom_template/debug" type="String" setter="" getter="">
- Path to the custom export template. If left empty, default template is used.
+ Path to an APK file to use as a custom export template for debug exports. If left empty, default template is used.
+ [b]Note:[/b] This is only used if [member EditorExportPlatformAndroid.gradle_build/use_gradle_build] is disabled.
</member>
<member name="custom_template/release" type="String" setter="" getter="">
- Path to the custom export template. If left empty, default template is used.
+ Path to an APK file to use as a custom export template for release exports. If left empty, default template is used.
+ [b]Note:[/b] This is only used if [member EditorExportPlatformAndroid.gradle_build/use_gradle_build] is disabled.
+ </member>
+ <member name="gradle_build/android_source_template" type="String" setter="" getter="">
+ Path to a ZIP file holding the source for the export template used in a Gradle build. If left empty, the default template is used.
</member>
<member name="gradle_build/export_format" type="int" setter="" getter="">
Export format for Gradle build.
</member>
+ <member name="gradle_build/gradle_build_directory" type="String" setter="" getter="">
+ Path to the Gradle build directory. If left empty, then [code]res://android[/code] will be used.
+ </member>
<member name="gradle_build/min_sdk" type="String" setter="" getter="">
Minimal Android SDK version for Gradle build.
</member>
diff --git a/platform/android/export/export_plugin.cpp b/platform/android/export/export_plugin.cpp
index 459f5a5983..bd062401a4 100644
--- a/platform/android/export/export_plugin.cpp
+++ b/platform/android/export/export_plugin.cpp
@@ -46,6 +46,7 @@
#include "editor/editor_node.h"
#include "editor/editor_paths.h"
#include "editor/editor_settings.h"
+#include "editor/export/export_template_manager.h"
#include "editor/import/resource_importer_texture_settings.h"
#include "editor/themes/editor_scale.h"
#include "main/splash.gen.h"
@@ -208,12 +209,14 @@ static const char *android_perms[] = {
nullptr
};
+static const char *MISMATCHED_VERSIONS_MESSAGE = "Android build version mismatch:\n| Template installed: %s\n| Requested version: %s\nPlease reinstall Android build template from 'Project' menu.";
+
static const char *SPLASH_IMAGE_EXPORT_PATH = "res/drawable-nodpi/splash.png";
static const char *LEGACY_BUILD_SPLASH_IMAGE_EXPORT_PATH = "res/drawable-nodpi-v4/splash.png";
static const char *SPLASH_BG_COLOR_PATH = "res/drawable-nodpi/splash_bg_color.png";
static const char *LEGACY_BUILD_SPLASH_BG_COLOR_PATH = "res/drawable-nodpi-v4/splash_bg_color.png";
-static const char *SPLASH_CONFIG_PATH = "res://android/build/res/drawable/splash_drawable.xml";
-static const char *GDEXTENSION_LIBS_PATH = "res://android/build/libs/gdextensionlibs.json";
+static const char *SPLASH_CONFIG_PATH = "res/drawable/splash_drawable.xml";
+static const char *GDEXTENSION_LIBS_PATH = "libs/gdextensionlibs.json";
static const int icon_densities_count = 6;
static const char *launcher_icon_option = PNAME("launcher_icons/main_192x192");
@@ -250,8 +253,8 @@ static const LauncherIcon launcher_adaptive_icon_backgrounds[icon_densities_coun
static const int EXPORT_FORMAT_APK = 0;
static const int EXPORT_FORMAT_AAB = 1;
-static const char *APK_ASSETS_DIRECTORY = "res://android/build/assets";
-static const char *AAB_ASSETS_DIRECTORY = "res://android/build/assetPacks/installTime/src/main/assets";
+static const char *APK_ASSETS_DIRECTORY = "assets";
+static const char *AAB_ASSETS_DIRECTORY = "assetPacks/installTime/src/main/assets";
static const int OPENGL_MIN_SDK_VERSION = 21; // Should match the value in 'platform/android/java/app/config.gradle#minSdk'
static const int VULKAN_MIN_SDK_VERSION = 24;
@@ -474,7 +477,8 @@ String EditorExportPlatformAndroid::get_valid_basename() const {
}
String EditorExportPlatformAndroid::get_assets_directory(const Ref<EditorExportPreset> &p_preset, int p_export_format) const {
- return p_export_format == EXPORT_FORMAT_AAB ? AAB_ASSETS_DIRECTORY : APK_ASSETS_DIRECTORY;
+ String gradle_build_directory = ExportTemplateManager::get_android_build_directory(p_preset);
+ return gradle_build_directory.path_join(p_export_format == EXPORT_FORMAT_AAB ? AAB_ASSETS_DIRECTORY : APK_ASSETS_DIRECTORY);
}
bool EditorExportPlatformAndroid::is_package_name_valid(const String &p_package, String *r_error) const {
@@ -774,11 +778,10 @@ Error EditorExportPlatformAndroid::copy_gradle_so(void *p_userdata, const Shared
}
if (abi_index != -1) {
exported = true;
- String base = "res://android/build/libs";
String type = export_data->debug ? "debug" : "release";
String abi = abis[abi_index].abi;
String filename = p_so.path.get_file();
- String dst_path = base.path_join(type).path_join(abi).path_join(filename);
+ String dst_path = export_data->libs_directory.path_join(type).path_join(abi).path_join(filename);
Vector<uint8_t> data = FileAccess::get_file_as_bytes(p_so.path);
print_verbose("Copying .so file from " + p_so.path + " to " + dst_path);
Error err = store_file_at_path(dst_path, data);
@@ -867,7 +870,7 @@ void EditorExportPlatformAndroid::_write_tmp_manifest(const Ref<EditorExportPres
manifest_text += _get_application_tag(Ref<EditorExportPlatform>(this), p_preset, _has_read_write_storage_permission(perms), p_debug);
manifest_text += "</manifest>\n";
- String manifest_path = vformat("res://android/build/src/%s/AndroidManifest.xml", (p_debug ? "debug" : "release"));
+ String manifest_path = ExportTemplateManager::get_android_build_directory(p_preset).path_join(vformat("src/%s/AndroidManifest.xml", (p_debug ? "debug" : "release")));
print_verbose("Storing manifest into " + manifest_path + ": " + "\n" + manifest_text);
store_string_at_path(manifest_path, manifest_text);
@@ -1628,15 +1631,6 @@ void EditorExportPlatformAndroid::load_icon_refs(const Ref<EditorExportPreset> &
}
}
-void EditorExportPlatformAndroid::store_image(const LauncherIcon launcher_icon, const Vector<uint8_t> &data) {
- store_image(launcher_icon.export_path, data);
-}
-
-void EditorExportPlatformAndroid::store_image(const String &export_path, const Vector<uint8_t> &data) {
- String img_path = export_path.insert(0, "res://android/build/");
- store_file_at_path(img_path, data);
-}
-
void EditorExportPlatformAndroid::_copy_icons_to_gradle_project(const Ref<EditorExportPreset> &p_preset,
const String &processed_splash_config_xml,
const Ref<Image> &splash_image,
@@ -1644,26 +1638,30 @@ void EditorExportPlatformAndroid::_copy_icons_to_gradle_project(const Ref<Editor
const Ref<Image> &main_image,
const Ref<Image> &foreground,
const Ref<Image> &background) {
+ String gradle_build_dir = ExportTemplateManager::get_android_build_directory(p_preset);
+
// Store the splash configuration
if (!processed_splash_config_xml.is_empty()) {
print_verbose("Storing processed splash configuration: " + String("\n") + processed_splash_config_xml);
- store_string_at_path(SPLASH_CONFIG_PATH, processed_splash_config_xml);
+ store_string_at_path(gradle_build_dir.path_join(SPLASH_CONFIG_PATH), processed_splash_config_xml);
}
// Store the splash image
if (splash_image.is_valid() && !splash_image->is_empty()) {
- print_verbose("Storing splash image in " + String(SPLASH_IMAGE_EXPORT_PATH));
+ String splash_export_path = gradle_build_dir.path_join(SPLASH_IMAGE_EXPORT_PATH);
+ print_verbose("Storing splash image in " + splash_export_path);
Vector<uint8_t> data;
_load_image_data(splash_image, data);
- store_image(SPLASH_IMAGE_EXPORT_PATH, data);
+ store_file_at_path(splash_export_path, data);
}
// Store the splash bg color image
if (splash_bg_color_image.is_valid() && !splash_bg_color_image->is_empty()) {
- print_verbose("Storing splash background image in " + String(SPLASH_BG_COLOR_PATH));
+ String splash_bg_color_path = gradle_build_dir.path_join(SPLASH_BG_COLOR_PATH);
+ print_verbose("Storing splash background image in " + splash_bg_color_path);
Vector<uint8_t> data;
_load_image_data(splash_bg_color_image, data);
- store_image(SPLASH_BG_COLOR_PATH, data);
+ store_file_at_path(splash_bg_color_path, data);
}
// Prepare images to be resized for the icons. If some image ends up being uninitialized,
@@ -1674,7 +1672,7 @@ void EditorExportPlatformAndroid::_copy_icons_to_gradle_project(const Ref<Editor
print_verbose("Processing launcher icon for dimension " + itos(launcher_icons[i].dimensions) + " into " + launcher_icons[i].export_path);
Vector<uint8_t> data;
_process_launcher_icons(launcher_icons[i].export_path, main_image, launcher_icons[i].dimensions, data);
- store_image(launcher_icons[i], data);
+ store_file_at_path(gradle_build_dir.path_join(launcher_icons[i].export_path), data);
}
if (foreground.is_valid() && !foreground->is_empty()) {
@@ -1682,7 +1680,7 @@ void EditorExportPlatformAndroid::_copy_icons_to_gradle_project(const Ref<Editor
Vector<uint8_t> data;
_process_launcher_icons(launcher_adaptive_icon_foregrounds[i].export_path, foreground,
launcher_adaptive_icon_foregrounds[i].dimensions, data);
- store_image(launcher_adaptive_icon_foregrounds[i], data);
+ store_file_at_path(gradle_build_dir.path_join(launcher_adaptive_icon_foregrounds[i].export_path), data);
}
if (background.is_valid() && !background->is_empty()) {
@@ -1690,7 +1688,7 @@ void EditorExportPlatformAndroid::_copy_icons_to_gradle_project(const Ref<Editor
Vector<uint8_t> data;
_process_launcher_icons(launcher_adaptive_icon_backgrounds[i].export_path, background,
launcher_adaptive_icon_backgrounds[i].dimensions, data);
- store_image(launcher_adaptive_icon_backgrounds[i], data);
+ store_file_at_path(gradle_build_dir.path_join(launcher_adaptive_icon_backgrounds[i].export_path), data);
}
}
}
@@ -1798,7 +1796,9 @@ void EditorExportPlatformAndroid::get_export_options(List<ExportOption> *r_optio
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "custom_template/debug", PROPERTY_HINT_GLOBAL_FILE, "*.apk"), ""));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "custom_template/release", PROPERTY_HINT_GLOBAL_FILE, "*.apk"), ""));
- r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "gradle_build/use_gradle_build"), false, false, true));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "gradle_build/use_gradle_build"), false, true, true));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "gradle_build/gradle_build_directory", PROPERTY_HINT_PLACEHOLDER_TEXT, "res://android"), "", false, false));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "gradle_build/android_source_template", PROPERTY_HINT_GLOBAL_FILE, "*.zip"), ""));
r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "gradle_build/export_format", PROPERTY_HINT_ENUM, "Export APK,Export AAB"), EXPORT_FORMAT_APK, false, true));
// Using String instead of int to default to an empty string (no override) with placeholder for instructions (see GH-62465).
// This implies doing validation that the string is a proper int.
@@ -1876,6 +1876,18 @@ void EditorExportPlatformAndroid::get_export_options(List<ExportOption> *r_optio
}
}
+bool EditorExportPlatformAndroid::get_export_option_visibility(const EditorExportPreset *p_preset, const String &p_option) const {
+ if (p_option == "gradle_build/gradle_build_directory" || p_option == "gradle_build/android_source_template") {
+ // @todo These are experimental options - keep them hidden for now.
+ //return (bool)p_preset->get("gradle_build/use_gradle_build");
+ return false;
+ } else if (p_option == "custom_template/debug" || p_option == "custom_template/release") {
+ // The APK templates are ignored if Gradle build is enabled.
+ return !p_preset->get("gradle_build/use_gradle_build");
+ }
+ return true;
+}
+
String EditorExportPlatformAndroid::get_name() const {
return "Android";
}
@@ -2345,7 +2357,7 @@ bool EditorExportPlatformAndroid::has_valid_export_configuration(const Ref<Edito
err += template_err;
}
} else {
- bool installed_android_build_template = FileAccess::exists("res://android/build/build.gradle");
+ bool installed_android_build_template = FileAccess::exists(ExportTemplateManager::get_android_build_directory(p_preset).path_join("build.gradle"));
if (!installed_android_build_template) {
r_missing_templates = !exists_export_template("android_source.zip", &err);
err += TTR("Android build template not installed in the project. Install it from the Project menu.") + "\n";
@@ -2492,6 +2504,19 @@ bool EditorExportPlatformAndroid::has_valid_project_configuration(const Ref<Edit
valid = false;
}
+ if (p_preset->get("gradle_build/use_gradle_build")) {
+ String build_version_path = ExportTemplateManager::get_android_build_directory(p_preset).get_base_dir().path_join(".build_version");
+ Ref<FileAccess> f = FileAccess::open(build_version_path, FileAccess::READ);
+ if (f.is_valid()) {
+ String current_version = ExportTemplateManager::get_android_template_identifier(p_preset);
+ String installed_version = f->get_line().strip_edges();
+ if (current_version != installed_version) {
+ err += vformat(TTR(MISMATCHED_VERSIONS_MESSAGE), installed_version, current_version);
+ err += "\n";
+ }
+ }
+ }
+
String min_sdk_str = p_preset->get("gradle_build/min_sdk");
int min_sdk_int = VULKAN_MIN_SDK_VERSION;
if (!min_sdk_str.is_empty()) { // Empty means no override, nothing to do.
@@ -2734,34 +2759,37 @@ Error EditorExportPlatformAndroid::sign_apk(const Ref<EditorExportPreset> &p_pre
return OK;
}
-void EditorExportPlatformAndroid::_clear_assets_directory() {
+void EditorExportPlatformAndroid::_clear_assets_directory(const Ref<EditorExportPreset> &p_preset) {
Ref<DirAccess> da_res = DirAccess::create(DirAccess::ACCESS_RESOURCES);
+ String gradle_build_directory = ExportTemplateManager::get_android_build_directory(p_preset);
// Clear the APK assets directory
- if (da_res->dir_exists(APK_ASSETS_DIRECTORY)) {
+ String apk_assets_directory = gradle_build_directory.path_join(APK_ASSETS_DIRECTORY);
+ if (da_res->dir_exists(apk_assets_directory)) {
print_verbose("Clearing APK assets directory...");
- Ref<DirAccess> da_assets = DirAccess::open(APK_ASSETS_DIRECTORY);
+ Ref<DirAccess> da_assets = DirAccess::open(apk_assets_directory);
ERR_FAIL_COND(da_assets.is_null());
da_assets->erase_contents_recursive();
- da_res->remove(APK_ASSETS_DIRECTORY);
+ da_res->remove(apk_assets_directory);
}
// Clear the AAB assets directory
- if (da_res->dir_exists(AAB_ASSETS_DIRECTORY)) {
+ String aab_assets_directory = gradle_build_directory.path_join(AAB_ASSETS_DIRECTORY);
+ if (da_res->dir_exists(aab_assets_directory)) {
print_verbose("Clearing AAB assets directory...");
- Ref<DirAccess> da_assets = DirAccess::open(AAB_ASSETS_DIRECTORY);
+ Ref<DirAccess> da_assets = DirAccess::open(aab_assets_directory);
ERR_FAIL_COND(da_assets.is_null());
da_assets->erase_contents_recursive();
- da_res->remove(AAB_ASSETS_DIRECTORY);
+ da_res->remove(aab_assets_directory);
}
}
-void EditorExportPlatformAndroid::_remove_copied_libs() {
+void EditorExportPlatformAndroid::_remove_copied_libs(String p_gdextension_libs_path) {
print_verbose("Removing previously installed libraries...");
Error error;
- String libs_json = FileAccess::get_file_as_string(GDEXTENSION_LIBS_PATH, &error);
+ String libs_json = FileAccess::get_file_as_string(p_gdextension_libs_path, &error);
if (error || libs_json.is_empty()) {
print_verbose("No previously installed libraries found");
return;
@@ -2777,7 +2805,7 @@ void EditorExportPlatformAndroid::_remove_copied_libs() {
print_verbose("Removing previously installed library " + libs[i]);
da->remove(libs[i]);
}
- da->remove(GDEXTENSION_LIBS_PATH);
+ da->remove(p_gdextension_libs_path);
}
String EditorExportPlatformAndroid::join_list(const List<String> &p_parts, const String &p_separator) {
@@ -2836,6 +2864,8 @@ String EditorExportPlatformAndroid::_resolve_export_plugin_android_library_path(
bool EditorExportPlatformAndroid::_is_clean_build_required(const Ref<EditorExportPreset> &p_preset) {
bool first_build = last_gradle_build_time == 0;
bool have_plugins_changed = false;
+ String gradle_build_dir = ExportTemplateManager::get_android_build_directory(p_preset);
+ bool has_build_dir_changed = last_gradle_build_dir != gradle_build_dir;
String plugin_names = _get_plugins_names(p_preset);
@@ -2855,9 +2885,10 @@ bool EditorExportPlatformAndroid::_is_clean_build_required(const Ref<EditorExpor
}
last_gradle_build_time = OS::get_singleton()->get_unix_time();
+ last_gradle_build_dir = gradle_build_dir;
last_plugin_names = plugin_names;
- return have_plugins_changed || first_build;
+ return have_plugins_changed || has_build_dir_changed || first_build;
}
Error EditorExportPlatformAndroid::export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags) {
@@ -2881,6 +2912,7 @@ Error EditorExportPlatformAndroid::export_project_helper(const Ref<EditorExportP
EditorProgress ep("export", TTR("Exporting for Android"), 105, true);
bool use_gradle_build = bool(p_preset->get("gradle_build/use_gradle_build"));
+ String gradle_build_directory = use_gradle_build ? ExportTemplateManager::get_android_build_directory(p_preset) : "";
bool p_give_internet = p_flags & (DEBUG_FLAG_DUMB_CLIENT | DEBUG_FLAG_REMOTE_DEBUG);
bool apk_expansion = p_preset->get("apk_expansion/enable");
Vector<ABI> enabled_abis = get_enabled_abis(p_preset);
@@ -2940,15 +2972,17 @@ Error EditorExportPlatformAndroid::export_project_helper(const Ref<EditorExportP
//test that installed build version is alright
{
print_verbose("Checking build version...");
- Ref<FileAccess> f = FileAccess::open("res://android/.build_version", FileAccess::READ);
+ String gradle_base_directory = gradle_build_directory.get_base_dir();
+ Ref<FileAccess> f = FileAccess::open(gradle_base_directory.path_join(".build_version"), FileAccess::READ);
if (f.is_null()) {
add_message(EXPORT_MESSAGE_ERROR, TTR("Export"), TTR("Trying to build from a gradle built template, but no version info for it exists. Please reinstall from the 'Project' menu."));
return ERR_UNCONFIGURED;
}
- String version = f->get_line().strip_edges();
- print_verbose("- build version: " + version);
- if (version != VERSION_FULL_CONFIG) {
- add_message(EXPORT_MESSAGE_ERROR, TTR("Export"), vformat(TTR("Android build version mismatch: Template installed: %s, Godot version: %s. Please reinstall Android build template from 'Project' menu."), version, VERSION_FULL_CONFIG));
+ String current_version = ExportTemplateManager::get_android_template_identifier(p_preset);
+ String installed_version = f->get_line().strip_edges();
+ print_verbose("- build version: " + installed_version);
+ if (installed_version != current_version) {
+ add_message(EXPORT_MESSAGE_ERROR, TTR("Export"), vformat(TTR(MISMATCHED_VERSIONS_MESSAGE), installed_version, current_version));
return ERR_UNCONFIGURED;
}
}
@@ -2969,9 +3003,9 @@ Error EditorExportPlatformAndroid::export_project_helper(const Ref<EditorExportP
// TODO: should we use "package/name" or "application/config/name"?
String project_name = get_project_name(p_preset->get("package/name"));
- err = _create_project_name_strings_files(p_preset, project_name); //project name localization.
+ err = _create_project_name_strings_files(p_preset, project_name, gradle_build_directory); //project name localization.
if (err != OK) {
- add_message(EXPORT_MESSAGE_ERROR, TTR("Export"), TTR("Unable to overwrite res://android/build/res/*.xml files with project name."));
+ add_message(EXPORT_MESSAGE_ERROR, TTR("Export"), TTR("Unable to overwrite res/*.xml files with project name."));
}
// Copies the project icon files into the appropriate Gradle project directory.
_copy_icons_to_gradle_project(p_preset, processed_splash_config_xml, splash_image, splash_bg_color_image, main_image, foreground, background);
@@ -2979,12 +3013,14 @@ Error EditorExportPlatformAndroid::export_project_helper(const Ref<EditorExportP
_write_tmp_manifest(p_preset, p_give_internet, p_debug);
//stores all the project files inside the Gradle project directory. Also includes all ABIs
- _clear_assets_directory();
- _remove_copied_libs();
+ _clear_assets_directory(p_preset);
+ String gdextension_libs_path = gradle_build_directory.path_join(GDEXTENSION_LIBS_PATH);
+ _remove_copied_libs(gdextension_libs_path);
if (!apk_expansion) {
print_verbose("Exporting project files...");
CustomExportData user_data;
user_data.assets_directory = assets_directory;
+ user_data.libs_directory = gradle_build_directory.path_join("libs");
user_data.debug = p_debug;
if (p_flags & DEBUG_FLAG_DUMB_CLIENT) {
err = export_project_files(p_preset, p_debug, ignore_apk_file, &user_data, copy_gradle_so);
@@ -3023,7 +3059,7 @@ Error EditorExportPlatformAndroid::export_project_helper(const Ref<EditorExportP
build_command = "gradlew";
#endif
- String build_path = ProjectSettings::get_singleton()->get_resource_path().path_join("android/build");
+ String build_path = ProjectSettings::get_singleton()->globalize_path(gradle_build_directory);
build_command = build_path.path_join(build_command);
String package_name = get_package_name(p_preset->get("package/unique_name"));
diff --git a/platform/android/export/export_plugin.h b/platform/android/export/export_plugin.h
index c282055fba..abc462a691 100644
--- a/platform/android/export/export_plugin.h
+++ b/platform/android/export/export_plugin.h
@@ -90,6 +90,7 @@ class EditorExportPlatformAndroid : public EditorExportPlatform {
#endif // DISABLE_DEPRECATED
String last_plugin_names;
uint64_t last_gradle_build_time = 0;
+ String last_gradle_build_dir;
Vector<Device> devices;
SafeFlag devices_changed;
@@ -174,10 +175,6 @@ class EditorExportPlatformAndroid : public EditorExportPlatform {
void load_icon_refs(const Ref<EditorExportPreset> &p_preset, Ref<Image> &icon, Ref<Image> &foreground, Ref<Image> &background);
- void store_image(const LauncherIcon launcher_icon, const Vector<uint8_t> &data);
-
- void store_image(const String &export_path, const Vector<uint8_t> &data);
-
void _copy_icons_to_gradle_project(const Ref<EditorExportPreset> &p_preset,
const String &processed_splash_config_xml,
const Ref<Image> &splash_image,
@@ -198,6 +195,8 @@ public:
virtual void get_export_options(List<ExportOption> *r_options) const override;
+ virtual bool get_export_option_visibility(const EditorExportPreset *p_preset, const String &p_option) const override;
+
virtual String get_export_option_warning(const EditorExportPreset *p_preset, const StringName &p_name) const override;
virtual String get_name() const override;
@@ -248,9 +247,9 @@ public:
Error sign_apk(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &export_path, EditorProgress &ep);
- void _clear_assets_directory();
+ void _clear_assets_directory(const Ref<EditorExportPreset> &p_preset);
- void _remove_copied_libs();
+ void _remove_copied_libs(String p_gdextension_libs_path);
static String join_list(const List<String> &p_parts, const String &p_separator);
static String join_abis(const Vector<ABI> &p_parts, const String &p_separator, bool p_use_arch);
diff --git a/platform/android/export/gradle_export_util.cpp b/platform/android/export/gradle_export_util.cpp
index 0915009235..9eddef6a4c 100644
--- a/platform/android/export/gradle_export_util.cpp
+++ b/platform/android/export/gradle_export_util.cpp
@@ -191,14 +191,14 @@ String _android_xml_escape(const String &p_string) {
}
// Creates strings.xml files inside the gradle project for different locales.
-Error _create_project_name_strings_files(const Ref<EditorExportPreset> &p_preset, const String &project_name) {
+Error _create_project_name_strings_files(const Ref<EditorExportPreset> &p_preset, const String &project_name, const String &p_gradle_build_dir) {
print_verbose("Creating strings resources for supported locales for project " + project_name);
// Stores the string into the default values directory.
String processed_default_xml_string = vformat(godot_project_name_xml_string, _android_xml_escape(project_name));
- store_string_at_path("res://android/build/res/values/godot_project_name_string.xml", processed_default_xml_string);
+ store_string_at_path(p_gradle_build_dir.path_join("res/values/godot_project_name_string.xml"), processed_default_xml_string);
// Searches the Gradle project res/ directory to find all supported locales
- Ref<DirAccess> da = DirAccess::open("res://android/build/res");
+ Ref<DirAccess> da = DirAccess::open(p_gradle_build_dir.path_join("res"));
if (da.is_null()) {
if (OS::get_singleton()->is_stdout_verbose()) {
print_error("Unable to open Android resources directory.");
@@ -217,7 +217,7 @@ Error _create_project_name_strings_files(const Ref<EditorExportPreset> &p_preset
continue;
}
String locale = file.replace("values-", "").replace("-r", "_");
- String locale_directory = "res://android/build/res/" + file + "/godot_project_name_string.xml";
+ String locale_directory = p_gradle_build_dir.path_join("res/" + file + "/godot_project_name_string.xml");
if (appnames.has(locale)) {
String locale_project_name = appnames[locale];
String processed_xml_string = vformat(godot_project_name_xml_string, _android_xml_escape(locale_project_name));
diff --git a/platform/android/export/gradle_export_util.h b/platform/android/export/gradle_export_util.h
index 2498394add..9f8e476f73 100644
--- a/platform/android/export/gradle_export_util.h
+++ b/platform/android/export/gradle_export_util.h
@@ -63,6 +63,7 @@ static const int XR_MODE_OPENXR = 1;
struct CustomExportData {
String assets_directory;
+ String libs_directory;
bool debug;
Vector<String> libs;
};
@@ -94,7 +95,7 @@ Error store_string_at_path(const String &p_path, const String &p_data);
Error rename_and_store_file_in_gradle_project(void *p_userdata, const String &p_path, const Vector<uint8_t> &p_data, int p_file, int p_total, const Vector<String> &p_enc_in_filters, const Vector<String> &p_enc_ex_filters, const Vector<uint8_t> &p_key);
// Creates strings.xml files inside the gradle project for different locales.
-Error _create_project_name_strings_files(const Ref<EditorExportPreset> &p_preset, const String &project_name);
+Error _create_project_name_strings_files(const Ref<EditorExportPreset> &p_preset, const String &project_name, const String &p_gradle_build_dir);
String bool_to_string(bool v);
diff --git a/platform/linuxbsd/doc_classes/EditorExportPlatformLinuxBSD.xml b/platform/linuxbsd/doc_classes/EditorExportPlatformLinuxBSD.xml
index 07378566c3..a44c86202e 100644
--- a/platform/linuxbsd/doc_classes/EditorExportPlatformLinuxBSD.xml
+++ b/platform/linuxbsd/doc_classes/EditorExportPlatformLinuxBSD.xml
@@ -57,17 +57,11 @@
- [code]{exe_name}[/code] - Name of application executable.
- [code]{cmd_args}[/code] - Array of the command line argument for the application.
</member>
- <member name="texture_format/bptc" type="bool" setter="" getter="">
- If [code]true[/code], project textures are exported in the BPTC format.
+ <member name="texture_format/etc2_astc" type="bool" setter="" getter="">
+ If [code]true[/code], project textures are exported in the ETC2/ASTC format.
</member>
- <member name="texture_format/etc" type="bool" setter="" getter="">
- If [code]true[/code], project textures are exported in the ETC format.
- </member>
- <member name="texture_format/etc2" type="bool" setter="" getter="">
- If [code]true[/code], project textures are exported in the ETC2 format.
- </member>
- <member name="texture_format/s3tc" type="bool" setter="" getter="">
- If [code]true[/code], project textures are exported in the S3TC format.
+ <member name="texture_format/s3tc_bptc" type="bool" setter="" getter="">
+ If [code]true[/code], project textures are exported in the S3TC/BPTC format.
</member>
</members>
</class>
diff --git a/platform/linuxbsd/os_linuxbsd.cpp b/platform/linuxbsd/os_linuxbsd.cpp
index 4e87dd42dd..85846335f7 100644
--- a/platform/linuxbsd/os_linuxbsd.cpp
+++ b/platform/linuxbsd/os_linuxbsd.cpp
@@ -127,14 +127,6 @@ void OS_LinuxBSD::alert(const String &p_alert, const String &p_title) {
}
}
-int OS_LinuxBSD::get_low_processor_usage_mode_sleep_usec() const {
- if (DisplayServer::get_singleton() == nullptr || DisplayServer::get_singleton()->get_name() != "Wayland" || is_in_low_processor_usage_mode()) {
- return OS::get_low_processor_usage_mode_sleep_usec();
- }
-
- return 500; // Roughly 2000 FPS, improves frame time when emulating VSync.
-}
-
void OS_LinuxBSD::initialize() {
crash_handler.initialize();
diff --git a/platform/linuxbsd/os_linuxbsd.h b/platform/linuxbsd/os_linuxbsd.h
index d4ffc0e9b4..9084061eb9 100644
--- a/platform/linuxbsd/os_linuxbsd.h
+++ b/platform/linuxbsd/os_linuxbsd.h
@@ -127,8 +127,6 @@ public:
virtual void alert(const String &p_alert, const String &p_title = "ALERT!") override;
- virtual int get_low_processor_usage_mode_sleep_usec() const override;
-
virtual bool _check_internal_feature_support(const String &p_feature) override;
void run();
diff --git a/platform/linuxbsd/wayland/display_server_wayland.cpp b/platform/linuxbsd/wayland/display_server_wayland.cpp
index 85bbfe546a..c957dea32d 100644
--- a/platform/linuxbsd/wayland/display_server_wayland.cpp
+++ b/platform/linuxbsd/wayland/display_server_wayland.cpp
@@ -867,11 +867,11 @@ bool DisplayServerWayland::window_is_focused(WindowID p_window_id) const {
}
bool DisplayServerWayland::window_can_draw(DisplayServer::WindowID p_window_id) const {
- return frame;
+ return !suspended;
}
bool DisplayServerWayland::can_any_window_draw() const {
- return frame;
+ return !suspended;
}
void DisplayServerWayland::window_set_ime_active(const bool p_active, DisplayServer::WindowID p_window_id) {
@@ -1143,7 +1143,32 @@ void DisplayServerWayland::process_events() {
wayland_thread.keyboard_echo_keys();
- frame = wayland_thread.get_reset_frame();
+ if (!suspended) {
+ if (emulate_vsync) {
+ // Due to various reasons, we manually handle display synchronization by
+ // waiting for a frame event (request to draw) or, if available, the actual
+ // window's suspend status. When a window is suspended, we can avoid drawing
+ // altogether, either because the compositor told us that we don't need to or
+ // because the pace of the frame events became unreliable.
+ bool frame = wayland_thread.wait_frame_suspend_ms(1000);
+ if (!frame) {
+ suspended = true;
+ }
+ } else {
+ if (wayland_thread.is_suspended()) {
+ suspended = true;
+ }
+ }
+
+ if (suspended) {
+ DEBUG_LOG_WAYLAND("Window suspended.");
+ }
+ } else {
+ if (wayland_thread.get_reset_frame()) {
+ // At last, a sign of life! We're no longer suspended.
+ suspended = false;
+ }
+ }
wayland_thread.mutex.unlock();
diff --git a/platform/linuxbsd/wayland/display_server_wayland.h b/platform/linuxbsd/wayland/display_server_wayland.h
index d4da80a55f..e42967eb7b 100644
--- a/platform/linuxbsd/wayland/display_server_wayland.h
+++ b/platform/linuxbsd/wayland/display_server_wayland.h
@@ -117,7 +117,7 @@ class DisplayServerWayland : public DisplayServer {
Context context;
- bool frame = false;
+ bool suspended = false;
bool emulate_vsync = false;
String rendering_driver;
diff --git a/platform/linuxbsd/wayland/wayland_thread.cpp b/platform/linuxbsd/wayland/wayland_thread.cpp
index ae1d96a3b1..ebb21722e2 100644
--- a/platform/linuxbsd/wayland/wayland_thread.cpp
+++ b/platform/linuxbsd/wayland/wayland_thread.cpp
@@ -469,7 +469,7 @@ void WaylandThread::_wl_registry_on_global(void *data, struct wl_registry *wl_re
}
if (strcmp(interface, xdg_wm_base_interface.name) == 0) {
- registry->xdg_wm_base = (struct xdg_wm_base *)wl_registry_bind(wl_registry, name, &xdg_wm_base_interface, MAX(2, MIN(5, (int)version)));
+ registry->xdg_wm_base = (struct xdg_wm_base *)wl_registry_bind(wl_registry, name, &xdg_wm_base_interface, MAX(2, MIN(6, (int)version)));
registry->xdg_wm_base_name = name;
xdg_wm_base_add_listener(registry->xdg_wm_base, &xdg_wm_base_listener, nullptr);
@@ -1063,9 +1063,10 @@ void WaylandThread::_xdg_toplevel_on_configure(void *data, struct xdg_toplevel *
WindowState *ws = (WindowState *)data;
ERR_FAIL_NULL(ws);
- // Expect the window to be in windowed mode. The mode will get overridden if
- // the compositor reports otherwise.
+ // Expect the window to be in a plain state. It will get properly set if the
+ // compositor reports otherwise below.
ws->mode = DisplayServer::WINDOW_MODE_WINDOWED;
+ ws->suspended = false;
uint32_t *state = nullptr;
wl_array_for_each(state, states) {
@@ -1078,6 +1079,10 @@ void WaylandThread::_xdg_toplevel_on_configure(void *data, struct xdg_toplevel *
ws->mode = DisplayServer::WINDOW_MODE_FULLSCREEN;
} break;
+ case XDG_TOPLEVEL_STATE_SUSPENDED: {
+ ws->suspended = true;
+ } break;
+
default: {
// We don't care about the other states (for now).
} break;
@@ -1176,9 +1181,10 @@ void WaylandThread::libdecor_frame_on_configure(struct libdecor_frame *frame, st
libdecor_window_state window_state = LIBDECOR_WINDOW_STATE_NONE;
- // Expect the window to be in windowed mode. The mode will get overridden if
- // the compositor reports otherwise.
+ // Expect the window to be in a plain state. It will get properly set if the
+ // compositor reports otherwise below.
ws->mode = DisplayServer::WINDOW_MODE_WINDOWED;
+ ws->suspended = false;
if (libdecor_configuration_get_window_state(configuration, &window_state)) {
if (window_state & LIBDECOR_WINDOW_STATE_MAXIMIZED) {
@@ -1188,6 +1194,10 @@ void WaylandThread::libdecor_frame_on_configure(struct libdecor_frame *frame, st
if (window_state & LIBDECOR_WINDOW_STATE_FULLSCREEN) {
ws->mode = DisplayServer::WINDOW_MODE_FULLSCREEN;
}
+
+ if (window_state & LIBDECOR_WINDOW_STATE_SUSPENDED) {
+ ws->suspended = true;
+ }
}
window_state_update_size(ws, width, height);
@@ -3872,6 +3882,102 @@ bool WaylandThread::get_reset_frame() {
return old_frame;
}
+// Dispatches events until a frame event is received, a window is reported as
+// suspended or the timeout expires.
+bool WaylandThread::wait_frame_suspend_ms(int p_timeout) {
+ if (main_window.suspended) {
+ // The window is suspended! The compositor is telling us _explicitly_ that we
+ // don't need to draw, without letting us guess through the frame event's
+ // timing and stuff like that. Our job here is done.
+ return false;
+ }
+
+ if (frame) {
+ // We already have a frame! Probably it got there while the caller locked :D
+ frame = false;
+ return true;
+ }
+
+ struct pollfd poll_fd;
+ poll_fd.fd = wl_display_get_fd(wl_display);
+ poll_fd.events = POLLIN | POLLHUP;
+
+ int begin_ms = OS::get_singleton()->get_ticks_msec();
+ int remaining_ms = p_timeout;
+
+ while (remaining_ms > 0) {
+ // Empty the event queue while it's full.
+ while (wl_display_prepare_read(wl_display) != 0) {
+ if (wl_display_dispatch_pending(wl_display) == -1) {
+ // Oh no. We'll check and handle any display error below.
+ break;
+ }
+
+ if (main_window.suspended) {
+ return false;
+ }
+
+ if (frame) {
+ // We had a frame event in the queue :D
+ frame = false;
+ return true;
+ }
+ }
+
+ int werror = wl_display_get_error(wl_display);
+
+ if (werror) {
+ if (werror == EPROTO) {
+ struct wl_interface *wl_interface = nullptr;
+ uint32_t id = 0;
+
+ int error_code = wl_display_get_protocol_error(wl_display, (const struct wl_interface **)&wl_interface, &id);
+ CRASH_NOW_MSG(vformat("Wayland protocol error %d on interface %s@%d.", error_code, wl_interface ? wl_interface->name : "unknown", id));
+ } else {
+ CRASH_NOW_MSG(vformat("Wayland client error code %d.", werror));
+ }
+ }
+
+ wl_display_flush(wl_display);
+
+ // Wait for the event file descriptor to have new data.
+ poll(&poll_fd, 1, remaining_ms);
+
+ if (poll_fd.revents | POLLIN) {
+ // Load the queues with fresh new data.
+ wl_display_read_events(wl_display);
+ } else {
+ // Oh well... Stop signaling that we want to read.
+ wl_display_cancel_read(wl_display);
+
+ // We've got no new events :(
+ // We won't even bother with checking the frame flag.
+ return false;
+ }
+
+ // Let's try dispatching now...
+ wl_display_dispatch_pending(wl_display);
+
+ if (main_window.suspended) {
+ return false;
+ }
+
+ if (frame) {
+ frame = false;
+ return true;
+ }
+
+ remaining_ms -= OS::get_singleton()->get_ticks_msec() - begin_ms;
+ }
+
+ DEBUG_LOG_WAYLAND_THREAD("Frame timeout.");
+ return false;
+}
+
+bool WaylandThread::is_suspended() const {
+ return main_window.suspended;
+}
+
void WaylandThread::destroy() {
if (!initialized) {
return;
diff --git a/platform/linuxbsd/wayland/wayland_thread.h b/platform/linuxbsd/wayland/wayland_thread.h
index 86033c1a09..f3e3c3a2ac 100644
--- a/platform/linuxbsd/wayland/wayland_thread.h
+++ b/platform/linuxbsd/wayland/wayland_thread.h
@@ -177,6 +177,7 @@ public:
Rect2i rect;
DisplayServer::WindowMode mode = DisplayServer::WINDOW_MODE_WINDOWED;
+ bool suspended = false;
// These are true by default as it isn't guaranteed that we'll find an
// xdg-shell implementation with wm_capabilities available. If and once we
@@ -939,6 +940,9 @@ public:
void set_frame();
bool get_reset_frame();
+ bool wait_frame_suspend_ms(int p_timeout);
+
+ bool is_suspended() const;
Error init();
void destroy();
diff --git a/platform/windows/doc_classes/EditorExportPlatformWindows.xml b/platform/windows/doc_classes/EditorExportPlatformWindows.xml
index c483d3380b..1239a2b32f 100644
--- a/platform/windows/doc_classes/EditorExportPlatformWindows.xml
+++ b/platform/windows/doc_classes/EditorExportPlatformWindows.xml
@@ -129,17 +129,11 @@
- [code]{exe_name}[/code] - Name of application executable.
- [code]{cmd_args}[/code] - Array of the command line argument for the application.
</member>
- <member name="texture_format/bptc" type="bool" setter="" getter="">
- If [code]true[/code], project textures are exported in the BPTC format.
+ <member name="texture_format/etc2_astc" type="bool" setter="" getter="">
+ If [code]true[/code], project textures are exported in the ETC2/ASTC format.
</member>
- <member name="texture_format/etc" type="bool" setter="" getter="">
- If [code]true[/code], project textures are exported in the ETC format.
- </member>
- <member name="texture_format/etc2" type="bool" setter="" getter="">
- If [code]true[/code], project textures are exported in the ETC2 format.
- </member>
- <member name="texture_format/s3tc" type="bool" setter="" getter="">
- If [code]true[/code], project textures are exported in the S3TC format.
+ <member name="texture_format/s3tc_bptc" type="bool" setter="" getter="">
+ If [code]true[/code], project textures are exported in the S3TC/BPTC format.
</member>
</members>
</class>