summaryrefslogtreecommitdiff
path: root/platform
diff options
context:
space:
mode:
Diffstat (limited to 'platform')
-rw-r--r--platform/android/SCsub11
-rw-r--r--platform/android/audio_driver_opensl.cpp4
-rw-r--r--platform/android/dir_access_jandroid.cpp8
-rw-r--r--platform/android/dir_access_jandroid.h4
-rw-r--r--platform/android/display_server_android.cpp11
-rw-r--r--platform/android/display_server_android.h1
-rw-r--r--platform/android/export/export_plugin.cpp56
-rw-r--r--platform/android/export/gradle_export_util.cpp28
-rw-r--r--platform/android/file_access_android.cpp8
-rw-r--r--platform/android/file_access_android.h5
-rw-r--r--platform/android/java/app/build.gradle34
-rw-r--r--platform/android/java/app/config.gradle52
-rw-r--r--platform/android/java/build.gradle144
-rw-r--r--platform/android/java/editor/build.gradle74
-rw-r--r--platform/android/java/editor/src/dev/res/values/strings.xml4
-rw-r--r--platform/android/java/editor/src/main/AndroidManifest.xml64
-rw-r--r--platform/android/java/editor/src/main/java/org/godotengine/editor/GodotEditor.java124
-rw-r--r--platform/android/java/editor/src/main/java/org/godotengine/editor/GodotGame.java40
-rw-r--r--platform/android/java/editor/src/main/java/org/godotengine/editor/GodotProjectManager.java41
-rw-r--r--platform/android/java/editor/src/main/res/values/strings.xml4
-rw-r--r--platform/android/java/lib/build.gradle84
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/Godot.java27
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/GodotGLRenderView.java3
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/GodotHost.java10
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/GodotIO.java6
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/GodotLib.java8
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/gl/EGLLogWrapper.java566
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/gl/GLSurfaceView.java1939
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/gl/GodotRenderer.java (renamed from platform/android/java/lib/src/org/godotengine/godot/GodotRenderer.java)22
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/xr/ovr/OvrConfigChooser.java3
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/xr/ovr/OvrContextFactory.java3
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/xr/ovr/OvrWindowSurfaceFactory.java2
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/xr/regular/RegularConfigChooser.java3
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/xr/regular/RegularContextFactory.java2
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/xr/regular/RegularFallbackConfigChooser.java2
-rw-r--r--platform/android/java/nativeSrcsConfigs/CMakeLists.txt2
-rw-r--r--platform/android/java/nativeSrcsConfigs/build.gradle3
-rw-r--r--platform/android/java/scripts/publish-module.gradle11
-rw-r--r--platform/android/java/settings.gradle1
-rw-r--r--platform/android/java_godot_io_wrapper.cpp11
-rw-r--r--platform/android/java_godot_io_wrapper.h2
-rw-r--r--platform/android/java_godot_lib_jni.cpp19
-rw-r--r--platform/android/java_godot_lib_jni.h2
-rw-r--r--platform/android/java_godot_wrapper.cpp14
-rw-r--r--platform/android/java_godot_wrapper.h3
-rw-r--r--platform/android/os_android.cpp65
-rw-r--r--platform/android/os_android.h19
-rw-r--r--platform/iphone/export/export_plugin.cpp98
-rw-r--r--platform/iphone/export/export_plugin.h8
-rw-r--r--platform/iphone/os_iphone.h6
-rw-r--r--platform/iphone/os_iphone.mm2
-rw-r--r--platform/javascript/api/javascript_tools_editor_plugin.cpp27
-rw-r--r--platform/javascript/export/export_plugin.cpp51
-rw-r--r--platform/javascript/export/export_server.h7
-rw-r--r--platform/linuxbsd/detect.py3
-rw-r--r--platform/linuxbsd/display_server_x11.cpp54
-rw-r--r--platform/linuxbsd/display_server_x11.h8
-rw-r--r--platform/linuxbsd/export/export_plugin.cpp12
-rw-r--r--platform/linuxbsd/gl_manager_x11.h15
-rw-r--r--platform/linuxbsd/godot_linuxbsd.cpp10
-rw-r--r--platform/linuxbsd/joypad_linux.cpp2
-rw-r--r--platform/linuxbsd/os_linuxbsd.cpp24
-rw-r--r--platform/linuxbsd/os_linuxbsd.h2
-rw-r--r--platform/osx/detect.py1
-rw-r--r--platform/osx/display_server_osx.h2
-rw-r--r--platform/osx/display_server_osx.mm45
-rw-r--r--platform/osx/export/codesign.cpp69
-rw-r--r--platform/osx/export/codesign.h14
-rw-r--r--platform/osx/export/export_plugin.cpp92
-rw-r--r--platform/osx/export/export_plugin.h4
-rw-r--r--platform/osx/export/lipo.cpp31
-rw-r--r--platform/osx/export/lipo.h2
-rw-r--r--platform/osx/export/macho.cpp48
-rw-r--r--platform/osx/export/macho.h8
-rw-r--r--platform/osx/export/plist.cpp5
-rw-r--r--platform/osx/godot_main_osx.mm10
-rw-r--r--platform/osx/joypad_osx.cpp6
-rw-r--r--platform/osx/joypad_osx.h2
-rw-r--r--platform/osx/os_osx.mm35
-rw-r--r--platform/uwp/export/app_packager.cpp50
-rw-r--r--platform/uwp/export/app_packager.h4
-rw-r--r--platform/uwp/export/export_plugin.cpp16
-rw-r--r--platform/uwp/export/export_plugin.h26
-rw-r--r--platform/uwp/joypad_uwp.h2
-rw-r--r--platform/uwp/os_uwp.h10
-rw-r--r--platform/windows/display_server_windows.cpp80
-rw-r--r--platform/windows/display_server_windows.h4
-rw-r--r--platform/windows/export/export_plugin.cpp13
-rw-r--r--platform/windows/gl_manager_windows.h13
-rw-r--r--platform/windows/joypad_windows.cpp10
-rw-r--r--platform/windows/joypad_windows.h4
-rw-r--r--platform/windows/os_windows.h4
92 files changed, 3867 insertions, 621 deletions
diff --git a/platform/android/SCsub b/platform/android/SCsub
index d031d14499..1a3c158d2e 100644
--- a/platform/android/SCsub
+++ b/platform/android/SCsub
@@ -53,10 +53,17 @@ else:
if lib_arch_dir != "":
if env["target"] == "release":
lib_type_dir = "release"
- else: # release_debug, debug
+ elif env["target"] == "release_debug":
lib_type_dir = "debug"
+ else: # debug
+ lib_type_dir = "dev"
- out_dir = "#platform/android/java/lib/libs/" + lib_type_dir + "/" + lib_arch_dir
+ if env["tools"]:
+ lib_tools_dir = "tools/"
+ else:
+ lib_tools_dir = ""
+
+ out_dir = "#platform/android/java/lib/libs/" + lib_tools_dir + lib_type_dir + "/" + lib_arch_dir
env_android.Command(
out_dir + "/libgodot_android.so", "#bin/libgodot" + env["SHLIBSUFFIX"], Move("$TARGET", "$SOURCE")
)
diff --git a/platform/android/audio_driver_opensl.cpp b/platform/android/audio_driver_opensl.cpp
index 8495d2cc18..dcaa586e3b 100644
--- a/platform/android/audio_driver_opensl.cpp
+++ b/platform/android/audio_driver_opensl.cpp
@@ -75,7 +75,7 @@ void AudioDriverOpenSL::_buffer_callback(
void AudioDriverOpenSL::_buffer_callbacks(
SLAndroidSimpleBufferQueueItf queueItf,
void *pContext) {
- AudioDriverOpenSL *ad = (AudioDriverOpenSL *)pContext;
+ AudioDriverOpenSL *ad = static_cast<AudioDriverOpenSL *>(pContext);
ad->_buffer_callback(queueItf);
}
@@ -208,7 +208,7 @@ void AudioDriverOpenSL::_record_buffer_callback(SLAndroidSimpleBufferQueueItf qu
}
void AudioDriverOpenSL::_record_buffer_callbacks(SLAndroidSimpleBufferQueueItf queueItf, void *pContext) {
- AudioDriverOpenSL *ad = (AudioDriverOpenSL *)pContext;
+ AudioDriverOpenSL *ad = static_cast<AudioDriverOpenSL *>(pContext);
ad->_record_buffer_callback(queueItf);
}
diff --git a/platform/android/dir_access_jandroid.cpp b/platform/android/dir_access_jandroid.cpp
index 7fb4f54fca..e2b1c757d6 100644
--- a/platform/android/dir_access_jandroid.cpp
+++ b/platform/android/dir_access_jandroid.cpp
@@ -42,7 +42,7 @@ jmethodID DirAccessJAndroid::_dir_next = nullptr;
jmethodID DirAccessJAndroid::_dir_close = nullptr;
jmethodID DirAccessJAndroid::_dir_is_dir = nullptr;
-DirAccess *DirAccessJAndroid::create_fs() {
+Ref<DirAccess> DirAccessJAndroid::create_fs() {
return memnew(DirAccessJAndroid);
}
@@ -143,7 +143,7 @@ Error DirAccessJAndroid::change_dir(String p_dir) {
return OK;
}
-String DirAccessJAndroid::get_current_dir(bool p_include_drive) {
+String DirAccessJAndroid::get_current_dir(bool p_include_drive) const {
return "res://" + current_dir;
}
@@ -155,9 +155,9 @@ bool DirAccessJAndroid::file_exists(String p_file) {
sd = current_dir.plus_file(p_file);
}
- FileAccessAndroid *f = memnew(FileAccessAndroid);
+ Ref<FileAccessAndroid> f;
+ f.instantiate();
bool exists = f->file_exists(sd);
- memdelete(f);
return exists;
}
diff --git a/platform/android/dir_access_jandroid.h b/platform/android/dir_access_jandroid.h
index 4f4a984b12..3d3c9f9223 100644
--- a/platform/android/dir_access_jandroid.h
+++ b/platform/android/dir_access_jandroid.h
@@ -51,7 +51,7 @@ class DirAccessJAndroid : public DirAccess {
String current_dir;
String current;
- static DirAccess *create_fs();
+ static Ref<DirAccess> create_fs();
public:
virtual Error list_dir_begin(); ///< This starts dir listing
@@ -64,7 +64,7 @@ public:
virtual String get_drive(int p_drive);
virtual Error change_dir(String p_dir); ///< can be relative or absolute, return false on success
- virtual String get_current_dir(bool p_include_drive = true); ///< return current dir location
+ virtual String get_current_dir(bool p_include_drive = true) const; ///< return current dir location
virtual bool file_exists(String p_file);
virtual bool dir_exists(String p_dir);
diff --git a/platform/android/display_server_android.cpp b/platform/android/display_server_android.cpp
index a7a8801bdc..e7de287fc6 100644
--- a/platform/android/display_server_android.cpp
+++ b/platform/android/display_server_android.cpp
@@ -42,7 +42,7 @@
#endif
DisplayServerAndroid *DisplayServerAndroid::get_singleton() {
- return (DisplayServerAndroid *)DisplayServer::get_singleton();
+ return static_cast<DisplayServerAndroid *>(DisplayServer::get_singleton());
}
bool DisplayServerAndroid::has_feature(Feature p_feature) const {
@@ -161,6 +161,13 @@ int DisplayServerAndroid::screen_get_dpi(int p_screen) const {
return godot_io_java->get_screen_dpi();
}
+float DisplayServerAndroid::screen_get_scale(int p_screen) const {
+ GodotIOJavaWrapper *godot_io_java = OS_Android::get_singleton()->get_godot_io_java();
+ ERR_FAIL_COND_V(!godot_io_java, 1.0f);
+
+ return godot_io_java->get_scaled_density();
+}
+
float DisplayServerAndroid::screen_get_refresh_rate(int p_screen) const {
GodotIOJavaWrapper *godot_io_java = OS_Android::get_singleton()->get_godot_io_java();
if (!godot_io_java) {
@@ -270,7 +277,7 @@ int64_t DisplayServerAndroid::window_get_native_handle(HandleType p_handle_type,
return 0; // Not supported.
}
case WINDOW_HANDLE: {
- return (int64_t)((OS_Android *)OS::get_singleton())->get_godot_java()->get_activity();
+ return reinterpret_cast<int64_t>(static_cast<OS_Android *>(OS::get_singleton())->get_godot_java()->get_activity());
}
case WINDOW_VIEW: {
return 0; // Not supported.
diff --git a/platform/android/display_server_android.h b/platform/android/display_server_android.h
index 23077a6529..1d268bbcfd 100644
--- a/platform/android/display_server_android.h
+++ b/platform/android/display_server_android.h
@@ -106,6 +106,7 @@ public:
virtual Size2i screen_get_size(int p_screen = SCREEN_OF_MAIN_WINDOW) const override;
virtual Rect2i screen_get_usable_rect(int p_screen = SCREEN_OF_MAIN_WINDOW) const override;
virtual int screen_get_dpi(int p_screen = SCREEN_OF_MAIN_WINDOW) const override;
+ virtual float screen_get_scale(int p_screen = SCREEN_OF_MAIN_WINDOW) const override;
virtual float screen_get_refresh_rate(int p_screen = SCREEN_OF_MAIN_WINDOW) const override;
virtual bool screen_is_touchscreen(int p_screen = SCREEN_OF_MAIN_WINDOW) const override;
diff --git a/platform/android/export/export_plugin.cpp b/platform/android/export/export_plugin.cpp
index df3693ba61..8cfa3a67b9 100644
--- a/platform/android/export/export_plugin.cpp
+++ b/platform/android/export/export_plugin.cpp
@@ -249,7 +249,7 @@ static const int DEFAULT_TARGET_SDK_VERSION = 30; // Should match the value in '
const String SDK_VERSION_RANGE = vformat("%s,%s,1", DEFAULT_MIN_SDK_VERSION, DEFAULT_TARGET_SDK_VERSION);
void EditorExportPlatformAndroid::_check_for_changes_poll_thread(void *ud) {
- EditorExportPlatformAndroid *ea = (EditorExportPlatformAndroid *)ud;
+ EditorExportPlatformAndroid *ea = static_cast<EditorExportPlatformAndroid *>(ud);
while (!ea->quit_request.is_set()) {
// Check for plugins updates
@@ -596,8 +596,8 @@ Vector<String> EditorExportPlatformAndroid::get_abis() {
/// List the gdap files in the directory specified by the p_path parameter.
Vector<String> EditorExportPlatformAndroid::list_gdap_files(const String &p_path) {
Vector<String> dir_files;
- DirAccessRef da = DirAccess::open(p_path);
- if (da) {
+ Ref<DirAccess> da = DirAccess::open(p_path);
+ if (da.is_valid()) {
da->list_dir_begin();
while (true) {
String file = da->get_next();
@@ -685,7 +685,7 @@ Error EditorExportPlatformAndroid::save_apk_so(void *p_userdata, const SharedObj
ERR_PRINT(err);
return FAILED;
}
- APKExportData *ed = (APKExportData *)p_userdata;
+ APKExportData *ed = static_cast<APKExportData *>(p_userdata);
Vector<String> abis = get_abis();
bool exported = false;
for (int i = 0; i < p_so.tags.size(); ++i) {
@@ -710,7 +710,7 @@ Error EditorExportPlatformAndroid::save_apk_so(void *p_userdata, const SharedObj
}
Error EditorExportPlatformAndroid::save_apk_file(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) {
- APKExportData *ed = (APKExportData *)p_userdata;
+ APKExportData *ed = static_cast<APKExportData *>(p_userdata);
String dst_path = p_path.replace_first("res://", "assets/");
store_in_apk(ed, dst_path, p_data, _should_compress_asset(p_path, p_data) ? Z_DEFLATED : 0);
@@ -725,7 +725,7 @@ Error EditorExportPlatformAndroid::copy_gradle_so(void *p_userdata, const Shared
ERR_FAIL_COND_V_MSG(!p_so.path.get_file().begins_with("lib"), FAILED,
"Android .so file names must start with \"lib\", but got: " + p_so.path);
Vector<String> abis = get_abis();
- CustomExportData *export_data = (CustomExportData *)p_userdata;
+ CustomExportData *export_data = static_cast<CustomExportData *>(p_userdata);
bool exported = false;
for (int i = 0; i < p_so.tags.size(); ++i) {
int abi_index = abis.find(p_so.tags[i]);
@@ -863,6 +863,7 @@ void EditorExportPlatformAndroid::_fix_manifest(const Ref<EditorExportPreset> &p
bool classify_as_game = p_preset->get("package/classify_as_game");
bool retain_data_on_uninstall = p_preset->get("package/retain_data_on_uninstall");
bool exclude_from_recents = p_preset->get("package/exclude_from_recents");
+ bool is_resizeable = bool(GLOBAL_GET("display/window/size/resizable"));
Vector<String> perms;
// Write permissions into the perms variable.
@@ -980,6 +981,10 @@ void EditorExportPlatformAndroid::_fix_manifest(const Ref<EditorExportPreset> &p
encode_uint32(exclude_from_recents, &p_manifest.write[iofs + 16]);
}
+ if (tname == "activity" && attrname == "resizeableActivity") {
+ encode_uint32(is_resizeable, &p_manifest.write[iofs + 16]);
+ }
+
if (tname == "supports-screens") {
if (attrname == "smallScreens") {
encode_uint32(screen_support_small ? 0xFFFFFFFF : 0, &p_manifest.write[iofs + 16]);
@@ -2003,7 +2008,7 @@ String EditorExportPlatformAndroid::get_apksigner_path() {
Error errn;
String build_tools_dir = sdk_path.plus_file("build-tools");
- DirAccessRef da = DirAccess::open(build_tools_dir, &errn);
+ Ref<DirAccess> da = DirAccess::open(build_tools_dir, &errn);
if (errn != OK) {
print_error("Unable to open Android 'build-tools' directory.");
return apksigner_path;
@@ -2120,7 +2125,7 @@ bool EditorExportPlatformAndroid::can_export(const Ref<EditorExportPreset> &p_pr
} else {
Error errn;
// Check for the platform-tools directory.
- DirAccessRef da = DirAccess::open(sdk_path.plus_file("platform-tools"), &errn);
+ Ref<DirAccess> da = DirAccess::open(sdk_path.plus_file("platform-tools"), &errn);
if (errn != OK) {
err += TTR("Invalid Android SDK path in Editor Settings.");
err += TTR("Missing 'platform-tools' directory!");
@@ -2138,7 +2143,7 @@ bool EditorExportPlatformAndroid::can_export(const Ref<EditorExportPreset> &p_pr
}
// Check for the build-tools directory.
- DirAccessRef build_tools_da = DirAccess::open(sdk_path.plus_file("build-tools"), &errn);
+ Ref<DirAccess> build_tools_da = DirAccess::open(sdk_path.plus_file("build-tools"), &errn);
if (errn != OK) {
err += TTR("Invalid Android SDK path in Editor Settings.");
err += TTR("Missing 'build-tools' directory!");
@@ -2417,12 +2422,12 @@ Error EditorExportPlatformAndroid::sign_apk(const Ref<EditorExportPreset> &p_pre
}
void EditorExportPlatformAndroid::_clear_assets_directory() {
- DirAccessRef da_res = DirAccess::create(DirAccess::ACCESS_RESOURCES);
+ Ref<DirAccess> da_res = DirAccess::create(DirAccess::ACCESS_RESOURCES);
// Clear the APK assets directory
if (da_res->dir_exists(APK_ASSETS_DIRECTORY)) {
print_verbose("Clearing APK assets directory..");
- DirAccessRef da_assets = DirAccess::open(APK_ASSETS_DIRECTORY);
+ Ref<DirAccess> da_assets = DirAccess::open(APK_ASSETS_DIRECTORY);
da_assets->erase_contents_recursive();
da_res->remove(APK_ASSETS_DIRECTORY);
}
@@ -2430,7 +2435,7 @@ void EditorExportPlatformAndroid::_clear_assets_directory() {
// Clear the AAB assets directory
if (da_res->dir_exists(AAB_ASSETS_DIRECTORY)) {
print_verbose("Clearing AAB assets directory..");
- DirAccessRef da_assets = DirAccess::open(AAB_ASSETS_DIRECTORY);
+ Ref<DirAccess> da_assets = DirAccess::open(AAB_ASSETS_DIRECTORY);
da_assets->erase_contents_recursive();
da_res->remove(AAB_ASSETS_DIRECTORY);
}
@@ -2450,7 +2455,7 @@ void EditorExportPlatformAndroid::_remove_copied_libs() {
ERR_FAIL_COND_MSG(error, "Error parsing \"" + libs_json + "\" on line " + itos(json.get_error_line()) + ": " + json.get_error_message());
Vector<String> libs = json.get_data();
- DirAccessRef da = DirAccess::create(DirAccess::ACCESS_RESOURCES);
+ Ref<DirAccess> da = DirAccess::create(DirAccess::ACCESS_RESOURCES);
for (int i = 0; i < libs.size(); i++) {
print_verbose("Removing previously installed library " + libs[i]);
da->remove(libs[i]);
@@ -2539,14 +2544,13 @@ Error EditorExportPlatformAndroid::export_project_helper(const Ref<EditorExportP
//test that installed build version is alright
{
print_verbose("Checking build version..");
- FileAccessRef f = FileAccess::open("res://android/.build_version", FileAccess::READ);
- if (!f) {
+ Ref<FileAccess> f = FileAccess::open("res://android/.build_version", FileAccess::READ);
+ if (f.is_null()) {
EditorNode::get_singleton()->show_warning(TTR("Trying to build from a custom 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);
- f->close();
if (version != VERSION_FULL_CONFIG) {
EditorNode::get_singleton()->show_warning(vformat(TTR("Android build version mismatch:\n Template installed: %s\n Godot Version: %s\nPlease reinstall Android build template from 'Project' menu."), version, VERSION_FULL_CONFIG));
return ERR_UNCONFIGURED;
@@ -2582,10 +2586,9 @@ Error EditorExportPlatformAndroid::export_project_helper(const Ref<EditorExportP
return err;
}
if (user_data.libs.size() > 0) {
- FileAccessRef fa = FileAccess::open(GDNATIVE_LIBS_PATH, FileAccess::WRITE);
+ Ref<FileAccess> fa = FileAccess::open(GDNATIVE_LIBS_PATH, FileAccess::WRITE);
JSON json;
fa->store_string(json.stringify(user_data.libs, "\t"));
- fa->close();
}
} else {
print_verbose("Saving apk expansion file..");
@@ -2765,8 +2768,7 @@ Error EditorExportPlatformAndroid::export_project_helper(const Ref<EditorExportP
return ERR_FILE_BAD_PATH;
}
- FileAccess *src_f = nullptr;
- zlib_filefunc_def io = zipio_create_io_from_file(&src_f);
+ zlib_filefunc_def io = zipio_create_io();
if (ep.step(TTR("Creating APK..."), 0)) {
return ERR_SKIP;
@@ -2780,9 +2782,7 @@ Error EditorExportPlatformAndroid::export_project_helper(const Ref<EditorExportP
int ret = unzGoToFirstFile(pkg);
- zlib_filefunc_def io2 = io;
- FileAccess *dst_f = nullptr;
- io2.opaque = &dst_f;
+ zlib_filefunc_def io2 = zipio_create_io();
String tmp_unaligned_path = EditorPaths::get_singleton()->get_cache_dir().plus_file("tmpexport-unaligned." + uitos(OS::get_singleton()->get_unix_time()) + ".apk");
@@ -2807,6 +2807,9 @@ Error EditorExportPlatformAndroid::export_project_helper(const Ref<EditorExportP
unz_file_info info;
char fname[16384];
ret = unzGetCurrentFileInfo(pkg, &info, fname, 16384, nullptr, 0, nullptr, 0);
+ if (ret != UNZ_OK) {
+ break;
+ }
bool skip = false;
@@ -2975,9 +2978,7 @@ Error EditorExportPlatformAndroid::export_project_helper(const Ref<EditorExportP
ret = unzGoToFirstFile(tmp_unaligned);
- io2 = io;
- dst_f = nullptr;
- io2.opaque = &dst_f;
+ io2 = zipio_create_io();
zipFile final_apk = zipOpen2(p_path.utf8().get_data(), APPEND_STATUS_CREATE, nullptr, &io2);
// Take files from the unaligned APK and write them out to the aligned one
@@ -2991,6 +2992,9 @@ Error EditorExportPlatformAndroid::export_project_helper(const Ref<EditorExportP
char fname[16384];
char extra[16384];
ret = unzGetCurrentFileInfo(tmp_unaligned, &info, fname, 16384, extra, 16384 - ZIP_ALIGNMENT, nullptr, 0);
+ if (ret != UNZ_OK) {
+ break;
+ }
String file = String::utf8(fname);
diff --git a/platform/android/export/gradle_export_util.cpp b/platform/android/export/gradle_export_util.cpp
index ab915a5f85..173bb8bcb7 100644
--- a/platform/android/export/gradle_export_util.cpp
+++ b/platform/android/export/gradle_export_util.cpp
@@ -75,8 +75,8 @@ String _get_android_orientation_label(DisplayServer::ScreenOrientation screen_or
// Utility method used to create a directory.
Error create_directory(const String &p_dir) {
if (!DirAccess::exists(p_dir)) {
- DirAccessRef filesystem_da = DirAccess::create(DirAccess::ACCESS_RESOURCES);
- ERR_FAIL_COND_V_MSG(!filesystem_da, ERR_CANT_CREATE, "Cannot create directory '" + p_dir + "'.");
+ Ref<DirAccess> filesystem_da = DirAccess::create(DirAccess::ACCESS_RESOURCES);
+ ERR_FAIL_COND_V_MSG(filesystem_da.is_null(), ERR_CANT_CREATE, "Cannot create directory '" + p_dir + "'.");
Error err = filesystem_da->make_dir_recursive(p_dir);
ERR_FAIL_COND_V_MSG(err, ERR_CANT_CREATE, "Cannot create directory '" + p_dir + "'.");
}
@@ -91,10 +91,9 @@ Error store_file_at_path(const String &p_path, const Vector<uint8_t> &p_data) {
if (err != OK) {
return err;
}
- FileAccess *fa = FileAccess::open(p_path, FileAccess::WRITE);
- ERR_FAIL_COND_V_MSG(!fa, ERR_CANT_CREATE, "Cannot create file '" + p_path + "'.");
+ Ref<FileAccess> fa = FileAccess::open(p_path, FileAccess::WRITE);
+ ERR_FAIL_COND_V_MSG(fa.is_null(), ERR_CANT_CREATE, "Cannot create file '" + p_path + "'.");
fa->store_buffer(p_data.ptr(), p_data.size());
- memdelete(fa);
return OK;
}
@@ -109,10 +108,9 @@ Error store_string_at_path(const String &p_path, const String &p_data) {
}
return err;
}
- FileAccess *fa = FileAccess::open(p_path, FileAccess::WRITE);
- ERR_FAIL_COND_V_MSG(!fa, ERR_CANT_CREATE, "Cannot create file '" + p_path + "'.");
+ Ref<FileAccess> fa = FileAccess::open(p_path, FileAccess::WRITE);
+ ERR_FAIL_COND_V_MSG(fa.is_null(), ERR_CANT_CREATE, "Cannot create file '" + p_path + "'.");
fa->store_string(p_data);
- memdelete(fa);
return OK;
}
@@ -122,7 +120,7 @@ Error store_string_at_path(const String &p_path, const String &p_data) {
// It's functionality mirrors that of the method save_apk_file.
// This method will be called ONLY when custom build is enabled.
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) {
- CustomExportData *export_data = (CustomExportData *)p_userdata;
+ CustomExportData *export_data = static_cast<CustomExportData *>(p_userdata);
String dst_path = p_path.replace_first("res://", export_data->assets_directory + "/");
print_verbose("Saving project files from " + p_path + " into " + dst_path);
Error err = store_file_at_path(dst_path, p_data);
@@ -152,8 +150,8 @@ Error _create_project_name_strings_files(const Ref<EditorExportPreset> &p_preset
store_string_at_path("res://android/build/res/values/godot_project_name_string.xml", processed_default_xml_string);
// Searches the Gradle project res/ directory to find all supported locales
- DirAccessRef da = DirAccess::open("res://android/build/res");
- if (!da) {
+ Ref<DirAccess> da = DirAccess::open("res://android/build/res");
+ if (da.is_null()) {
if (OS::get_singleton()->is_stdout_verbose()) {
print_error("Unable to open Android resources directory.");
}
@@ -253,11 +251,13 @@ String _get_activity_tag(const Ref<EditorExportPreset> &p_preset) {
String orientation = _get_android_orientation_label(DisplayServer::ScreenOrientation(int(GLOBAL_GET("display/window/handheld/orientation"))));
String manifest_activity_text = vformat(
" <activity android:name=\"com.godot.game.GodotApp\" "
- "tools:replace=\"android:screenOrientation,android:excludeFromRecents\" "
+ "tools:replace=\"android:screenOrientation,android:excludeFromRecents,android:resizeableActivity\" "
"android:excludeFromRecents=\"%s\" "
- "android:screenOrientation=\"%s\">\n",
+ "android:screenOrientation=\"%s\" "
+ "android:resizeableActivity=\"%s\">\n",
bool_to_string(p_preset->get("package/exclude_from_recents")),
- orientation);
+ orientation,
+ bool_to_string(bool(GLOBAL_GET("display/window/size/resizable"))));
if (uses_xr) {
manifest_activity_text += " <meta-data tools:node=\"replace\" android:name=\"com.oculus.vr.focusaware\" android:value=\"true\" />\n";
} else {
diff --git a/platform/android/file_access_android.cpp b/platform/android/file_access_android.cpp
index c84a919b6b..d7ba31e3c9 100644
--- a/platform/android/file_access_android.cpp
+++ b/platform/android/file_access_android.cpp
@@ -34,11 +34,13 @@
AAssetManager *FileAccessAndroid::asset_manager = nullptr;
-FileAccess *FileAccessAndroid::create_android() {
+Ref<FileAccess> FileAccessAndroid::create_android() {
return memnew(FileAccessAndroid);
}
Error FileAccessAndroid::_open(const String &p_path, int p_mode_flags) {
+ _close();
+
String path = fix_path(p_path).simplify_path();
if (path.begins_with("/")) {
path = path.substr(1, path.length());
@@ -58,7 +60,7 @@ Error FileAccessAndroid::_open(const String &p_path, int p_mode_flags) {
return OK;
}
-void FileAccessAndroid::close() {
+void FileAccessAndroid::_close() {
if (!a) {
return;
}
@@ -162,5 +164,5 @@ bool FileAccessAndroid::file_exists(const String &p_path) {
}
FileAccessAndroid::~FileAccessAndroid() {
- close();
+ _close();
}
diff --git a/platform/android/file_access_android.h b/platform/android/file_access_android.h
index fb612cd008..33b692da20 100644
--- a/platform/android/file_access_android.h
+++ b/platform/android/file_access_android.h
@@ -38,17 +38,18 @@
//#include <android_native_app_glue.h>
class FileAccessAndroid : public FileAccess {
- static FileAccess *create_android();
+ static Ref<FileAccess> create_android();
mutable AAsset *a = nullptr;
mutable uint64_t len = 0;
mutable uint64_t pos = 0;
mutable bool eof = false;
+ void _close();
+
public:
static AAssetManager *asset_manager;
virtual Error _open(const String &p_path, int p_mode_flags); ///< open a file
- virtual void close(); ///< close a file
virtual bool is_open() const; ///< true when file is open
virtual void seek(uint64_t p_position); ///< seek to a given position
diff --git a/platform/android/java/app/build.gradle b/platform/android/java/app/build.gradle
index 5d1a9d7b99..b6303d1bc9 100644
--- a/platform/android/java/app/build.gradle
+++ b/platform/android/java/app/build.gradle
@@ -33,6 +33,11 @@ allprojects {
}
}
+configurations {
+ // Initializes a placeholder for the devImplementation dependency configuration.
+ devImplementation {}
+}
+
dependencies {
implementation libraries.kotlinStdLib
implementation libraries.androidxFragment
@@ -45,6 +50,7 @@ dependencies {
// Custom build mode. In this scenario this project is the only one around and the Godot
// library is available through the pre-generated godot-lib.*.aar android archive files.
debugImplementation fileTree(dir: 'libs/debug', include: ['*.jar', '*.aar'])
+ devImplementation fileTree(dir: 'libs/dev', include: ['*.jar', '*.aar'])
releaseImplementation fileTree(dir: 'libs/release', include: ['*.jar', '*.aar'])
}
@@ -66,6 +72,7 @@ dependencies {
android {
compileSdkVersion versions.compileSdk
buildToolsVersion versions.buildTools
+ ndkVersion versions.ndkVersion
compileOptions {
sourceCompatibility versions.javaVersion
@@ -93,6 +100,8 @@ android {
versionName getExportVersionName()
minSdkVersion getExportMinSdkVersion()
targetSdkVersion getExportTargetSdkVersion()
+
+ missingDimensionStrategy 'products', 'template'
}
lintOptions {
@@ -146,6 +155,18 @@ android {
}
}
+ dev {
+ initWith debug
+ // Signing and zip-aligning are skipped for prebuilt builds, but
+ // performed for custom builds.
+ zipAlignEnabled shouldZipAlign()
+ if (shouldSign()) {
+ signingConfig signingConfigs.debug
+ } else {
+ signingConfig null
+ }
+ }
+
release {
// Signing and zip-aligning are skipped for prebuilt builds, but
// performed for custom builds.
@@ -167,6 +188,7 @@ android {
assets.srcDirs = ['assets']
}
debug.jniLibs.srcDirs = ['libs/debug', 'libs/debug/vulkan_validation_layers']
+ dev.jniLibs.srcDirs = ['libs/dev']
release.jniLibs.srcDirs = ['libs/release']
}
@@ -183,6 +205,12 @@ task copyAndRenameDebugApk(type: Copy) {
rename "android_debug.apk", getExportFilename()
}
+task copyAndRenameDevApk(type: Copy) {
+ from "$buildDir/outputs/apk/dev/android_dev.apk"
+ into getExportPath()
+ rename "android_dev.apk", getExportFilename()
+}
+
task copyAndRenameReleaseApk(type: Copy) {
from "$buildDir/outputs/apk/release/android_release.apk"
into getExportPath()
@@ -195,6 +223,12 @@ task copyAndRenameDebugAab(type: Copy) {
rename "build-debug.aab", getExportFilename()
}
+task copyAndRenameDevAab(type: Copy) {
+ from "$buildDir/outputs/bundle/dev/build-dev.aab"
+ into getExportPath()
+ rename "build-dev.aab", getExportFilename()
+}
+
task copyAndRenameReleaseAab(type: Copy) {
from "$buildDir/outputs/bundle/release/build-release.aab"
into getExportPath()
diff --git a/platform/android/java/app/config.gradle b/platform/android/java/app/config.gradle
index c238d1b361..1b2976e715 100644
--- a/platform/android/java/app/config.gradle
+++ b/platform/android/java/app/config.gradle
@@ -76,7 +76,7 @@ ext.getGodotEditorVersion = { ->
String editorVersion = project.hasProperty("godot_editor_version") ? project.property("godot_editor_version") : ""
if (editorVersion == null || editorVersion.isEmpty()) {
// Try the library version first
- editorVersion = getGodotLibraryVersion()
+ editorVersion = getGodotLibraryVersionName()
if (editorVersion.isEmpty()) {
// Fallback value.
@@ -86,9 +86,24 @@ ext.getGodotEditorVersion = { ->
return editorVersion
}
+ext.getGodotLibraryVersionCode = { ->
+ String versionName = ""
+ int versionCode = 1
+ (versionName, versionCode) = getGodotLibraryVersion()
+ return versionCode
+}
+
+ext.getGodotLibraryVersionName = { ->
+ String versionName = ""
+ int versionCode = 1
+ (versionName, versionCode) = getGodotLibraryVersion()
+ return versionName
+}
+
ext.generateGodotLibraryVersion = { List<String> requiredKeys ->
// Attempt to read the version from the `version.py` file.
- String libraryVersion = ""
+ String libraryVersionName = ""
+ int libraryVersionCode = 0
File versionFile = new File("../../../version.py")
if (versionFile.isFile()) {
@@ -109,15 +124,35 @@ ext.generateGodotLibraryVersion = { List<String> requiredKeys ->
}
if (requiredKeys.empty) {
- libraryVersion = map.values().join(".")
+ libraryVersionName = map.values().join(".")
+ try {
+ if (map.containsKey("patch")) {
+ libraryVersionCode = Integer.parseInt(map["patch"])
+ }
+
+ if (map.containsKey("minor")) {
+ libraryVersionCode += (Integer.parseInt(map["minor"]) * 100)
+ }
+
+ if (map.containsKey("major")) {
+ libraryVersionCode += (Integer.parseInt(map["major"]) * 10000)
+ }
+ } catch (NumberFormatException ignore) {
+ libraryVersionCode = 1
+ }
}
}
- if (libraryVersion.isEmpty()) {
+ if (libraryVersionName.isEmpty()) {
// Fallback value in case we're unable to read the file.
- libraryVersion = "custom_build"
+ libraryVersionName = "custom_build"
+ }
+
+ if (libraryVersionCode == 0) {
+ libraryVersionCode = 1
}
- return libraryVersion
+
+ return [libraryVersionName, libraryVersionCode]
}
ext.getGodotLibraryVersion = { ->
@@ -127,7 +162,10 @@ ext.getGodotLibraryVersion = { ->
ext.getGodotPublishVersion = { ->
List<String> requiredKeys = ["major", "minor", "patch", "status"]
- return generateGodotLibraryVersion(requiredKeys)
+ String versionName = ""
+ int versionCode = 1
+ (versionName, versionCode) = generateGodotLibraryVersion(requiredKeys)
+ return versionName
}
final String VALUE_SEPARATOR_REGEX = "\\|"
diff --git a/platform/android/java/build.gradle b/platform/android/java/build.gradle
index 83bc68c992..e16ca65df5 100644
--- a/platform/android/java/build.gradle
+++ b/platform/android/java/build.gradle
@@ -26,21 +26,22 @@ allprojects {
ext {
supportedAbis = ["armv7", "arm64v8", "x86", "x86_64"]
- supportedTargets = ["release", "debug"]
+ supportedTargetsMap = [release: "release", dev: "debug", debug: "release_debug"]
+ supportedFlavors = ["editor", "template"]
- // Used by gradle to specify which architecture to build for by default when running `./gradlew build`.
- // This command is usually used by Android Studio.
+ // Used by gradle to specify which architecture to build for by default when running
+ // `./gradlew build` (this command is usually used by Android Studio).
// If building manually on the command line, it's recommended to use the
- // `./gradlew generateGodotTemplates` build command instead after running the `scons` command.
- // The defaultAbi must be one of the {supportedAbis} values.
- defaultAbi = "arm64v8"
+ // `./gradlew generateGodotTemplates` build command instead after running the `scons` command(s).
+ // The {selectedAbis} values must be from the {supportedAbis} values.
+ selectedAbis = ["arm64v8"]
}
def rootDir = "../../.."
def binDir = "$rootDir/bin/"
-def getSconsTaskName(String buildType) {
- return "compileGodotNativeLibs" + buildType.capitalize()
+def getSconsTaskName(String flavor, String buildType, String abi) {
+ return "compileGodotNativeLibs" + flavor.capitalize() + buildType.capitalize() + abi.capitalize()
}
/**
@@ -55,6 +56,17 @@ task copyDebugBinaryToBin(type: Copy) {
}
/**
+ * Copy the generated 'android_dev.apk' binary template into the Godot bin directory.
+ * Depends on the app build task to ensure the binary is generated prior to copying.
+ */
+task copyDevBinaryToBin(type: Copy) {
+ dependsOn ':app:assembleDev'
+ from('app/build/outputs/apk/dev')
+ into(binDir)
+ include('android_dev.apk')
+}
+
+/**
* Copy the generated 'android_release.apk' binary template into the Godot bin directory.
* Depends on the app build task to ensure the binary is generated prior to copying.
*/
@@ -70,7 +82,7 @@ task copyReleaseBinaryToBin(type: Copy) {
* Depends on the library build task to ensure the AAR file is generated prior to copying.
*/
task copyDebugAARToAppModule(type: Copy) {
- dependsOn ':lib:assembleDebug'
+ dependsOn ':lib:assembleTemplateDebug'
from('lib/build/outputs/aar')
into('app/libs/debug')
include('godot-lib.debug.aar')
@@ -81,18 +93,40 @@ task copyDebugAARToAppModule(type: Copy) {
* Depends on the library build task to ensure the AAR file is generated prior to copying.
*/
task copyDebugAARToBin(type: Copy) {
- dependsOn ':lib:assembleDebug'
+ dependsOn ':lib:assembleTemplateDebug'
from('lib/build/outputs/aar')
into(binDir)
include('godot-lib.debug.aar')
}
/**
+ * Copy the Godot android library archive dev file into the app module dev libs directory.
+ * Depends on the library build task to ensure the AAR file is generated prior to copying.
+ */
+task copyDevAARToAppModule(type: Copy) {
+ dependsOn ':lib:assembleTemplateDev'
+ from('lib/build/outputs/aar')
+ into('app/libs/dev')
+ include('godot-lib.dev.aar')
+}
+
+/**
+ * Copy the Godot android library archive dev file into the root bin directory.
+ * Depends on the library build task to ensure the AAR file is generated prior to copying.
+ */
+task copyDevAARToBin(type: Copy) {
+ dependsOn ':lib:assembleTemplateDev'
+ from('lib/build/outputs/aar')
+ into(binDir)
+ include('godot-lib.dev.aar')
+}
+
+/**
* Copy the Godot android library archive release file into the app module release libs directory.
* Depends on the library build task to ensure the AAR file is generated prior to copying.
*/
task copyReleaseAARToAppModule(type: Copy) {
- dependsOn ':lib:assembleRelease'
+ dependsOn ':lib:assembleTemplateRelease'
from('lib/build/outputs/aar')
into('app/libs/release')
include('godot-lib.release.aar')
@@ -103,7 +137,7 @@ task copyReleaseAARToAppModule(type: Copy) {
* Depends on the library build task to ensure the AAR file is generated prior to copying.
*/
task copyReleaseAARToBin(type: Copy) {
- dependsOn ':lib:assembleRelease'
+ dependsOn ':lib:assembleTemplateRelease'
from('lib/build/outputs/aar')
into(binDir)
include('godot-lib.release.aar')
@@ -111,7 +145,7 @@ task copyReleaseAARToBin(type: Copy) {
/**
* Generate Godot custom build template by zipping the source files from the app directory, as well
- * as the AAR files generated by 'copyDebugAAR' and 'copyReleaseAAR'.
+ * as the AAR files generated by 'copyDebugAAR', 'copyDevAAR' and 'copyReleaseAAR'.
* The zip file also includes some gradle tools to allow building of the custom build.
*/
task zipCustomBuild(type: Zip) {
@@ -130,8 +164,18 @@ def templateExcludedBuildTask() {
def excludedTasks = []
if (!isAndroidStudio()) {
logger.lifecycle("Excluding Android studio build tasks")
- for (String buildType : supportedTargets) {
- excludedTasks += ":lib:" + getSconsTaskName(buildType)
+ for (String flavor : supportedFlavors) {
+ for (String buildType : supportedTargetsMap.keySet()) {
+ if (buildType == "release" && flavor == "editor") {
+ // The editor can't be used with target=release as debugging tools are then not
+ // included, and it would crash on errors instead of reporting them.
+ continue
+ }
+
+ for (String abi : selectedAbis) {
+ excludedTasks += ":lib:" + getSconsTaskName(flavor, buildType, abi)
+ }
+ }
}
}
return excludedTasks
@@ -141,7 +185,7 @@ def templateBuildTasks() {
def tasks = []
// Only build the apks and aar files for which we have native shared libraries.
- for (String target : supportedTargets) {
+ for (String target : supportedTargetsMap.keySet()) {
File targetLibs = new File("lib/libs/" + target)
if (targetLibs != null
&& targetLibs.isDirectory()
@@ -167,6 +211,50 @@ def isAndroidStudio() {
return sysProps != null && sysProps['idea.platform.prefix'] != null
}
+task copyEditorDebugBinaryToBin(type: Copy) {
+ dependsOn ':editor:assembleDebug'
+ from('editor/build/outputs/apk/debug')
+ into(binDir)
+ include('android_editor.apk')
+}
+
+task copyEditorDevBinaryToBin(type: Copy) {
+ dependsOn ':editor:assembleDev'
+ from('editor/build/outputs/apk/dev')
+ into(binDir)
+ include('android_editor_dev.apk')
+}
+
+/**
+ * Generate the Godot Editor Android apk.
+ *
+ * Note: The Godot 'tools' shared libraries must have been generated (via scons) prior to running
+ * this gradle task. The task will only build the apk(s) for which the shared libraries is
+ * available.
+ */
+task generateGodotEditor {
+ gradle.startParameter.excludedTaskNames += templateExcludedBuildTask()
+
+ def tasks = []
+
+ for (String target : supportedTargetsMap.keySet()) {
+ if (target == "release") {
+ // The editor can't be used with target=release as debugging tools are then not
+ // included, and it would crash on errors instead of reporting them.
+ continue
+ }
+ File targetLibs = new File("lib/libs/tools/" + target)
+ if (targetLibs != null
+ && targetLibs.isDirectory()
+ && targetLibs.listFiles() != null
+ && targetLibs.listFiles().length > 0) {
+ tasks += "copyEditor${target.capitalize()}BinaryToBin"
+ }
+ }
+
+ dependsOn = tasks
+}
+
/**
* Master task used to coordinate the tasks defined above to generate the set of Godot templates.
*/
@@ -191,7 +279,27 @@ task generateDevTemplate {
}
/**
- * Clean the generated artifacts.
+ * Clean the generated editor artifacts.
+ */
+task cleanGodotEditor(type: Delete) {
+ // Delete the generated native tools libs
+ delete("lib/libs/tools")
+
+ // Delete the library generated AAR files
+ delete("lib/build/outputs/aar")
+
+ // Delete the generated binary apks
+ delete("editor/build/outputs/apk")
+
+ // Delete the Godot editor apks in the Godot bin directory
+ delete("$binDir/android_editor.apk")
+ delete("$binDir/android_editor_dev.apk")
+
+ finalizedBy getTasksByName("clean", true)
+}
+
+/**
+ * Clean the generated template artifacts.
*/
task cleanGodotTemplates(type: Delete) {
// Delete the generated native libs
@@ -208,9 +316,11 @@ task cleanGodotTemplates(type: Delete) {
// Delete the Godot templates in the Godot bin directory
delete("$binDir/android_debug.apk")
+ delete("$binDir/android_dev.apk")
delete("$binDir/android_release.apk")
delete("$binDir/android_source.zip")
delete("$binDir/godot-lib.debug.aar")
+ delete("$binDir/godot-lib.dev.aar")
delete("$binDir/godot-lib.release.aar")
finalizedBy getTasksByName("clean", true)
diff --git a/platform/android/java/editor/build.gradle b/platform/android/java/editor/build.gradle
new file mode 100644
index 0000000000..3312f61ad3
--- /dev/null
+++ b/platform/android/java/editor/build.gradle
@@ -0,0 +1,74 @@
+// Gradle build config for Godot Engine's Android port.
+apply plugin: 'com.android.application'
+
+dependencies {
+ implementation libraries.kotlinStdLib
+ implementation libraries.androidxFragment
+ implementation project(":lib")
+}
+
+android {
+ compileSdkVersion versions.compileSdk
+ buildToolsVersion versions.buildTools
+ ndkVersion versions.ndkVersion
+
+ defaultConfig {
+ // The 'applicationId' suffix allows to install Godot 3.x(v3) and 4.x(v4) on the same device
+ applicationId "org.godotengine.editor.v4"
+ versionCode getGodotLibraryVersionCode()
+ versionName getGodotLibraryVersionName()
+ minSdkVersion versions.minSdk
+ //noinspection ExpiredTargetSdkVersion - Restrict to version 29 until https://github.com/godotengine/godot/pull/51815 is submitted
+ targetSdkVersion 29 // versions.targetSdk
+
+ missingDimensionStrategy 'products', 'editor'
+ }
+
+ compileOptions {
+ sourceCompatibility versions.javaVersion
+ targetCompatibility versions.javaVersion
+ }
+
+ buildTypes {
+ dev {
+ initWith debug
+ applicationIdSuffix ".dev"
+ }
+
+ debug {
+ initWith release
+
+ // Need to swap with the release signing config when this is ready for public release.
+ signingConfig signingConfigs.debug
+ }
+
+ release {
+ // This buildtype is disabled below.
+ // The editor can't be used with target=release only, as debugging tools are then not
+ // included, and it would crash on errors instead of reporting them.
+ }
+ }
+
+ packagingOptions {
+ // 'doNotStrip' is enabled for development within Android Studio
+ if (shouldNotStrip()) {
+ doNotStrip '**/*.so'
+ }
+ }
+
+ // Disable 'release' buildtype.
+ // The editor can't be used with target=release only, as debugging tools are then not
+ // included, and it would crash on errors instead of reporting them.
+ variantFilter { variant ->
+ if (variant.buildType.name == "release") {
+ setIgnore(true)
+ }
+ }
+
+ applicationVariants.all { variant ->
+ variant.outputs.all { output ->
+ def suffix = variant.name == "dev" ? "_dev" : ""
+ output.outputFileName = "android_editor${suffix}.apk"
+ }
+ }
+}
diff --git a/platform/android/java/editor/src/dev/res/values/strings.xml b/platform/android/java/editor/src/dev/res/values/strings.xml
new file mode 100644
index 0000000000..45fae3fd39
--- /dev/null
+++ b/platform/android/java/editor/src/dev/res/values/strings.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <string name="godot_editor_name_string">Godot Editor 4.x (dev)</string>
+</resources>
diff --git a/platform/android/java/editor/src/main/AndroidManifest.xml b/platform/android/java/editor/src/main/AndroidManifest.xml
new file mode 100644
index 0000000000..bae075d929
--- /dev/null
+++ b/platform/android/java/editor/src/main/AndroidManifest.xml
@@ -0,0 +1,64 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ package="org.godotengine.editor"
+ android:installLocation="auto">
+
+ <supports-screens
+ android:largeScreens="true"
+ android:normalScreens="true"
+ android:smallScreens="true"
+ android:xlargeScreens="true" />
+
+ <uses-feature
+ android:glEsVersion="0x00020000"
+ android:required="true" />
+
+ <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+ <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
+ <uses-permission android:name="android.permission.INTERNET" />
+
+ <application
+ android:allowBackup="false"
+ android:icon="@mipmap/icon"
+ android:label="@string/godot_editor_name_string"
+ tools:ignore="GoogleAppIndexingWarning"
+ android:requestLegacyExternalStorage="true">
+
+ <activity
+ android:name=".GodotProjectManager"
+ android:configChanges="orientation|keyboardHidden|screenSize|smallestScreenSize|density|keyboard|navigation|screenLayout|uiMode"
+ android:launchMode="singleTask"
+ android:screenOrientation="userLandscape"
+ android:exported="true"
+ android:theme="@android:style/Theme.Black.NoTitleBar.Fullscreen"
+ android:process=":GodotProjectManager">
+
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+
+ <activity
+ android:name=".GodotEditor"
+ android:configChanges="orientation|keyboardHidden|screenSize|smallestScreenSize|density|keyboard|navigation|screenLayout|uiMode"
+ android:process=":GodotEditor"
+ android:launchMode="singleTask"
+ android:screenOrientation="userLandscape"
+ android:theme="@android:style/Theme.Black.NoTitleBar.Fullscreen">
+ </activity>
+
+ <activity
+ android:name=".GodotGame"
+ android:configChanges="orientation|keyboardHidden|screenSize|smallestScreenSize|density|keyboard|navigation|screenLayout|uiMode"
+ android:label="@string/godot_project_name_string"
+ android:process=":GodotGame"
+ android:launchMode="singleTask"
+ android:screenOrientation="userLandscape"
+ android:theme="@android:style/Theme.Black.NoTitleBar.Fullscreen">
+ </activity>
+
+ </application>
+
+</manifest>
diff --git a/platform/android/java/editor/src/main/java/org/godotengine/editor/GodotEditor.java b/platform/android/java/editor/src/main/java/org/godotengine/editor/GodotEditor.java
new file mode 100644
index 0000000000..8a6bf88267
--- /dev/null
+++ b/platform/android/java/editor/src/main/java/org/godotengine/editor/GodotEditor.java
@@ -0,0 +1,124 @@
+/*************************************************************************/
+/* GodotEditor.java */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+package org.godotengine.editor;
+
+import org.godotengine.godot.FullScreenGodotApp;
+import org.godotengine.godot.utils.PermissionsUtil;
+
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.Debug;
+
+import androidx.annotation.Nullable;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * Base class for the Godot Android Editor activities.
+ *
+ * This provides the basic templates for the activities making up this application.
+ * Each derived activity runs in its own process, which enable up to have several instances of
+ * the Godot engine up and running at the same time.
+ *
+ * It also plays the role of the primary editor window.
+ */
+public class GodotEditor extends FullScreenGodotApp {
+ private static final boolean WAIT_FOR_DEBUGGER = false;
+ private static final String COMMAND_LINE_PARAMS = "command_line_params";
+
+ private static final String EDITOR_ARG = "--editor";
+ private static final String PROJECT_MANAGER_ARG = "--project-manager";
+
+ private final List<String> commandLineParams = new ArrayList<>();
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ PermissionsUtil.requestManifestPermissions(this);
+
+ String[] params = getIntent().getStringArrayExtra(COMMAND_LINE_PARAMS);
+ updateCommandLineParams(params);
+
+ if (BuildConfig.BUILD_TYPE.equals("debug") && WAIT_FOR_DEBUGGER) {
+ Debug.waitForDebugger();
+ }
+ super.onCreate(savedInstanceState);
+ }
+
+ private void updateCommandLineParams(@Nullable String[] args) {
+ // Update the list of command line params with the new args
+ commandLineParams.clear();
+ if (args != null && args.length > 0) {
+ commandLineParams.addAll(Arrays.asList(args));
+ }
+ }
+
+ @Override
+ public List<String> getCommandLine() {
+ return commandLineParams;
+ }
+
+ @Override
+ public void onNewGodotInstanceRequested(String[] args) {
+ // Parse the arguments to figure out which activity to start.
+ Class<?> targetClass = GodotGame.class;
+ for (String arg : args) {
+ if (EDITOR_ARG.equals(arg)) {
+ targetClass = GodotEditor.class;
+ break;
+ }
+
+ if (PROJECT_MANAGER_ARG.equals(arg)) {
+ targetClass = GodotProjectManager.class;
+ break;
+ }
+ }
+
+ // Launch a new activity
+ Intent newInstance = new Intent(this, targetClass).putExtra(COMMAND_LINE_PARAMS, args);
+ startActivity(newInstance);
+ }
+
+ @Override
+ public void setRequestedOrientation(int requestedOrientation) {
+ if (!overrideOrientationRequest()) {
+ super.setRequestedOrientation(requestedOrientation);
+ }
+ }
+
+ /**
+ * The Godot Android Editor sets its own orientation via its AndroidManifest
+ */
+ protected boolean overrideOrientationRequest() {
+ return true;
+ }
+}
diff --git a/platform/android/java/editor/src/main/java/org/godotengine/editor/GodotGame.java b/platform/android/java/editor/src/main/java/org/godotengine/editor/GodotGame.java
new file mode 100644
index 0000000000..12766775a8
--- /dev/null
+++ b/platform/android/java/editor/src/main/java/org/godotengine/editor/GodotGame.java
@@ -0,0 +1,40 @@
+/*************************************************************************/
+/* GodotGame.java */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+package org.godotengine.editor;
+
+/**
+ * Drives the 'run project' window of the Godot Editor.
+ */
+public class GodotGame extends GodotEditor {
+ protected boolean overrideOrientationRequest() {
+ return false;
+ }
+}
diff --git a/platform/android/java/editor/src/main/java/org/godotengine/editor/GodotProjectManager.java b/platform/android/java/editor/src/main/java/org/godotengine/editor/GodotProjectManager.java
new file mode 100644
index 0000000000..d30f66bb8c
--- /dev/null
+++ b/platform/android/java/editor/src/main/java/org/godotengine/editor/GodotProjectManager.java
@@ -0,0 +1,41 @@
+/*************************************************************************/
+/* GodotProjectManager.java */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+package org.godotengine.editor;
+
+/**
+ * Launcher activity for the Godot Android Editor.
+ *
+ * It presents the user with the project manager interface.
+ * Upon selection of a project, this activity (via its parent logic) starts the
+ * {@link GodotEditor} activity.
+ */
+public class GodotProjectManager extends GodotEditor {
+}
diff --git a/platform/android/java/editor/src/main/res/values/strings.xml b/platform/android/java/editor/src/main/res/values/strings.xml
new file mode 100644
index 0000000000..e8ce34f34d
--- /dev/null
+++ b/platform/android/java/editor/src/main/res/values/strings.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <string name="godot_editor_name_string">Godot Editor 4.x</string>
+</resources>
diff --git a/platform/android/java/lib/build.gradle b/platform/android/java/lib/build.gradle
index 120a40a31d..c806de1ded 100644
--- a/platform/android/java/lib/build.gradle
+++ b/platform/android/java/lib/build.gradle
@@ -18,14 +18,13 @@ def pathToRootDir = "../../../../"
android {
compileSdkVersion versions.compileSdk
buildToolsVersion versions.buildTools
-
ndkVersion versions.ndkVersion
defaultConfig {
minSdkVersion versions.minSdk
targetSdkVersion versions.targetSdk
- manifestPlaceholders = [godotLibraryVersion: getGodotLibraryVersion()]
+ manifestPlaceholders = [godotLibraryVersion: getGodotLibraryVersionName()]
}
namespace = "org.godotengine.godot"
@@ -35,6 +34,18 @@ android {
targetCompatibility versions.javaVersion
}
+ buildTypes {
+ dev {
+ initWith debug
+ }
+ }
+
+ flavorDimensions "products"
+ productFlavors {
+ editor {}
+ template {}
+ }
+
lintOptions {
abortOnError false
disable 'MissingTranslation', 'UnusedResources'
@@ -58,24 +69,50 @@ android {
aidl.srcDirs = ['aidl']
assets.srcDirs = ['assets']
}
+
debug.jniLibs.srcDirs = ['libs/debug']
+ dev.jniLibs.srcDirs = ['libs/dev']
release.jniLibs.srcDirs = ['libs/release']
+
+ // Editor jni library
+ editorDebug.jniLibs.srcDirs = ['libs/tools/debug']
+ editorDev.jniLibs.srcDirs = ['libs/tools/dev']
+ }
+
+ // Disable 'editorRelease'.
+ // The editor can't be used with target=release as debugging tools are then not
+ // included, and it would crash on errors instead of reporting them.
+ variantFilter { variant ->
+ if (variant.name == "editorRelease") {
+ setIgnore(true)
+ }
}
libraryVariants.all { variant ->
- variant.outputs.all { output ->
- output.outputFileName = "godot-lib.${variant.name}.aar"
+ def flavorName = variant.getFlavorName()
+ if (flavorName == null || flavorName == "") {
+ throw new GradleException("Invalid product flavor: $flavorName")
}
- def buildType = variant.buildType.name.capitalize()
+ boolean toolsFlag = flavorName == "editor"
- def releaseTarget = buildType.toLowerCase()
- if (releaseTarget == null || releaseTarget == "") {
- throw new GradleException("Invalid build type: " + buildType)
+ def buildType = variant.buildType.name
+ if (buildType == null || buildType == "" || !supportedTargetsMap.containsKey(buildType)) {
+ throw new GradleException("Invalid build type: $buildType")
}
- if (!supportedAbis.contains(defaultAbi)) {
- throw new GradleException("Invalid default abi: " + defaultAbi)
+ def sconsTarget = supportedTargetsMap[buildType]
+ if (sconsTarget == null || sconsTarget == "") {
+ throw new GradleException("Invalid scons target: $sconsTarget")
+ }
+
+ // Update the name of the generated library
+ def outputSuffix = "${buildType}.aar"
+ if (toolsFlag) {
+ outputSuffix = "tools.$outputSuffix"
+ }
+ variant.outputs.all { output ->
+ output.outputFileName = "godot-lib.${outputSuffix}"
}
// Find scons' executable path
@@ -88,13 +125,11 @@ android {
for (ext in sconsExts) {
String sconsNameExt = sconsName + ext
logger.lifecycle("Checking $sconsNameExt")
-
sconsExecutableFile = org.gradle.internal.os.OperatingSystem.current().findInPath(sconsNameExt)
if (sconsExecutableFile != null) {
// We're done!
break
}
-
// Check all the options in path
List<File> allOptions = org.gradle.internal.os.OperatingSystem.current().findAllInPath(sconsNameExt)
if (!allOptions.isEmpty()) {
@@ -103,27 +138,32 @@ android {
break
}
}
-
if (sconsExecutableFile == null) {
throw new GradleException("Unable to find executable path for the '$sconsName' command.")
} else {
logger.lifecycle("Found executable path for $sconsName: ${sconsExecutableFile.absolutePath}")
}
- // Creating gradle task to generate the native libraries for the default abi.
- def taskName = getSconsTaskName(buildType)
- tasks.create(name: taskName, type: Exec) {
- executable sconsExecutableFile.absolutePath
- args "--directory=${pathToRootDir}", "platform=android", "target=${releaseTarget}", "android_arch=${defaultAbi}", "-j" + Runtime.runtime.availableProcessors()
- }
+ for (String selectedAbi : selectedAbis) {
+ if (!supportedAbis.contains(selectedAbi)) {
+ throw new GradleException("Invalid selected abi: $selectedAbi")
+ }
- // Schedule the tasks so the generated libs are present before the aar file is packaged.
- tasks["merge${buildType}JniLibFolders"].dependsOn taskName
+ // Creating gradle task to generate the native libraries for the selected abi.
+ def taskName = getSconsTaskName(flavorName, buildType, selectedAbi)
+ tasks.create(name: taskName, type: Exec) {
+ executable sconsExecutableFile.absolutePath
+ args "--directory=${pathToRootDir}", "platform=android", "tools=${toolsFlag}", "target=${sconsTarget}", "android_arch=${selectedAbi}", "-j" + Runtime.runtime.availableProcessors()
+ }
+
+ // Schedule the tasks so the generated libs are present before the aar file is packaged.
+ tasks["merge${flavorName.capitalize()}${buildType.capitalize()}JniLibFolders"].dependsOn taskName
+ }
}
// TODO: Enable when issues with AGP 7.1+ are resolved (https://github.com/GodotVR/godot_openxr/issues/187).
// publishing {
-// singleVariant("release") {
+// singleVariant("templateRelease") {
// withSourcesJar()
// withJavadocJar()
// }
diff --git a/platform/android/java/lib/src/org/godotengine/godot/Godot.java b/platform/android/java/lib/src/org/godotengine/godot/Godot.java
index 78848c109a..8a86136daf 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/Godot.java
+++ b/platform/android/java/lib/src/org/godotengine/godot/Godot.java
@@ -47,7 +47,6 @@ import android.app.AlertDialog;
import android.app.PendingIntent;
import android.content.ClipData;
import android.content.ClipboardManager;
-import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
@@ -333,9 +332,11 @@ public class Godot extends Fragment implements SensorEventListener, IDownloaderC
}
public void restart() {
- if (godotHost != null) {
- godotHost.onGodotRestartRequested(this);
- }
+ runOnUiThread(() -> {
+ if (godotHost != null) {
+ godotHost.onGodotRestartRequested(this);
+ }
+ });
}
public void alert(final String message, final String title) {
@@ -859,9 +860,11 @@ public class Godot extends Fragment implements SensorEventListener, IDownloaderC
private void forceQuit() {
// TODO: This is a temp solution. The proper fix will involve tracking down and properly shutting down each
// native Godot components that is started in Godot#onVideoInit.
- if (godotHost != null) {
- godotHost.onGodotForceQuit(this);
- }
+ runOnUiThread(() -> {
+ if (godotHost != null) {
+ godotHost.onGodotForceQuit(this);
+ }
+ });
}
private boolean obbIsCorrupted(String f, String main_pack_md5) {
@@ -1010,6 +1013,7 @@ public class Godot extends Fragment implements SensorEventListener, IDownloaderC
mProgressFraction.setText(Helpers.getDownloadProgressString(progress.mOverallProgress,
progress.mOverallTotal));
}
+
public void initInputDevices() {
mRenderView.initInputDevices();
}
@@ -1018,4 +1022,13 @@ public class Godot extends Fragment implements SensorEventListener, IDownloaderC
private GodotRenderView getRenderView() { // used by native side to get renderView
return mRenderView;
}
+
+ @Keep
+ private void createNewGodotInstance(String[] args) {
+ runOnUiThread(() -> {
+ if (godotHost != null) {
+ godotHost.onNewGodotInstanceRequested(args);
+ }
+ });
+ }
}
diff --git a/platform/android/java/lib/src/org/godotengine/godot/GodotGLRenderView.java b/platform/android/java/lib/src/org/godotengine/godot/GodotGLRenderView.java
index 61093d54de..08da1b1832 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/GodotGLRenderView.java
+++ b/platform/android/java/lib/src/org/godotengine/godot/GodotGLRenderView.java
@@ -29,6 +29,8 @@
/*************************************************************************/
package org.godotengine.godot;
+import org.godotengine.godot.gl.GLSurfaceView;
+import org.godotengine.godot.gl.GodotRenderer;
import org.godotengine.godot.input.GodotGestureHandler;
import org.godotengine.godot.input.GodotInputHandler;
import org.godotengine.godot.utils.GLUtils;
@@ -43,7 +45,6 @@ import org.godotengine.godot.xr.regular.RegularFallbackConfigChooser;
import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.PixelFormat;
-import android.opengl.GLSurfaceView;
import android.os.Build;
import android.view.GestureDetector;
import android.view.KeyEvent;
diff --git a/platform/android/java/lib/src/org/godotengine/godot/GodotHost.java b/platform/android/java/lib/src/org/godotengine/godot/GodotHost.java
index 8e8f993369..2e7b67194f 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/GodotHost.java
+++ b/platform/android/java/lib/src/org/godotengine/godot/GodotHost.java
@@ -60,8 +60,16 @@ public interface GodotHost {
default void onGodotForceQuit(Godot instance) {}
/**
- * Invoked on the GL thread when the Godot instance wants to be restarted. It's up to the host
+ * Invoked on the UI thread when the Godot instance wants to be restarted. It's up to the host
* to perform the appropriate action(s).
*/
default void onGodotRestartRequested(Godot instance) {}
+
+ /**
+ * Invoked on the UI thread when a new Godot instance is requested. It's up to the host to
+ * perform the appropriate action(s).
+ *
+ * @param args Arguments used to initialize the new instance.
+ */
+ default void onNewGodotInstanceRequested(String[] args) {}
}
diff --git a/platform/android/java/lib/src/org/godotengine/godot/GodotIO.java b/platform/android/java/lib/src/org/godotengine/godot/GodotIO.java
index b151e7eec1..e8e292df5d 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/GodotIO.java
+++ b/platform/android/java/lib/src/org/godotengine/godot/GodotIO.java
@@ -222,10 +222,14 @@ public class GodotIO {
}
public int getScreenDPI() {
- DisplayMetrics metrics = activity.getApplicationContext().getResources().getDisplayMetrics();
+ DisplayMetrics metrics = activity.getResources().getDisplayMetrics();
return (int)(metrics.density * 160f);
}
+ public float getScaledDensity() {
+ return activity.getResources().getDisplayMetrics().scaledDensity;
+ }
+
public double getScreenRefreshRate(double fallback) {
Display display = activity.getWindowManager().getDefaultDisplay();
if (display != null) {
diff --git a/platform/android/java/lib/src/org/godotengine/godot/GodotLib.java b/platform/android/java/lib/src/org/godotengine/godot/GodotLib.java
index 29e4b4b29e..253a51b83c 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/GodotLib.java
+++ b/platform/android/java/lib/src/org/godotengine/godot/GodotLib.java
@@ -30,6 +30,8 @@
package org.godotengine.godot;
+import org.godotengine.godot.gl.GodotRenderer;
+
import android.app.Activity;
import android.hardware.SensorEvent;
import android.view.Surface;
@@ -68,7 +70,7 @@ public class GodotLib {
* @param p_surface
* @param p_width
* @param p_height
- * @see android.opengl.GLSurfaceView.Renderer#onSurfaceChanged(GL10, int, int)
+ * @see org.godotengine.godot.gl.GLSurfaceView.Renderer#onSurfaceChanged(GL10, int, int)
*/
public static native void resize(Surface p_surface, int p_width, int p_height);
@@ -85,9 +87,9 @@ public class GodotLib {
/**
* Invoked on the GL thread to draw the current frame.
- * @see android.opengl.GLSurfaceView.Renderer#onDrawFrame(GL10)
+ * @see org.godotengine.godot.gl.GLSurfaceView.Renderer#onDrawFrame(GL10)
*/
- public static native void step();
+ public static native boolean step();
/**
* Forward touch events from the main thread to the GL thread.
diff --git a/platform/android/java/lib/src/org/godotengine/godot/gl/EGLLogWrapper.java b/platform/android/java/lib/src/org/godotengine/godot/gl/EGLLogWrapper.java
new file mode 100644
index 0000000000..af16cfce74
--- /dev/null
+++ b/platform/android/java/lib/src/org/godotengine/godot/gl/EGLLogWrapper.java
@@ -0,0 +1,566 @@
+// clang-format off
+
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.godotengine.godot.gl;
+
+import android.opengl.GLDebugHelper;
+import android.opengl.GLException;
+
+import java.io.IOException;
+import java.io.Writer;
+
+import javax.microedition.khronos.egl.EGL;
+import javax.microedition.khronos.egl.EGL10;
+import javax.microedition.khronos.egl.EGL11;
+import javax.microedition.khronos.egl.EGLConfig;
+import javax.microedition.khronos.egl.EGLContext;
+import javax.microedition.khronos.egl.EGLDisplay;
+import javax.microedition.khronos.egl.EGLSurface;
+
+class EGLLogWrapper implements EGL11 {
+ private EGL10 mEgl10;
+ Writer mLog;
+ boolean mLogArgumentNames;
+ boolean mCheckError;
+ private int mArgCount;
+
+
+ public EGLLogWrapper(EGL egl, int configFlags, Writer log) {
+ mEgl10 = (EGL10) egl;
+ mLog = log;
+ mLogArgumentNames =
+ (GLDebugHelper.CONFIG_LOG_ARGUMENT_NAMES & configFlags) != 0;
+ mCheckError =
+ (GLDebugHelper.CONFIG_CHECK_GL_ERROR & configFlags) != 0;
+ }
+
+ public boolean eglChooseConfig(EGLDisplay display, int[] attrib_list,
+ EGLConfig[] configs, int config_size, int[] num_config) {
+ begin("eglChooseConfig");
+ arg("display", display);
+ arg("attrib_list", attrib_list);
+ arg("config_size", config_size);
+ end();
+
+ boolean result = mEgl10.eglChooseConfig(display, attrib_list, configs,
+ config_size, num_config);
+ arg("configs", configs);
+ arg("num_config", num_config);
+ returns(result);
+ checkError();
+ return result;
+ }
+
+ public boolean eglCopyBuffers(EGLDisplay display, EGLSurface surface,
+ Object native_pixmap) {
+ begin("eglCopyBuffers");
+ arg("display", display);
+ arg("surface", surface);
+ arg("native_pixmap", native_pixmap);
+ end();
+
+ boolean result = mEgl10.eglCopyBuffers(display, surface, native_pixmap);
+ returns(result);
+ checkError();
+ return result;
+ }
+
+ public EGLContext eglCreateContext(EGLDisplay display, EGLConfig config,
+ EGLContext share_context, int[] attrib_list) {
+ begin("eglCreateContext");
+ arg("display", display);
+ arg("config", config);
+ arg("share_context", share_context);
+ arg("attrib_list", attrib_list);
+ end();
+
+ EGLContext result = mEgl10.eglCreateContext(display, config,
+ share_context, attrib_list);
+ returns(result);
+ checkError();
+ return result;
+ }
+
+ public EGLSurface eglCreatePbufferSurface(EGLDisplay display,
+ EGLConfig config, int[] attrib_list) {
+ begin("eglCreatePbufferSurface");
+ arg("display", display);
+ arg("config", config);
+ arg("attrib_list", attrib_list);
+ end();
+
+ EGLSurface result = mEgl10.eglCreatePbufferSurface(display, config,
+ attrib_list);
+ returns(result);
+ checkError();
+ return result;
+ }
+
+ public EGLSurface eglCreatePixmapSurface(EGLDisplay display,
+ EGLConfig config, Object native_pixmap, int[] attrib_list) {
+ begin("eglCreatePixmapSurface");
+ arg("display", display);
+ arg("config", config);
+ arg("native_pixmap", native_pixmap);
+ arg("attrib_list", attrib_list);
+ end();
+
+ EGLSurface result = mEgl10.eglCreatePixmapSurface(display, config,
+ native_pixmap, attrib_list);
+ returns(result);
+ checkError();
+ return result;
+ }
+
+ public EGLSurface eglCreateWindowSurface(EGLDisplay display,
+ EGLConfig config, Object native_window, int[] attrib_list) {
+ begin("eglCreateWindowSurface");
+ arg("display", display);
+ arg("config", config);
+ arg("native_window", native_window);
+ arg("attrib_list", attrib_list);
+ end();
+
+ EGLSurface result = mEgl10.eglCreateWindowSurface(display, config,
+ native_window, attrib_list);
+ returns(result);
+ checkError();
+ return result;
+ }
+
+ public boolean eglDestroyContext(EGLDisplay display, EGLContext context) {
+ begin("eglDestroyContext");
+ arg("display", display);
+ arg("context", context);
+ end();
+
+ boolean result = mEgl10.eglDestroyContext(display, context);
+ returns(result);
+ checkError();
+ return result;
+ }
+
+ public boolean eglDestroySurface(EGLDisplay display, EGLSurface surface) {
+ begin("eglDestroySurface");
+ arg("display", display);
+ arg("surface", surface);
+ end();
+
+ boolean result = mEgl10.eglDestroySurface(display, surface);
+ returns(result);
+ checkError();
+ return result;
+ }
+
+ public boolean eglGetConfigAttrib(EGLDisplay display, EGLConfig config,
+ int attribute, int[] value) {
+ begin("eglGetConfigAttrib");
+ arg("display", display);
+ arg("config", config);
+ arg("attribute", attribute);
+ end();
+ boolean result = mEgl10.eglGetConfigAttrib(display, config, attribute,
+ value);
+ arg("value", value);
+ returns(result);
+ checkError();
+ return false;
+ }
+
+ public boolean eglGetConfigs(EGLDisplay display, EGLConfig[] configs,
+ int config_size, int[] num_config) {
+ begin("eglGetConfigs");
+ arg("display", display);
+ arg("config_size", config_size);
+ end();
+
+ boolean result = mEgl10.eglGetConfigs(display, configs, config_size,
+ num_config);
+ arg("configs", configs);
+ arg("num_config", num_config);
+ returns(result);
+ checkError();
+ return result;
+ }
+
+ public EGLContext eglGetCurrentContext() {
+ begin("eglGetCurrentContext");
+ end();
+
+ EGLContext result = mEgl10.eglGetCurrentContext();
+ returns(result);
+
+ checkError();
+ return result;
+ }
+
+ public EGLDisplay eglGetCurrentDisplay() {
+ begin("eglGetCurrentDisplay");
+ end();
+
+ EGLDisplay result = mEgl10.eglGetCurrentDisplay();
+ returns(result);
+
+ checkError();
+ return result;
+ }
+
+ public EGLSurface eglGetCurrentSurface(int readdraw) {
+ begin("eglGetCurrentSurface");
+ arg("readdraw", readdraw);
+ end();
+
+ EGLSurface result = mEgl10.eglGetCurrentSurface(readdraw);
+ returns(result);
+
+ checkError();
+ return result;
+ }
+
+ public EGLDisplay eglGetDisplay(Object native_display) {
+ begin("eglGetDisplay");
+ arg("native_display", native_display);
+ end();
+
+ EGLDisplay result = mEgl10.eglGetDisplay(native_display);
+ returns(result);
+
+ checkError();
+ return result;
+ }
+
+ public int eglGetError() {
+ begin("eglGetError");
+ end();
+
+ int result = mEgl10.eglGetError();
+ returns(getErrorString(result));
+
+ return result;
+ }
+
+ public boolean eglInitialize(EGLDisplay display, int[] major_minor) {
+ begin("eglInitialize");
+ arg("display", display);
+ end();
+ boolean result = mEgl10.eglInitialize(display, major_minor);
+ returns(result);
+ arg("major_minor", major_minor);
+ checkError();
+ return result;
+ }
+
+ public boolean eglMakeCurrent(EGLDisplay display, EGLSurface draw,
+ EGLSurface read, EGLContext context) {
+ begin("eglMakeCurrent");
+ arg("display", display);
+ arg("draw", draw);
+ arg("read", read);
+ arg("context", context);
+ end();
+ boolean result = mEgl10.eglMakeCurrent(display, draw, read, context);
+ returns(result);
+ checkError();
+ return result;
+ }
+
+ public boolean eglQueryContext(EGLDisplay display, EGLContext context,
+ int attribute, int[] value) {
+ begin("eglQueryContext");
+ arg("display", display);
+ arg("context", context);
+ arg("attribute", attribute);
+ end();
+ boolean result = mEgl10.eglQueryContext(display, context, attribute,
+ value);
+ returns(value[0]);
+ returns(result);
+ checkError();
+ return result;
+ }
+
+ public String eglQueryString(EGLDisplay display, int name) {
+ begin("eglQueryString");
+ arg("display", display);
+ arg("name", name);
+ end();
+ String result = mEgl10.eglQueryString(display, name);
+ returns(result);
+ checkError();
+ return result;
+ }
+
+ public boolean eglQuerySurface(EGLDisplay display, EGLSurface surface,
+ int attribute, int[] value) {
+ begin("eglQuerySurface");
+ arg("display", display);
+ arg("surface", surface);
+ arg("attribute", attribute);
+ end();
+ boolean result = mEgl10.eglQuerySurface(display, surface, attribute,
+ value);
+ returns(value[0]);
+ returns(result);
+ checkError();
+ return result;
+ }
+
+ public boolean eglSwapBuffers(EGLDisplay display, EGLSurface surface) {
+ begin("eglSwapBuffers");
+ arg("display", display);
+ arg("surface", surface);
+ end();
+ boolean result = mEgl10.eglSwapBuffers(display, surface);
+ returns(result);
+ checkError();
+ return result;
+ }
+
+ public boolean eglTerminate(EGLDisplay display) {
+ begin("eglTerminate");
+ arg("display", display);
+ end();
+ boolean result = mEgl10.eglTerminate(display);
+ returns(result);
+ checkError();
+ return result;
+ }
+
+ public boolean eglWaitGL() {
+ begin("eglWaitGL");
+ end();
+ boolean result = mEgl10.eglWaitGL();
+ returns(result);
+ checkError();
+ return result;
+ }
+
+ public boolean eglWaitNative(int engine, Object bindTarget) {
+ begin("eglWaitNative");
+ arg("engine", engine);
+ arg("bindTarget", bindTarget);
+ end();
+ boolean result = mEgl10.eglWaitNative(engine, bindTarget);
+ returns(result);
+ checkError();
+ return result;
+ }
+
+ private void checkError() {
+ int eglError;
+ if ((eglError = mEgl10.eglGetError()) != EGL_SUCCESS) {
+ String errorMessage = "eglError: " + getErrorString(eglError);
+ logLine(errorMessage);
+ if (mCheckError) {
+ throw new GLException(eglError, errorMessage);
+ }
+ }
+ }
+
+ private void logLine(String message) {
+ log(message + '\n');
+ }
+
+ private void log(String message) {
+ try {
+ mLog.write(message);
+ } catch (IOException e) {
+ // Ignore exception, keep on trying
+ }
+ }
+
+ private void begin(String name) {
+ log(name + '(');
+ mArgCount = 0;
+ }
+
+ private void arg(String name, String value) {
+ if (mArgCount++ > 0) {
+ log(", ");
+ }
+ if (mLogArgumentNames) {
+ log(name + "=");
+ }
+ log(value);
+ }
+
+ private void end() {
+ log(");\n");
+ flush();
+ }
+
+ private void flush() {
+ try {
+ mLog.flush();
+ } catch (IOException e) {
+ mLog = null;
+ }
+ }
+
+ private void arg(String name, int value) {
+ arg(name, Integer.toString(value));
+ }
+
+ private void arg(String name, Object object) {
+ arg(name, toString(object));
+ }
+
+ private void arg(String name, EGLDisplay object) {
+ if (object == EGL10.EGL_DEFAULT_DISPLAY) {
+ arg(name, "EGL10.EGL_DEFAULT_DISPLAY");
+ } else if (object == EGL_NO_DISPLAY) {
+ arg(name, "EGL10.EGL_NO_DISPLAY");
+ } else {
+ arg(name, toString(object));
+ }
+ }
+
+ private void arg(String name, EGLContext object) {
+ if (object == EGL10.EGL_NO_CONTEXT) {
+ arg(name, "EGL10.EGL_NO_CONTEXT");
+ } else {
+ arg(name, toString(object));
+ }
+ }
+
+ private void arg(String name, EGLSurface object) {
+ if (object == EGL10.EGL_NO_SURFACE) {
+ arg(name, "EGL10.EGL_NO_SURFACE");
+ } else {
+ arg(name, toString(object));
+ }
+ }
+
+ private void returns(String result) {
+ log(" returns " + result + ";\n");
+ flush();
+ }
+
+ private void returns(int result) {
+ returns(Integer.toString(result));
+ }
+
+ private void returns(boolean result) {
+ returns(Boolean.toString(result));
+ }
+
+ private void returns(Object result) {
+ returns(toString(result));
+ }
+
+ private String toString(Object obj) {
+ if (obj == null) {
+ return "null";
+ } else {
+ return obj.toString();
+ }
+ }
+
+ private void arg(String name, int[] arr) {
+ if (arr == null) {
+ arg(name, "null");
+ } else {
+ arg(name, toString(arr.length, arr, 0));
+ }
+ }
+
+ private void arg(String name, Object[] arr) {
+ if (arr == null) {
+ arg(name, "null");
+ } else {
+ arg(name, toString(arr.length, arr, 0));
+ }
+ }
+
+ private String toString(int n, int[] arr, int offset) {
+ StringBuilder buf = new StringBuilder();
+ buf.append("{\n");
+ int arrLen = arr.length;
+ for (int i = 0; i < n; i++) {
+ int index = offset + i;
+ buf.append(" [" + index + "] = ");
+ if (index < 0 || index >= arrLen) {
+ buf.append("out of bounds");
+ } else {
+ buf.append(arr[index]);
+ }
+ buf.append('\n');
+ }
+ buf.append("}");
+ return buf.toString();
+ }
+
+ private String toString(int n, Object[] arr, int offset) {
+ StringBuilder buf = new StringBuilder();
+ buf.append("{\n");
+ int arrLen = arr.length;
+ for (int i = 0; i < n; i++) {
+ int index = offset + i;
+ buf.append(" [" + index + "] = ");
+ if (index < 0 || index >= arrLen) {
+ buf.append("out of bounds");
+ } else {
+ buf.append(arr[index]);
+ }
+ buf.append('\n');
+ }
+ buf.append("}");
+ return buf.toString();
+ }
+
+ private static String getHex(int value) {
+ return "0x" + Integer.toHexString(value);
+ }
+
+ public static String getErrorString(int error) {
+ switch (error) {
+ case EGL_SUCCESS:
+ return "EGL_SUCCESS";
+ case EGL_NOT_INITIALIZED:
+ return "EGL_NOT_INITIALIZED";
+ case EGL_BAD_ACCESS:
+ return "EGL_BAD_ACCESS";
+ case EGL_BAD_ALLOC:
+ return "EGL_BAD_ALLOC";
+ case EGL_BAD_ATTRIBUTE:
+ return "EGL_BAD_ATTRIBUTE";
+ case EGL_BAD_CONFIG:
+ return "EGL_BAD_CONFIG";
+ case EGL_BAD_CONTEXT:
+ return "EGL_BAD_CONTEXT";
+ case EGL_BAD_CURRENT_SURFACE:
+ return "EGL_BAD_CURRENT_SURFACE";
+ case EGL_BAD_DISPLAY:
+ return "EGL_BAD_DISPLAY";
+ case EGL_BAD_MATCH:
+ return "EGL_BAD_MATCH";
+ case EGL_BAD_NATIVE_PIXMAP:
+ return "EGL_BAD_NATIVE_PIXMAP";
+ case EGL_BAD_NATIVE_WINDOW:
+ return "EGL_BAD_NATIVE_WINDOW";
+ case EGL_BAD_PARAMETER:
+ return "EGL_BAD_PARAMETER";
+ case EGL_BAD_SURFACE:
+ return "EGL_BAD_SURFACE";
+ case EGL11.EGL_CONTEXT_LOST:
+ return "EGL_CONTEXT_LOST";
+ default:
+ return getHex(error);
+ }
+ }
+}
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
new file mode 100644
index 0000000000..8449c08b88
--- /dev/null
+++ b/platform/android/java/lib/src/org/godotengine/godot/gl/GLSurfaceView.java
@@ -0,0 +1,1939 @@
+// clang-format off
+
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.godotengine.godot.gl;
+
+import android.content.Context;
+import android.opengl.EGL14;
+import android.opengl.EGLExt;
+import android.opengl.GLDebugHelper;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.SurfaceHolder;
+import android.view.SurfaceView;
+
+import java.io.Writer;
+import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+
+import javax.microedition.khronos.egl.EGL10;
+import javax.microedition.khronos.egl.EGL11;
+import javax.microedition.khronos.egl.EGLConfig;
+import javax.microedition.khronos.egl.EGLContext;
+import javax.microedition.khronos.egl.EGLDisplay;
+import javax.microedition.khronos.egl.EGLSurface;
+import javax.microedition.khronos.opengles.GL;
+import javax.microedition.khronos.opengles.GL10;
+
+/**
+ * An implementation of SurfaceView that uses the dedicated surface for
+ * displaying OpenGL rendering.
+ * <p>
+ * A GLSurfaceView provides the following features:
+ * <p>
+ * <ul>
+ * <li>Manages a surface, which is a special piece of memory that can be
+ * composited into the Android view system.
+ * <li>Manages an EGL display, which enables OpenGL to render into a surface.
+ * <li>Accepts a user-provided Renderer object that does the actual rendering.
+ * <li>Renders on a dedicated thread to decouple rendering performance from the
+ * UI thread.
+ * <li>Supports both on-demand and continuous rendering.
+ * <li>Optionally wraps, traces, and/or error-checks the renderer's OpenGL calls.
+ * </ul>
+ *
+ * <div class="special reference">
+ * <h3>Developer Guides</h3>
+ * <p>For more information about how to use OpenGL, read the
+ * <a href="{@docRoot}guide/topics/graphics/opengl.html">OpenGL</a> developer guide.</p>
+ * </div>
+ *
+ * <h3>Using GLSurfaceView</h3>
+ * <p>
+ * Typically you use GLSurfaceView by subclassing it and overriding one or more of the
+ * View system input event methods. If your application does not need to override event
+ * methods then GLSurfaceView can be used as-is. For the most part
+ * GLSurfaceView behavior is customized by calling "set" methods rather than by subclassing.
+ * For example, unlike a regular View, drawing is delegated to a separate Renderer object which
+ * is registered with the GLSurfaceView
+ * using the {@link #setRenderer(Renderer)} call.
+ * <p>
+ * <h3>Initializing GLSurfaceView</h3>
+ * All you have to do to initialize a GLSurfaceView is call {@link #setRenderer(Renderer)}.
+ * However, if desired, you can modify the default behavior of GLSurfaceView by calling one or
+ * more of these methods before calling setRenderer:
+ * <ul>
+ * <li>{@link #setDebugFlags(int)}
+ * <li>{@link #setEGLConfigChooser(boolean)}
+ * <li>{@link #setEGLConfigChooser(EGLConfigChooser)}
+ * <li>{@link #setEGLConfigChooser(int, int, int, int, int, int)}
+ * <li>{@link #setGLWrapper(GLWrapper)}
+ * </ul>
+ * <p>
+ * <h4>Specifying the android.view.Surface</h4>
+ * By default GLSurfaceView will create a PixelFormat.RGB_888 format surface. If a translucent
+ * surface is required, call getHolder().setFormat(PixelFormat.TRANSLUCENT).
+ * The exact format of a TRANSLUCENT surface is device dependent, but it will be
+ * a 32-bit-per-pixel surface with 8 bits per component.
+ * <p>
+ * <h4>Choosing an EGL Configuration</h4>
+ * A given Android device may support multiple EGLConfig rendering configurations.
+ * The available configurations may differ in how many channels of data are present, as
+ * well as how many bits are allocated to each channel. Therefore, the first thing
+ * GLSurfaceView has to do when starting to render is choose what EGLConfig to use.
+ * <p>
+ * By default GLSurfaceView chooses a EGLConfig that has an RGB_888 pixel format,
+ * with at least a 16-bit depth buffer and no stencil.
+ * <p>
+ * If you would prefer a different EGLConfig
+ * you can override the default behavior by calling one of the
+ * setEGLConfigChooser methods.
+ * <p>
+ * <h4>Debug Behavior</h4>
+ * You can optionally modify the behavior of GLSurfaceView by calling
+ * one or more of the debugging methods {@link #setDebugFlags(int)},
+ * and {@link #setGLWrapper}. These methods may be called before and/or after setRenderer, but
+ * typically they are called before setRenderer so that they take effect immediately.
+ * <p>
+ * <h4>Setting a Renderer</h4>
+ * Finally, you must call {@link #setRenderer} to register a {@link Renderer}.
+ * The renderer is
+ * responsible for doing the actual OpenGL rendering.
+ * <p>
+ * <h3>Rendering Mode</h3>
+ * Once the renderer is set, you can control whether the renderer draws
+ * continuously or on-demand by calling
+ * {@link #setRenderMode}. The default is continuous rendering.
+ * <p>
+ * <h3>Activity Life-cycle</h3>
+ * A GLSurfaceView must be notified when to pause and resume rendering. GLSurfaceView clients
+ * are required to call {@link #onPause()} when the activity stops and
+ * {@link #onResume()} when the activity starts. These calls allow GLSurfaceView to
+ * pause and resume the rendering thread, and also allow GLSurfaceView to release and recreate
+ * the OpenGL display.
+ * <p>
+ * <h3>Handling events</h3>
+ * <p>
+ * To handle an event you will typically subclass GLSurfaceView and override the
+ * appropriate method, just as you would with any other View. However, when handling
+ * the event, you may need to communicate with the Renderer object
+ * that's running in the rendering thread. You can do this using any
+ * standard Java cross-thread communication mechanism. In addition,
+ * one relatively easy way to communicate with your renderer is
+ * to call
+ * {@link #queueEvent(Runnable)}. For example:
+ * <pre class="prettyprint">
+ * class MyGLSurfaceView extends GLSurfaceView {
+ *
+ * private MyRenderer mMyRenderer;
+ *
+ * public void start() {
+ * mMyRenderer = ...;
+ * setRenderer(mMyRenderer);
+ * }
+ *
+ * public boolean onKeyDown(int keyCode, KeyEvent event) {
+ * if (keyCode == KeyEvent.KEYCODE_DPAD_CENTER) {
+ * queueEvent(new Runnable() {
+ * // This method will be called on the rendering
+ * // thread:
+ * public void run() {
+ * mMyRenderer.handleDpadCenter();
+ * }});
+ * return true;
+ * }
+ * return super.onKeyDown(keyCode, event);
+ * }
+ * }
+ * </pre>
+ *
+ */
+public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback2 {
+ private final static String TAG = "GLSurfaceView";
+ private final static boolean LOG_ATTACH_DETACH = false;
+ private final static boolean LOG_THREADS = false;
+ private final static boolean LOG_PAUSE_RESUME = false;
+ private final static boolean LOG_SURFACE = false;
+ private final static boolean LOG_RENDERER = false;
+ private final static boolean LOG_RENDERER_DRAW_FRAME = false;
+ private final static boolean LOG_EGL = false;
+ /**
+ * The renderer only renders
+ * when the surface is created, or when {@link #requestRender} is called.
+ *
+ * @see #getRenderMode()
+ * @see #setRenderMode(int)
+ * @see #requestRender()
+ */
+ public final static int RENDERMODE_WHEN_DIRTY = 0;
+ /**
+ * The renderer is called
+ * continuously to re-render the scene.
+ *
+ * @see #getRenderMode()
+ * @see #setRenderMode(int)
+ */
+ public final static int RENDERMODE_CONTINUOUSLY = 1;
+
+ /**
+ * Check glError() after every GL call and throw an exception if glError indicates
+ * that an error has occurred. This can be used to help track down which OpenGL ES call
+ * is causing an error.
+ *
+ * @see #getDebugFlags
+ * @see #setDebugFlags
+ */
+ public final static int DEBUG_CHECK_GL_ERROR = 1;
+
+ /**
+ * Log GL calls to the system log at "verbose" level with tag "GLSurfaceView".
+ *
+ * @see #getDebugFlags
+ * @see #setDebugFlags
+ */
+ public final static int DEBUG_LOG_GL_CALLS = 2;
+
+ /**
+ * Standard View constructor. In order to render something, you
+ * must call {@link #setRenderer} to register a renderer.
+ */
+ public GLSurfaceView(Context context) {
+ super(context);
+ init();
+ }
+
+ /**
+ * Standard View constructor. In order to render something, you
+ * must call {@link #setRenderer} to register a renderer.
+ */
+ public GLSurfaceView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ init();
+ }
+
+ @Override
+ protected void finalize() throws Throwable {
+ try {
+ if (mGLThread != null) {
+ // GLThread may still be running if this view was never
+ // attached to a window.
+ mGLThread.requestExitAndWait();
+ }
+ } finally {
+ super.finalize();
+ }
+ }
+
+ private void init() {
+ // Install a SurfaceHolder.Callback so we get notified when the
+ // underlying surface is created and destroyed
+ SurfaceHolder holder = getHolder();
+ holder.addCallback(this);
+ // setFormat is done by SurfaceView in SDK 2.3 and newer. Uncomment
+ // this statement if back-porting to 2.2 or older:
+ // holder.setFormat(PixelFormat.RGB_565);
+ //
+ // setType is not needed for SDK 2.0 or newer. Uncomment this
+ // statement if back-porting this code to older SDKs.
+ // holder.setType(SurfaceHolder.SURFACE_TYPE_GPU);
+ }
+
+ /**
+ * Set the glWrapper. If the glWrapper is not null, its
+ * {@link GLWrapper#wrap(GL)} method is called
+ * whenever a surface is created. A GLWrapper can be used to wrap
+ * the GL object that's passed to the renderer. Wrapping a GL
+ * object enables examining and modifying the behavior of the
+ * GL calls made by the renderer.
+ * <p>
+ * Wrapping is typically used for debugging purposes.
+ * <p>
+ * The default value is null.
+ * @param glWrapper the new GLWrapper
+ */
+ public void setGLWrapper(GLWrapper glWrapper) {
+ mGLWrapper = glWrapper;
+ }
+
+ /**
+ * Set the debug flags to a new value. The value is
+ * constructed by OR-together zero or more
+ * of the DEBUG_CHECK_* constants. The debug flags take effect
+ * whenever a surface is created. The default value is zero.
+ * @param debugFlags the new debug flags
+ * @see #DEBUG_CHECK_GL_ERROR
+ * @see #DEBUG_LOG_GL_CALLS
+ */
+ public void setDebugFlags(int debugFlags) {
+ mDebugFlags = debugFlags;
+ }
+
+ /**
+ * Get the current value of the debug flags.
+ * @return the current value of the debug flags.
+ */
+ public int getDebugFlags() {
+ return mDebugFlags;
+ }
+
+ /**
+ * Control whether the EGL context is preserved when the GLSurfaceView is paused and
+ * resumed.
+ * <p>
+ * If set to true, then the EGL context may be preserved when the GLSurfaceView is paused.
+ * <p>
+ * Prior to API level 11, whether the EGL context is actually preserved or not
+ * depends upon whether the Android device can support an arbitrary number of
+ * EGL contexts or not. Devices that can only support a limited number of EGL
+ * contexts must release the EGL context in order to allow multiple applications
+ * to share the GPU.
+ * <p>
+ * If set to false, the EGL context will be released when the GLSurfaceView is paused,
+ * and recreated when the GLSurfaceView is resumed.
+ * <p>
+ *
+ * The default is false.
+ *
+ * @param preserveOnPause preserve the EGL context when paused
+ */
+ public void setPreserveEGLContextOnPause(boolean preserveOnPause) {
+ mPreserveEGLContextOnPause = preserveOnPause;
+ }
+
+ /**
+ * @return true if the EGL context will be preserved when paused
+ */
+ public boolean getPreserveEGLContextOnPause() {
+ return mPreserveEGLContextOnPause;
+ }
+
+ /**
+ * Set the renderer associated with this view. Also starts the thread that
+ * will call the renderer, which in turn causes the rendering to start.
+ * <p>This method should be called once and only once in the life-cycle of
+ * a GLSurfaceView.
+ * <p>The following GLSurfaceView methods can only be called <em>before</em>
+ * setRenderer is called:
+ * <ul>
+ * <li>{@link #setEGLConfigChooser(boolean)}
+ * <li>{@link #setEGLConfigChooser(EGLConfigChooser)}
+ * <li>{@link #setEGLConfigChooser(int, int, int, int, int, int)}
+ * </ul>
+ * <p>
+ * The following GLSurfaceView methods can only be called <em>after</em>
+ * setRenderer is called:
+ * <ul>
+ * <li>{@link #getRenderMode()}
+ * <li>{@link #onPause()}
+ * <li>{@link #onResume()}
+ * <li>{@link #queueEvent(Runnable)}
+ * <li>{@link #requestRender()}
+ * <li>{@link #setRenderMode(int)}
+ * </ul>
+ *
+ * @param renderer the renderer to use to perform OpenGL drawing.
+ */
+ public void setRenderer(Renderer renderer) {
+ checkRenderThreadState();
+ if (mEGLConfigChooser == null) {
+ mEGLConfigChooser = new SimpleEGLConfigChooser(true);
+ }
+ if (mEGLContextFactory == null) {
+ mEGLContextFactory = new DefaultContextFactory();
+ }
+ if (mEGLWindowSurfaceFactory == null) {
+ mEGLWindowSurfaceFactory = new DefaultWindowSurfaceFactory();
+ }
+ mRenderer = renderer;
+ mGLThread = new GLThread(mThisWeakRef);
+ mGLThread.start();
+ }
+
+ /**
+ * Install a custom EGLContextFactory.
+ * <p>If this method is
+ * called, it must be called before {@link #setRenderer(Renderer)}
+ * is called.
+ * <p>
+ * If this method is not called, then by default
+ * a context will be created with no shared context and
+ * with a null attribute list.
+ */
+ public void setEGLContextFactory(EGLContextFactory factory) {
+ checkRenderThreadState();
+ mEGLContextFactory = factory;
+ }
+
+ /**
+ * Install a custom EGLWindowSurfaceFactory.
+ * <p>If this method is
+ * called, it must be called before {@link #setRenderer(Renderer)}
+ * is called.
+ * <p>
+ * If this method is not called, then by default
+ * a window surface will be created with a null attribute list.
+ */
+ public void setEGLWindowSurfaceFactory(EGLWindowSurfaceFactory factory) {
+ checkRenderThreadState();
+ mEGLWindowSurfaceFactory = factory;
+ }
+
+ /**
+ * Install a custom EGLConfigChooser.
+ * <p>If this method is
+ * called, it must be called before {@link #setRenderer(Renderer)}
+ * is called.
+ * <p>
+ * If no setEGLConfigChooser method is called, then by default the
+ * view will choose an EGLConfig that is compatible with the current
+ * android.view.Surface, with a depth buffer depth of
+ * at least 16 bits.
+ * @param configChooser
+ */
+ public void setEGLConfigChooser(EGLConfigChooser configChooser) {
+ checkRenderThreadState();
+ mEGLConfigChooser = configChooser;
+ }
+
+ /**
+ * Install a config chooser which will choose a config
+ * as close to 16-bit RGB as possible, with or without an optional depth
+ * buffer as close to 16-bits as possible.
+ * <p>If this method is
+ * called, it must be called before {@link #setRenderer(Renderer)}
+ * is called.
+ * <p>
+ * If no setEGLConfigChooser method is called, then by default the
+ * view will choose an RGB_888 surface with a depth buffer depth of
+ * at least 16 bits.
+ *
+ * @param needDepth
+ */
+ public void setEGLConfigChooser(boolean needDepth) {
+ setEGLConfigChooser(new SimpleEGLConfigChooser(needDepth));
+ }
+
+ /**
+ * Install a config chooser which will choose a config
+ * with at least the specified depthSize and stencilSize,
+ * and exactly the specified redSize, greenSize, blueSize and alphaSize.
+ * <p>If this method is
+ * called, it must be called before {@link #setRenderer(Renderer)}
+ * is called.
+ * <p>
+ * If no setEGLConfigChooser method is called, then by default the
+ * view will choose an RGB_888 surface with a depth buffer depth of
+ * at least 16 bits.
+ *
+ */
+ public void setEGLConfigChooser(int redSize, int greenSize, int blueSize,
+ int alphaSize, int depthSize, int stencilSize) {
+ setEGLConfigChooser(new ComponentSizeChooser(redSize, greenSize,
+ blueSize, alphaSize, depthSize, stencilSize));
+ }
+
+ /**
+ * Inform the default EGLContextFactory and default EGLConfigChooser
+ * which EGLContext client version to pick.
+ * <p>Use this method to create an OpenGL ES 2.0-compatible context.
+ * Example:
+ * <pre class="prettyprint">
+ * public MyView(Context context) {
+ * super(context);
+ * setEGLContextClientVersion(2); // Pick an OpenGL ES 2.0 context.
+ * setRenderer(new MyRenderer());
+ * }
+ * </pre>
+ * <p>Note: Activities which require OpenGL ES 2.0 should indicate this by
+ * setting @lt;uses-feature android:glEsVersion="0x00020000" /> in the activity's
+ * AndroidManifest.xml file.
+ * <p>If this method is called, it must be called before {@link #setRenderer(Renderer)}
+ * is called.
+ * <p>This method only affects the behavior of the default EGLContexFactory and the
+ * default EGLConfigChooser. If
+ * {@link #setEGLContextFactory(EGLContextFactory)} has been called, then the supplied
+ * EGLContextFactory is responsible for creating an OpenGL ES 2.0-compatible context.
+ * If
+ * {@link #setEGLConfigChooser(EGLConfigChooser)} has been called, then the supplied
+ * EGLConfigChooser is responsible for choosing an OpenGL ES 2.0-compatible config.
+ * @param version The EGLContext client version to choose. Use 2 for OpenGL ES 2.0
+ */
+ public void setEGLContextClientVersion(int version) {
+ checkRenderThreadState();
+ mEGLContextClientVersion = version;
+ }
+
+ /**
+ * Set the rendering mode. When renderMode is
+ * RENDERMODE_CONTINUOUSLY, the renderer is called
+ * repeatedly to re-render the scene. When renderMode
+ * is RENDERMODE_WHEN_DIRTY, the renderer only rendered when the surface
+ * is created, or when {@link #requestRender} is called. Defaults to RENDERMODE_CONTINUOUSLY.
+ * <p>
+ * Using RENDERMODE_WHEN_DIRTY can improve battery life and overall system performance
+ * by allowing the GPU and CPU to idle when the view does not need to be updated.
+ * <p>
+ * This method can only be called after {@link #setRenderer(Renderer)}
+ *
+ * @param renderMode one of the RENDERMODE_X constants
+ * @see #RENDERMODE_CONTINUOUSLY
+ * @see #RENDERMODE_WHEN_DIRTY
+ */
+ public void setRenderMode(int renderMode) {
+ mGLThread.setRenderMode(renderMode);
+ }
+
+ /**
+ * Get the current rendering mode. May be called
+ * from any thread. Must not be called before a renderer has been set.
+ * @return the current rendering mode.
+ * @see #RENDERMODE_CONTINUOUSLY
+ * @see #RENDERMODE_WHEN_DIRTY
+ */
+ public int getRenderMode() {
+ return mGLThread.getRenderMode();
+ }
+
+ /**
+ * Request that the renderer render a frame.
+ * This method is typically used when the render mode has been set to
+ * {@link #RENDERMODE_WHEN_DIRTY}, so that frames are only rendered on demand.
+ * May be called
+ * from any thread. Must not be called before a renderer has been set.
+ */
+ public void requestRender() {
+ mGLThread.requestRender();
+ }
+
+ /**
+ * This method is part of the SurfaceHolder.Callback interface, and is
+ * not normally called or subclassed by clients of GLSurfaceView.
+ */
+ public void surfaceCreated(SurfaceHolder holder) {
+ mGLThread.surfaceCreated();
+ }
+
+ /**
+ * This method is part of the SurfaceHolder.Callback interface, and is
+ * not normally called or subclassed by clients of GLSurfaceView.
+ */
+ public void surfaceDestroyed(SurfaceHolder holder) {
+ // Surface will be destroyed when we return
+ mGLThread.surfaceDestroyed();
+ }
+
+ /**
+ * This method is part of the SurfaceHolder.Callback interface, and is
+ * not normally called or subclassed by clients of GLSurfaceView.
+ */
+ public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
+ mGLThread.onWindowResize(w, h);
+ }
+
+ /**
+ * This method is part of the SurfaceHolder.Callback2 interface, and is
+ * not normally called or subclassed by clients of GLSurfaceView.
+ */
+ @Override
+ public void surfaceRedrawNeededAsync(SurfaceHolder holder, Runnable finishDrawing) {
+ if (mGLThread != null) {
+ mGLThread.requestRenderAndNotify(finishDrawing);
+ }
+ }
+
+ /**
+ * This method is part of the SurfaceHolder.Callback2 interface, and is
+ * not normally called or subclassed by clients of GLSurfaceView.
+ */
+ @Deprecated
+ @Override
+ public void surfaceRedrawNeeded(SurfaceHolder holder) {
+ // Since we are part of the framework we know only surfaceRedrawNeededAsync
+ // will be called.
+ }
+
+
+ /**
+ * Pause the rendering thread, optionally tearing down the EGL context
+ * depending upon the value of {@link #setPreserveEGLContextOnPause(boolean)}.
+ *
+ * This method should be called when it is no longer desirable for the
+ * GLSurfaceView to continue rendering, such as in response to
+ * {@link android.app.Activity#onStop Activity.onStop}.
+ *
+ * Must not be called before a renderer has been set.
+ */
+ public void onPause() {
+ mGLThread.onPause();
+ }
+
+ /**
+ * Resumes the rendering thread, re-creating the OpenGL context if necessary. It
+ * is the counterpart to {@link #onPause()}.
+ *
+ * This method should typically be called in
+ * {@link android.app.Activity#onStart Activity.onStart}.
+ *
+ * Must not be called before a renderer has been set.
+ */
+ public void onResume() {
+ mGLThread.onResume();
+ }
+
+ /**
+ * Queue a runnable to be run on the GL rendering thread. This can be used
+ * to communicate with the Renderer on the rendering thread.
+ * Must not be called before a renderer has been set.
+ * @param r the runnable to be run on the GL rendering thread.
+ */
+ public void queueEvent(Runnable r) {
+ mGLThread.queueEvent(r);
+ }
+
+ /**
+ * This method is used as part of the View class and is not normally
+ * called or subclassed by clients of GLSurfaceView.
+ */
+ @Override
+ protected void onAttachedToWindow() {
+ super.onAttachedToWindow();
+ if (LOG_ATTACH_DETACH) {
+ Log.d(TAG, "onAttachedToWindow reattach =" + mDetached);
+ }
+ if (mDetached && (mRenderer != null)) {
+ int renderMode = RENDERMODE_CONTINUOUSLY;
+ if (mGLThread != null) {
+ renderMode = mGLThread.getRenderMode();
+ }
+ mGLThread = new GLThread(mThisWeakRef);
+ if (renderMode != RENDERMODE_CONTINUOUSLY) {
+ mGLThread.setRenderMode(renderMode);
+ }
+ mGLThread.start();
+ }
+ mDetached = false;
+ }
+
+ @Override
+ protected void onDetachedFromWindow() {
+ if (LOG_ATTACH_DETACH) {
+ Log.d(TAG, "onDetachedFromWindow");
+ }
+ if (mGLThread != null) {
+ mGLThread.requestExitAndWait();
+ }
+ mDetached = true;
+ super.onDetachedFromWindow();
+ }
+
+ // ----------------------------------------------------------------------
+
+ /**
+ * An interface used to wrap a GL interface.
+ * <p>Typically
+ * used for implementing debugging and tracing on top of the default
+ * GL interface. You would typically use this by creating your own class
+ * that implemented all the GL methods by delegating to another GL instance.
+ * Then you could add your own behavior before or after calling the
+ * delegate. All the GLWrapper would do was instantiate and return the
+ * wrapper GL instance:
+ * <pre class="prettyprint">
+ * class MyGLWrapper implements GLWrapper {
+ * GL wrap(GL gl) {
+ * return new MyGLImplementation(gl);
+ * }
+ * static class MyGLImplementation implements GL,GL10,GL11,... {
+ * ...
+ * }
+ * }
+ * </pre>
+ * @see #setGLWrapper(GLWrapper)
+ */
+ public interface GLWrapper {
+ /**
+ * Wraps a gl interface in another gl interface.
+ * @param gl a GL interface that is to be wrapped.
+ * @return either the input argument or another GL object that wraps the input argument.
+ */
+ GL wrap(GL gl);
+ }
+
+ /**
+ * A generic renderer interface.
+ * <p>
+ * The renderer is responsible for making OpenGL calls to render a frame.
+ * <p>
+ * GLSurfaceView clients typically create their own classes that implement
+ * this interface, and then call {@link GLSurfaceView#setRenderer} to
+ * register the renderer with the GLSurfaceView.
+ * <p>
+ *
+ * <div class="special reference">
+ * <h3>Developer Guides</h3>
+ * <p>For more information about how to use OpenGL, read the
+ * <a href="{@docRoot}guide/topics/graphics/opengl.html">OpenGL</a> developer guide.</p>
+ * </div>
+ *
+ * <h3>Threading</h3>
+ * The renderer will be called on a separate thread, so that rendering
+ * performance is decoupled from the UI thread. Clients typically need to
+ * communicate with the renderer from the UI thread, because that's where
+ * input events are received. Clients can communicate using any of the
+ * standard Java techniques for cross-thread communication, or they can
+ * use the {@link GLSurfaceView#queueEvent(Runnable)} convenience method.
+ * <p>
+ * <h3>EGL Context Lost</h3>
+ * There are situations where the EGL rendering context will be lost. This
+ * typically happens when device wakes up after going to sleep. When
+ * the EGL context is lost, all OpenGL resources (such as textures) that are
+ * associated with that context will be automatically deleted. In order to
+ * keep rendering correctly, a renderer must recreate any lost resources
+ * that it still needs. The {@link #onSurfaceCreated(GL10, EGLConfig)} method
+ * is a convenient place to do this.
+ *
+ *
+ * @see #setRenderer(Renderer)
+ */
+ public interface Renderer {
+ /**
+ * Called when the surface is created or recreated.
+ * <p>
+ * Called when the rendering thread
+ * starts and whenever the EGL context is lost. The EGL context will typically
+ * be lost when the Android device awakes after going to sleep.
+ * <p>
+ * Since this method is called at the beginning of rendering, as well as
+ * every time the EGL context is lost, this method is a convenient place to put
+ * code to create resources that need to be created when the rendering
+ * starts, and that need to be recreated when the EGL context is lost.
+ * Textures are an example of a resource that you might want to create
+ * here.
+ * <p>
+ * Note that when the EGL context is lost, all OpenGL resources associated
+ * with that context will be automatically deleted. You do not need to call
+ * the corresponding "glDelete" methods such as glDeleteTextures to
+ * manually delete these lost resources.
+ * <p>
+ * @param gl the GL interface. Use <code>instanceof</code> to
+ * test if the interface supports GL11 or higher interfaces.
+ * @param config the EGLConfig of the created surface. Can be used
+ * to create matching pbuffers.
+ */
+ void onSurfaceCreated(GL10 gl, EGLConfig config);
+
+ /**
+ * Called when the surface changed size.
+ * <p>
+ * Called after the surface is created and whenever
+ * the OpenGL ES surface size changes.
+ * <p>
+ * Typically you will set your viewport here. If your camera
+ * is fixed then you could also set your projection matrix here:
+ * <pre class="prettyprint">
+ * void onSurfaceChanged(GL10 gl, int width, int height) {
+ * gl.glViewport(0, 0, width, height);
+ * // for a fixed camera, set the projection too
+ * float ratio = (float) width / height;
+ * gl.glMatrixMode(GL10.GL_PROJECTION);
+ * gl.glLoadIdentity();
+ * gl.glFrustumf(-ratio, ratio, -1, 1, 1, 10);
+ * }
+ * </pre>
+ * @param gl the GL interface. Use <code>instanceof</code> to
+ * test if the interface supports GL11 or higher interfaces.
+ * @param width
+ * @param height
+ */
+ void onSurfaceChanged(GL10 gl, int width, int height);
+
+ // -- GODOT start --
+ /**
+ * Called to draw the current frame.
+ * <p>
+ * This method is responsible for drawing the current frame.
+ * <p>
+ * The implementation of this method typically looks like this:
+ * <pre class="prettyprint">
+ * boolean onDrawFrame(GL10 gl) {
+ * gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);
+ * //... other gl calls to render the scene ...
+ * return true;
+ * }
+ * </pre>
+ * @param gl the GL interface. Use <code>instanceof</code> to
+ * test if the interface supports GL11 or higher interfaces.
+ *
+ * @return true if the buffers should be swapped, false otherwise.
+ */
+ boolean onDrawFrame(GL10 gl);
+ // -- GODOT end --
+ }
+
+ /**
+ * An interface for customizing the eglCreateContext and eglDestroyContext calls.
+ * <p>
+ * This interface must be implemented by clients wishing to call
+ * {@link GLSurfaceView#setEGLContextFactory(EGLContextFactory)}
+ */
+ public interface EGLContextFactory {
+ EGLContext createContext(EGL10 egl, EGLDisplay display, EGLConfig eglConfig);
+ void destroyContext(EGL10 egl, EGLDisplay display, EGLContext context);
+ }
+
+ private class DefaultContextFactory implements EGLContextFactory {
+ private int EGL_CONTEXT_CLIENT_VERSION = 0x3098;
+
+ public EGLContext createContext(EGL10 egl, EGLDisplay display, EGLConfig config) {
+ int[] attrib_list = {EGL_CONTEXT_CLIENT_VERSION, mEGLContextClientVersion,
+ EGL10.EGL_NONE };
+
+ return egl.eglCreateContext(display, config, EGL10.EGL_NO_CONTEXT,
+ mEGLContextClientVersion != 0 ? attrib_list : null);
+ }
+
+ public void destroyContext(EGL10 egl, EGLDisplay display,
+ EGLContext context) {
+ if (!egl.eglDestroyContext(display, context)) {
+ Log.e("DefaultContextFactory", "display:" + display + " context: " + context);
+ if (LOG_THREADS) {
+ Log.i("DefaultContextFactory", "tid=" + Thread.currentThread().getId());
+ }
+ EglHelper.throwEglException("eglDestroyContex", egl.eglGetError());
+ }
+ }
+ }
+
+ /**
+ * An interface for customizing the eglCreateWindowSurface and eglDestroySurface calls.
+ * <p>
+ * This interface must be implemented by clients wishing to call
+ * {@link GLSurfaceView#setEGLWindowSurfaceFactory(EGLWindowSurfaceFactory)}
+ */
+ public interface EGLWindowSurfaceFactory {
+ /**
+ * @return null if the surface cannot be constructed.
+ */
+ EGLSurface createWindowSurface(EGL10 egl, EGLDisplay display, EGLConfig config,
+ Object nativeWindow);
+ void destroySurface(EGL10 egl, EGLDisplay display, EGLSurface surface);
+ }
+
+ private static class DefaultWindowSurfaceFactory implements EGLWindowSurfaceFactory {
+
+ public EGLSurface createWindowSurface(EGL10 egl, EGLDisplay display,
+ EGLConfig config, Object nativeWindow) {
+ EGLSurface result = null;
+ try {
+ result = egl.eglCreateWindowSurface(display, config, nativeWindow, null);
+ } catch (IllegalArgumentException e) {
+ // This exception indicates that the surface flinger surface
+ // is not valid. This can happen if the surface flinger surface has
+ // been torn down, but the application has not yet been
+ // notified via SurfaceHolder.Callback.surfaceDestroyed.
+ // In theory the application should be notified first,
+ // but in practice sometimes it is not. See b/4588890
+ Log.e(TAG, "eglCreateWindowSurface", e);
+ }
+ return result;
+ }
+
+ public void destroySurface(EGL10 egl, EGLDisplay display,
+ EGLSurface surface) {
+ egl.eglDestroySurface(display, surface);
+ }
+ }
+
+ /**
+ * An interface for choosing an EGLConfig configuration from a list of
+ * potential configurations.
+ * <p>
+ * This interface must be implemented by clients wishing to call
+ * {@link GLSurfaceView#setEGLConfigChooser(EGLConfigChooser)}
+ */
+ public interface EGLConfigChooser {
+ /**
+ * Choose a configuration from the list. Implementors 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.
+ * @param egl the EGL10 for the current display.
+ * @param display the current display.
+ * @return the chosen configuration.
+ */
+ EGLConfig chooseConfig(EGL10 egl, EGLDisplay display);
+ }
+
+ private abstract class BaseConfigChooser
+ implements EGLConfigChooser {
+ public BaseConfigChooser(int[] configSpec) {
+ mConfigSpec = filterConfigSpec(configSpec);
+ }
+
+ public EGLConfig chooseConfig(EGL10 egl, EGLDisplay display) {
+ int[] num_config = new int[1];
+ if (!egl.eglChooseConfig(display, mConfigSpec, null, 0,
+ num_config)) {
+ throw new IllegalArgumentException("eglChooseConfig failed");
+ }
+
+ int numConfigs = num_config[0];
+
+ if (numConfigs <= 0) {
+ throw new IllegalArgumentException(
+ "No configs match configSpec");
+ }
+
+ EGLConfig[] configs = new EGLConfig[numConfigs];
+ if (!egl.eglChooseConfig(display, mConfigSpec, configs, numConfigs,
+ num_config)) {
+ throw new IllegalArgumentException("eglChooseConfig#2 failed");
+ }
+ EGLConfig config = chooseConfig(egl, display, configs);
+ if (config == null) {
+ throw new IllegalArgumentException("No config chosen");
+ }
+ return config;
+ }
+
+ abstract EGLConfig chooseConfig(EGL10 egl, EGLDisplay display,
+ EGLConfig[] configs);
+
+ protected int[] mConfigSpec;
+
+ private int[] filterConfigSpec(int[] configSpec) {
+ if (mEGLContextClientVersion != 2 && mEGLContextClientVersion != 3) {
+ return configSpec;
+ }
+ /* We know none of the subclasses define EGL_RENDERABLE_TYPE.
+ * And we know the configSpec is well formed.
+ */
+ int len = configSpec.length;
+ int[] newConfigSpec = new int[len + 2];
+ System.arraycopy(configSpec, 0, newConfigSpec, 0, len-1);
+ newConfigSpec[len-1] = EGL10.EGL_RENDERABLE_TYPE;
+ if (mEGLContextClientVersion == 2) {
+ newConfigSpec[len] = EGL14.EGL_OPENGL_ES2_BIT; /* EGL_OPENGL_ES2_BIT */
+ } else {
+ newConfigSpec[len] = EGLExt.EGL_OPENGL_ES3_BIT_KHR; /* EGL_OPENGL_ES3_BIT_KHR */
+ }
+ newConfigSpec[len+1] = EGL10.EGL_NONE;
+ return newConfigSpec;
+ }
+ }
+
+ /**
+ * Choose a configuration with exactly the specified r,g,b,a sizes,
+ * and at least the specified depth and stencil sizes.
+ */
+ private class ComponentSizeChooser extends BaseConfigChooser {
+ public ComponentSizeChooser(int redSize, int greenSize, int blueSize,
+ int alphaSize, int depthSize, int stencilSize) {
+ super(new int[] {
+ EGL10.EGL_RED_SIZE, redSize,
+ EGL10.EGL_GREEN_SIZE, greenSize,
+ EGL10.EGL_BLUE_SIZE, blueSize,
+ EGL10.EGL_ALPHA_SIZE, alphaSize,
+ EGL10.EGL_DEPTH_SIZE, depthSize,
+ EGL10.EGL_STENCIL_SIZE, stencilSize,
+ EGL10.EGL_NONE});
+ mValue = new int[1];
+ mRedSize = redSize;
+ mGreenSize = greenSize;
+ mBlueSize = blueSize;
+ mAlphaSize = alphaSize;
+ mDepthSize = depthSize;
+ mStencilSize = stencilSize;
+ }
+
+ @Override
+ public EGLConfig chooseConfig(EGL10 egl, EGLDisplay display,
+ EGLConfig[] configs) {
+ for (EGLConfig config : configs) {
+ int d = findConfigAttrib(egl, display, config,
+ EGL10.EGL_DEPTH_SIZE, 0);
+ int s = findConfigAttrib(egl, display, config,
+ EGL10.EGL_STENCIL_SIZE, 0);
+ if ((d >= mDepthSize) && (s >= mStencilSize)) {
+ int r = findConfigAttrib(egl, display, config,
+ EGL10.EGL_RED_SIZE, 0);
+ int g = findConfigAttrib(egl, display, config,
+ EGL10.EGL_GREEN_SIZE, 0);
+ int b = findConfigAttrib(egl, display, config,
+ EGL10.EGL_BLUE_SIZE, 0);
+ int a = findConfigAttrib(egl, display, config,
+ EGL10.EGL_ALPHA_SIZE, 0);
+ if ((r == mRedSize) && (g == mGreenSize)
+ && (b == mBlueSize) && (a == mAlphaSize)) {
+ return config;
+ }
+ }
+ }
+ return null;
+ }
+
+ private int findConfigAttrib(EGL10 egl, EGLDisplay display,
+ EGLConfig config, int attribute, int defaultValue) {
+
+ if (egl.eglGetConfigAttrib(display, config, attribute, mValue)) {
+ return mValue[0];
+ }
+ return defaultValue;
+ }
+
+ private int[] mValue;
+ // Subclasses can adjust these values:
+ protected int mRedSize;
+ protected int mGreenSize;
+ protected int mBlueSize;
+ protected int mAlphaSize;
+ protected int mDepthSize;
+ protected int mStencilSize;
+ }
+
+ /**
+ * This class will choose a RGB_888 surface with
+ * or without a depth buffer.
+ *
+ */
+ private class SimpleEGLConfigChooser extends ComponentSizeChooser {
+ public SimpleEGLConfigChooser(boolean withDepthBuffer) {
+ super(8, 8, 8, 0, withDepthBuffer ? 16 : 0, 0);
+ }
+ }
+
+ /**
+ * An EGL helper class.
+ */
+
+ private static class EglHelper {
+ public EglHelper(WeakReference<GLSurfaceView> glSurfaceViewWeakRef) {
+ mGLSurfaceViewWeakRef = glSurfaceViewWeakRef;
+ }
+
+ /**
+ * Initialize EGL for a given configuration spec.
+ */
+ public void start() {
+ if (LOG_EGL) {
+ Log.w("EglHelper", "start() tid=" + Thread.currentThread().getId());
+ }
+ /*
+ * Get an EGL instance
+ */
+ mEgl = (EGL10) EGLContext.getEGL();
+
+ /*
+ * Get to the default display.
+ */
+ mEglDisplay = mEgl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY);
+
+ if (mEglDisplay == EGL10.EGL_NO_DISPLAY) {
+ throw new RuntimeException("eglGetDisplay failed");
+ }
+
+ /*
+ * We can now initialize EGL for that display
+ */
+ int[] version = new int[2];
+ if(!mEgl.eglInitialize(mEglDisplay, version)) {
+ throw new RuntimeException("eglInitialize failed");
+ }
+ GLSurfaceView view = mGLSurfaceViewWeakRef.get();
+ if (view == null) {
+ mEglConfig = null;
+ mEglContext = null;
+ } else {
+ mEglConfig = view.mEGLConfigChooser.chooseConfig(mEgl, mEglDisplay);
+
+ /*
+ * Create an EGL context. We want to do this as rarely as we can, because an
+ * EGL context is a somewhat heavy object.
+ */
+ mEglContext = view.mEGLContextFactory.createContext(mEgl, mEglDisplay, mEglConfig);
+ }
+ if (mEglContext == null || mEglContext == EGL10.EGL_NO_CONTEXT) {
+ mEglContext = null;
+ throwEglException("createContext");
+ }
+ if (LOG_EGL) {
+ Log.w("EglHelper", "createContext " + mEglContext + " tid=" + Thread.currentThread().getId());
+ }
+
+ mEglSurface = null;
+ }
+
+ /**
+ * Create an egl surface for the current SurfaceHolder surface. If a surface
+ * already exists, destroy it before creating the new surface.
+ *
+ * @return true if the surface was created successfully.
+ */
+ public boolean createSurface() {
+ if (LOG_EGL) {
+ Log.w("EglHelper", "createSurface() tid=" + Thread.currentThread().getId());
+ }
+ /*
+ * Check preconditions.
+ */
+ if (mEgl == null) {
+ throw new RuntimeException("egl not initialized");
+ }
+ if (mEglDisplay == null) {
+ throw new RuntimeException("eglDisplay not initialized");
+ }
+ if (mEglConfig == null) {
+ throw new RuntimeException("mEglConfig not initialized");
+ }
+
+ /*
+ * The window size has changed, so we need to create a new
+ * surface.
+ */
+ destroySurfaceImp();
+
+ /*
+ * Create an EGL surface we can render into.
+ */
+ GLSurfaceView view = mGLSurfaceViewWeakRef.get();
+ if (view != null) {
+ mEglSurface = view.mEGLWindowSurfaceFactory.createWindowSurface(mEgl,
+ mEglDisplay, mEglConfig, view.getHolder());
+ } else {
+ mEglSurface = null;
+ }
+
+ if (mEglSurface == null || mEglSurface == EGL10.EGL_NO_SURFACE) {
+ int error = mEgl.eglGetError();
+ if (error == EGL10.EGL_BAD_NATIVE_WINDOW) {
+ Log.e("EglHelper", "createWindowSurface returned EGL_BAD_NATIVE_WINDOW.");
+ }
+ return false;
+ }
+
+ /*
+ * Before we can issue GL commands, we need to make sure
+ * the context is current and bound to a surface.
+ */
+ if (!mEgl.eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext)) {
+ /*
+ * Could not make the context current, probably because the underlying
+ * SurfaceView surface has been destroyed.
+ */
+ logEglErrorAsWarning("EGLHelper", "eglMakeCurrent", mEgl.eglGetError());
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * Create a GL object for the current EGL context.
+ * @return
+ */
+ GL createGL() {
+
+ GL gl = mEglContext.getGL();
+ GLSurfaceView view = mGLSurfaceViewWeakRef.get();
+ if (view != null) {
+ if (view.mGLWrapper != null) {
+ gl = view.mGLWrapper.wrap(gl);
+ }
+
+ if ((view.mDebugFlags & (DEBUG_CHECK_GL_ERROR | DEBUG_LOG_GL_CALLS)) != 0) {
+ int configFlags = 0;
+ Writer log = null;
+ if ((view.mDebugFlags & DEBUG_CHECK_GL_ERROR) != 0) {
+ configFlags |= GLDebugHelper.CONFIG_CHECK_GL_ERROR;
+ }
+ if ((view.mDebugFlags & DEBUG_LOG_GL_CALLS) != 0) {
+ log = new LogWriter();
+ }
+ gl = GLDebugHelper.wrap(gl, configFlags, log);
+ }
+ }
+ return gl;
+ }
+
+ /**
+ * Display the current render surface.
+ * @return the EGL error code from eglSwapBuffers.
+ */
+ public int swap() {
+ if (! mEgl.eglSwapBuffers(mEglDisplay, mEglSurface)) {
+ return mEgl.eglGetError();
+ }
+ return EGL10.EGL_SUCCESS;
+ }
+
+ public void destroySurface() {
+ if (LOG_EGL) {
+ Log.w("EglHelper", "destroySurface() tid=" + Thread.currentThread().getId());
+ }
+ destroySurfaceImp();
+ }
+
+ private void destroySurfaceImp() {
+ if (mEglSurface != null && mEglSurface != EGL10.EGL_NO_SURFACE) {
+ mEgl.eglMakeCurrent(mEglDisplay, EGL10.EGL_NO_SURFACE,
+ EGL10.EGL_NO_SURFACE,
+ EGL10.EGL_NO_CONTEXT);
+ GLSurfaceView view = mGLSurfaceViewWeakRef.get();
+ if (view != null) {
+ view.mEGLWindowSurfaceFactory.destroySurface(mEgl, mEglDisplay, mEglSurface);
+ }
+ mEglSurface = null;
+ }
+ }
+
+ public void finish() {
+ if (LOG_EGL) {
+ Log.w("EglHelper", "finish() tid=" + Thread.currentThread().getId());
+ }
+ if (mEglContext != null) {
+ GLSurfaceView view = mGLSurfaceViewWeakRef.get();
+ if (view != null) {
+ view.mEGLContextFactory.destroyContext(mEgl, mEglDisplay, mEglContext);
+ }
+ mEglContext = null;
+ }
+ if (mEglDisplay != null) {
+ mEgl.eglTerminate(mEglDisplay);
+ mEglDisplay = null;
+ }
+ }
+
+ private void throwEglException(String function) {
+ throwEglException(function, mEgl.eglGetError());
+ }
+
+ public static void throwEglException(String function, int error) {
+ String message = formatEglError(function, error);
+ if (LOG_THREADS) {
+ Log.e("EglHelper", "throwEglException tid=" + Thread.currentThread().getId() + " "
+ + message);
+ }
+ throw new RuntimeException(message);
+ }
+
+ public static void logEglErrorAsWarning(String tag, String function, int error) {
+ Log.w(tag, formatEglError(function, error));
+ }
+
+ public static String formatEglError(String function, int error) {
+ return function + " failed: " + EGLLogWrapper.getErrorString(error);
+ }
+
+ private WeakReference<GLSurfaceView> mGLSurfaceViewWeakRef;
+ EGL10 mEgl;
+ EGLDisplay mEglDisplay;
+ EGLSurface mEglSurface;
+ EGLConfig mEglConfig;
+ EGLContext mEglContext;
+
+ }
+
+ /**
+ * A generic GL Thread. Takes care of initializing EGL and GL. Delegates
+ * to a Renderer instance to do the actual drawing. Can be configured to
+ * render continuously or on request.
+ *
+ * All potentially blocking synchronization is done through the
+ * sGLThreadManager object. This avoids multiple-lock ordering issues.
+ *
+ */
+ static class GLThread extends Thread {
+ GLThread(WeakReference<GLSurfaceView> glSurfaceViewWeakRef) {
+ super();
+ mWidth = 0;
+ mHeight = 0;
+ mRequestRender = true;
+ mRenderMode = RENDERMODE_CONTINUOUSLY;
+ mWantRenderNotification = false;
+ mGLSurfaceViewWeakRef = glSurfaceViewWeakRef;
+ }
+
+ @Override
+ public void run() {
+ setName("GLThread " + getId());
+ if (LOG_THREADS) {
+ Log.i("GLThread", "starting tid=" + getId());
+ }
+
+ try {
+ guardedRun();
+ } catch (InterruptedException e) {
+ // fall thru and exit normally
+ } finally {
+ sGLThreadManager.threadExiting(this);
+ }
+ }
+
+ /*
+ * This private method should only be called inside a
+ * synchronized(sGLThreadManager) block.
+ */
+ private void stopEglSurfaceLocked() {
+ if (mHaveEglSurface) {
+ mHaveEglSurface = false;
+ mEglHelper.destroySurface();
+ }
+ }
+
+ /*
+ * This private method should only be called inside a
+ * synchronized(sGLThreadManager) block.
+ */
+ private void stopEglContextLocked() {
+ if (mHaveEglContext) {
+ mEglHelper.finish();
+ mHaveEglContext = false;
+ sGLThreadManager.releaseEglContextLocked(this);
+ }
+ }
+ private void guardedRun() throws InterruptedException {
+ mEglHelper = new EglHelper(mGLSurfaceViewWeakRef);
+ mHaveEglContext = false;
+ mHaveEglSurface = false;
+ mWantRenderNotification = false;
+
+ try {
+ GL10 gl = null;
+ boolean createEglContext = false;
+ boolean createEglSurface = false;
+ boolean createGlInterface = false;
+ boolean lostEglContext = false;
+ boolean sizeChanged = false;
+ boolean wantRenderNotification = false;
+ boolean doRenderNotification = false;
+ boolean askedToReleaseEglContext = false;
+ int w = 0;
+ int h = 0;
+ Runnable event = null;
+ Runnable finishDrawingRunnable = null;
+
+ while (true) {
+ synchronized (sGLThreadManager) {
+ while (true) {
+ if (mShouldExit) {
+ return;
+ }
+
+ if (! mEventQueue.isEmpty()) {
+ event = mEventQueue.remove(0);
+ break;
+ }
+
+ // Update the pause state.
+ boolean pausing = false;
+ if (mPaused != mRequestPaused) {
+ pausing = mRequestPaused;
+ mPaused = mRequestPaused;
+ sGLThreadManager.notifyAll();
+ if (LOG_PAUSE_RESUME) {
+ Log.i("GLThread", "mPaused is now " + mPaused + " tid=" + getId());
+ }
+ }
+
+ // Do we need to give up the EGL context?
+ if (mShouldReleaseEglContext) {
+ if (LOG_SURFACE) {
+ Log.i("GLThread", "releasing EGL context because asked to tid=" + getId());
+ }
+ stopEglSurfaceLocked();
+ stopEglContextLocked();
+ mShouldReleaseEglContext = false;
+ askedToReleaseEglContext = true;
+ }
+
+ // Have we lost the EGL context?
+ if (lostEglContext) {
+ stopEglSurfaceLocked();
+ stopEglContextLocked();
+ lostEglContext = false;
+ }
+
+ // When pausing, release the EGL surface:
+ if (pausing && mHaveEglSurface) {
+ if (LOG_SURFACE) {
+ Log.i("GLThread", "releasing EGL surface because paused tid=" + getId());
+ }
+ stopEglSurfaceLocked();
+ }
+
+ // When pausing, optionally release the EGL Context:
+ if (pausing && mHaveEglContext) {
+ GLSurfaceView view = mGLSurfaceViewWeakRef.get();
+ boolean preserveEglContextOnPause = view == null ?
+ false : view.mPreserveEGLContextOnPause;
+ if (!preserveEglContextOnPause) {
+ stopEglContextLocked();
+ if (LOG_SURFACE) {
+ Log.i("GLThread", "releasing EGL context because paused tid=" + getId());
+ }
+ }
+ }
+
+ // Have we lost the SurfaceView surface?
+ if ((! mHasSurface) && (! mWaitingForSurface)) {
+ if (LOG_SURFACE) {
+ Log.i("GLThread", "noticed surfaceView surface lost tid=" + getId());
+ }
+ if (mHaveEglSurface) {
+ stopEglSurfaceLocked();
+ }
+ mWaitingForSurface = true;
+ mSurfaceIsBad = false;
+ sGLThreadManager.notifyAll();
+ }
+
+ // Have we acquired the surface view surface?
+ if (mHasSurface && mWaitingForSurface) {
+ if (LOG_SURFACE) {
+ Log.i("GLThread", "noticed surfaceView surface acquired tid=" + getId());
+ }
+ mWaitingForSurface = false;
+ sGLThreadManager.notifyAll();
+ }
+
+ if (doRenderNotification) {
+ if (LOG_SURFACE) {
+ Log.i("GLThread", "sending render notification tid=" + getId());
+ }
+ mWantRenderNotification = false;
+ doRenderNotification = false;
+ mRenderComplete = true;
+ sGLThreadManager.notifyAll();
+ }
+
+ if (mFinishDrawingRunnable != null) {
+ finishDrawingRunnable = mFinishDrawingRunnable;
+ mFinishDrawingRunnable = null;
+ }
+
+ // Ready to draw?
+ if (readyToDraw()) {
+
+ // If we don't have an EGL context, try to acquire one.
+ if (! mHaveEglContext) {
+ if (askedToReleaseEglContext) {
+ askedToReleaseEglContext = false;
+ } else {
+ try {
+ mEglHelper.start();
+ } catch (RuntimeException t) {
+ sGLThreadManager.releaseEglContextLocked(this);
+ throw t;
+ }
+ mHaveEglContext = true;
+ createEglContext = true;
+
+ sGLThreadManager.notifyAll();
+ }
+ }
+
+ if (mHaveEglContext && !mHaveEglSurface) {
+ mHaveEglSurface = true;
+ createEglSurface = true;
+ createGlInterface = true;
+ sizeChanged = true;
+ }
+
+ if (mHaveEglSurface) {
+ if (mSizeChanged) {
+ sizeChanged = true;
+ w = mWidth;
+ h = mHeight;
+ mWantRenderNotification = true;
+ if (LOG_SURFACE) {
+ Log.i("GLThread",
+ "noticing that we want render notification tid="
+ + getId());
+ }
+
+ // Destroy and recreate the EGL surface.
+ createEglSurface = true;
+
+ mSizeChanged = false;
+ }
+ mRequestRender = false;
+ sGLThreadManager.notifyAll();
+ if (mWantRenderNotification) {
+ wantRenderNotification = true;
+ }
+ break;
+ }
+ } else {
+ if (finishDrawingRunnable != null) {
+ Log.w(TAG, "Warning, !readyToDraw() but waiting for " +
+ "draw finished! Early reporting draw finished.");
+ finishDrawingRunnable.run();
+ finishDrawingRunnable = null;
+ }
+ }
+ // By design, this is the only place in a GLThread thread where we wait().
+ if (LOG_THREADS) {
+ Log.i("GLThread", "waiting tid=" + getId()
+ + " mHaveEglContext: " + mHaveEglContext
+ + " mHaveEglSurface: " + mHaveEglSurface
+ + " mFinishedCreatingEglSurface: " + mFinishedCreatingEglSurface
+ + " mPaused: " + mPaused
+ + " mHasSurface: " + mHasSurface
+ + " mSurfaceIsBad: " + mSurfaceIsBad
+ + " mWaitingForSurface: " + mWaitingForSurface
+ + " mWidth: " + mWidth
+ + " mHeight: " + mHeight
+ + " mRequestRender: " + mRequestRender
+ + " mRenderMode: " + mRenderMode);
+ }
+ sGLThreadManager.wait();
+ }
+ } // end of synchronized(sGLThreadManager)
+
+ if (event != null) {
+ event.run();
+ event = null;
+ continue;
+ }
+
+ if (createEglSurface) {
+ if (LOG_SURFACE) {
+ Log.w("GLThread", "egl createSurface");
+ }
+ if (mEglHelper.createSurface()) {
+ synchronized(sGLThreadManager) {
+ mFinishedCreatingEglSurface = true;
+ sGLThreadManager.notifyAll();
+ }
+ } else {
+ synchronized(sGLThreadManager) {
+ mFinishedCreatingEglSurface = true;
+ mSurfaceIsBad = true;
+ sGLThreadManager.notifyAll();
+ }
+ continue;
+ }
+ createEglSurface = false;
+ }
+
+ if (createGlInterface) {
+ gl = (GL10) mEglHelper.createGL();
+
+ createGlInterface = false;
+ }
+
+ // -- GODOT start --
+ if (createEglContext) {
+ if (LOG_RENDERER) {
+ Log.w("GLThread", "onSurfaceCreated");
+ }
+ GLSurfaceView view = mGLSurfaceViewWeakRef.get();
+ if (view != null) {
+ try {
+ view.mRenderer.onSurfaceCreated(gl, mEglHelper.mEglConfig);
+ } finally {
+ }
+ }
+ createEglContext = false;
+ }
+
+ if (sizeChanged) {
+ if (LOG_RENDERER) {
+ Log.w("GLThread", "onSurfaceChanged(" + w + ", " + h + ")");
+ }
+ GLSurfaceView view = mGLSurfaceViewWeakRef.get();
+ if (view != null) {
+ try {
+ view.mRenderer.onSurfaceChanged(gl, w, h);
+ } finally {
+ }
+ }
+ sizeChanged = false;
+ }
+
+ boolean swapBuffers = false;
+ if (LOG_RENDERER_DRAW_FRAME) {
+ Log.w("GLThread", "onDrawFrame tid=" + getId());
+ }
+ {
+ GLSurfaceView view = mGLSurfaceViewWeakRef.get();
+ if (view != null) {
+ try {
+ swapBuffers = view.mRenderer.onDrawFrame(gl);
+ if (finishDrawingRunnable != null) {
+ finishDrawingRunnable.run();
+ finishDrawingRunnable = null;
+ }
+ } finally {}
+ }
+ }
+ if (swapBuffers) {
+ int swapError = mEglHelper.swap();
+ switch (swapError) {
+ case EGL10.EGL_SUCCESS:
+ break;
+ case EGL11.EGL_CONTEXT_LOST:
+ if (LOG_SURFACE) {
+ Log.i("GLThread", "egl context lost tid=" + getId());
+ }
+ lostEglContext = true;
+ break;
+ default:
+ // Other errors typically mean that the current surface is bad,
+ // probably because the SurfaceView surface has been destroyed,
+ // but we haven't been notified yet.
+ // Log the error to help developers understand why rendering stopped.
+ EglHelper.logEglErrorAsWarning("GLThread", "eglSwapBuffers", swapError);
+
+ synchronized (sGLThreadManager) {
+ mSurfaceIsBad = true;
+ sGLThreadManager.notifyAll();
+ }
+ break;
+ }
+ }
+ // -- GODOT end --
+
+ if (wantRenderNotification) {
+ doRenderNotification = true;
+ wantRenderNotification = false;
+ }
+ }
+
+ } finally {
+ /*
+ * clean-up everything...
+ */
+ synchronized (sGLThreadManager) {
+ stopEglSurfaceLocked();
+ stopEglContextLocked();
+ }
+ }
+ }
+
+ public boolean ableToDraw() {
+ return mHaveEglContext && mHaveEglSurface && readyToDraw();
+ }
+
+ private boolean readyToDraw() {
+ return (!mPaused) && mHasSurface && (!mSurfaceIsBad)
+ && (mWidth > 0) && (mHeight > 0)
+ && (mRequestRender || (mRenderMode == RENDERMODE_CONTINUOUSLY));
+ }
+
+ public void setRenderMode(int renderMode) {
+ if ( !((RENDERMODE_WHEN_DIRTY <= renderMode) && (renderMode <= RENDERMODE_CONTINUOUSLY)) ) {
+ throw new IllegalArgumentException("renderMode");
+ }
+ synchronized(sGLThreadManager) {
+ mRenderMode = renderMode;
+ sGLThreadManager.notifyAll();
+ }
+ }
+
+ public int getRenderMode() {
+ synchronized(sGLThreadManager) {
+ return mRenderMode;
+ }
+ }
+
+ public void requestRender() {
+ synchronized(sGLThreadManager) {
+ mRequestRender = true;
+ sGLThreadManager.notifyAll();
+ }
+ }
+
+ public void requestRenderAndNotify(Runnable finishDrawing) {
+ synchronized(sGLThreadManager) {
+ // If we are already on the GL thread, this means a client callback
+ // has caused reentrancy, for example via updating the SurfaceView parameters.
+ // We will return to the client rendering code, so here we don't need to
+ // do anything.
+ if (Thread.currentThread() == this) {
+ return;
+ }
+
+ mWantRenderNotification = true;
+ mRequestRender = true;
+ mRenderComplete = false;
+ mFinishDrawingRunnable = finishDrawing;
+
+ sGLThreadManager.notifyAll();
+ }
+ }
+
+ public void surfaceCreated() {
+ synchronized(sGLThreadManager) {
+ if (LOG_THREADS) {
+ Log.i("GLThread", "surfaceCreated tid=" + getId());
+ }
+ mHasSurface = true;
+ mFinishedCreatingEglSurface = false;
+ sGLThreadManager.notifyAll();
+ while (mWaitingForSurface
+ && !mFinishedCreatingEglSurface
+ && !mExited) {
+ try {
+ sGLThreadManager.wait();
+ } catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ }
+ }
+ }
+ }
+
+ public void surfaceDestroyed() {
+ synchronized(sGLThreadManager) {
+ if (LOG_THREADS) {
+ Log.i("GLThread", "surfaceDestroyed tid=" + getId());
+ }
+ mHasSurface = false;
+ sGLThreadManager.notifyAll();
+ while((!mWaitingForSurface) && (!mExited)) {
+ try {
+ sGLThreadManager.wait();
+ } catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ }
+ }
+ }
+ }
+
+ public void onPause() {
+ synchronized (sGLThreadManager) {
+ if (LOG_PAUSE_RESUME) {
+ Log.i("GLThread", "onPause tid=" + getId());
+ }
+ mRequestPaused = true;
+ sGLThreadManager.notifyAll();
+ while ((! mExited) && (! mPaused)) {
+ if (LOG_PAUSE_RESUME) {
+ Log.i("Main thread", "onPause waiting for mPaused.");
+ }
+ try {
+ sGLThreadManager.wait();
+ } catch (InterruptedException ex) {
+ Thread.currentThread().interrupt();
+ }
+ }
+ }
+ }
+
+ public void onResume() {
+ synchronized (sGLThreadManager) {
+ if (LOG_PAUSE_RESUME) {
+ Log.i("GLThread", "onResume tid=" + getId());
+ }
+ mRequestPaused = false;
+ mRequestRender = true;
+ mRenderComplete = false;
+ sGLThreadManager.notifyAll();
+ while ((! mExited) && mPaused && (!mRenderComplete)) {
+ if (LOG_PAUSE_RESUME) {
+ Log.i("Main thread", "onResume waiting for !mPaused.");
+ }
+ try {
+ sGLThreadManager.wait();
+ } catch (InterruptedException ex) {
+ Thread.currentThread().interrupt();
+ }
+ }
+ }
+ }
+
+ public void onWindowResize(int w, int h) {
+ synchronized (sGLThreadManager) {
+ mWidth = w;
+ mHeight = h;
+ mSizeChanged = true;
+ mRequestRender = true;
+ mRenderComplete = false;
+
+ // If we are already on the GL thread, this means a client callback
+ // has caused reentrancy, for example via updating the SurfaceView parameters.
+ // We need to process the size change eventually though and update our EGLSurface.
+ // So we set the parameters and return so they can be processed on our
+ // next iteration.
+ if (Thread.currentThread() == this) {
+ return;
+ }
+
+ sGLThreadManager.notifyAll();
+
+ // Wait for thread to react to resize and render a frame
+ while (! mExited && !mPaused && !mRenderComplete
+ && ableToDraw()) {
+ if (LOG_SURFACE) {
+ Log.i("Main thread", "onWindowResize waiting for render complete from tid=" + getId());
+ }
+ try {
+ sGLThreadManager.wait();
+ } catch (InterruptedException ex) {
+ Thread.currentThread().interrupt();
+ }
+ }
+ }
+ }
+
+ public void requestExitAndWait() {
+ // don't call this from GLThread thread or it is a guaranteed
+ // deadlock!
+ synchronized(sGLThreadManager) {
+ mShouldExit = true;
+ sGLThreadManager.notifyAll();
+ while (! mExited) {
+ try {
+ sGLThreadManager.wait();
+ } catch (InterruptedException ex) {
+ Thread.currentThread().interrupt();
+ }
+ }
+ }
+ }
+
+ public void requestReleaseEglContextLocked() {
+ mShouldReleaseEglContext = true;
+ sGLThreadManager.notifyAll();
+ }
+
+ /**
+ * Queue an "event" to be run on the GL rendering thread.
+ * @param r the runnable to be run on the GL rendering thread.
+ */
+ public void queueEvent(Runnable r) {
+ if (r == null) {
+ throw new IllegalArgumentException("r must not be null");
+ }
+ synchronized(sGLThreadManager) {
+ mEventQueue.add(r);
+ sGLThreadManager.notifyAll();
+ }
+ }
+
+ // Once the thread is started, all accesses to the following member
+ // variables are protected by the sGLThreadManager monitor
+ private boolean mShouldExit;
+ private boolean mExited;
+ private boolean mRequestPaused;
+ private boolean mPaused;
+ private boolean mHasSurface;
+ private boolean mSurfaceIsBad;
+ private boolean mWaitingForSurface;
+ private boolean mHaveEglContext;
+ private boolean mHaveEglSurface;
+ private boolean mFinishedCreatingEglSurface;
+ private boolean mShouldReleaseEglContext;
+ private int mWidth;
+ private int mHeight;
+ private int mRenderMode;
+ private boolean mRequestRender;
+ private boolean mWantRenderNotification;
+ private boolean mRenderComplete;
+ private ArrayList<Runnable> mEventQueue = new ArrayList<Runnable>();
+ private boolean mSizeChanged = true;
+ private Runnable mFinishDrawingRunnable = null;
+
+ // End of member variables protected by the sGLThreadManager monitor.
+
+ private EglHelper mEglHelper;
+
+ /**
+ * Set once at thread construction time, nulled out when the parent view is garbage
+ * called. This weak reference allows the GLSurfaceView to be garbage collected while
+ * the GLThread is still alive.
+ */
+ private WeakReference<GLSurfaceView> mGLSurfaceViewWeakRef;
+
+ }
+
+ static class LogWriter extends Writer {
+
+ @Override public void close() {
+ flushBuilder();
+ }
+
+ @Override public void flush() {
+ flushBuilder();
+ }
+
+ @Override public void write(char[] buf, int offset, int count) {
+ for(int i = 0; i < count; i++) {
+ char c = buf[offset + i];
+ if ( c == '\n') {
+ flushBuilder();
+ }
+ else {
+ mBuilder.append(c);
+ }
+ }
+ }
+
+ private void flushBuilder() {
+ if (mBuilder.length() > 0) {
+ Log.v("GLSurfaceView", mBuilder.toString());
+ mBuilder.delete(0, mBuilder.length());
+ }
+ }
+
+ private StringBuilder mBuilder = new StringBuilder();
+ }
+
+
+ private void checkRenderThreadState() {
+ if (mGLThread != null) {
+ throw new IllegalStateException(
+ "setRenderer has already been called for this instance.");
+ }
+ }
+
+ private static class GLThreadManager {
+ private static String TAG = "GLThreadManager";
+
+ public synchronized void threadExiting(GLThread thread) {
+ if (LOG_THREADS) {
+ Log.i("GLThread", "exiting tid=" + thread.getId());
+ }
+ thread.mExited = true;
+ notifyAll();
+ }
+
+ /*
+ * Releases the EGL context. Requires that we are already in the
+ * sGLThreadManager monitor when this is called.
+ */
+ public void releaseEglContextLocked(GLThread thread) {
+ notifyAll();
+ }
+ }
+
+ private static final GLThreadManager sGLThreadManager = new GLThreadManager();
+
+ private final WeakReference<GLSurfaceView> mThisWeakRef =
+ new WeakReference<GLSurfaceView>(this);
+ private GLThread mGLThread;
+ private Renderer mRenderer;
+ private boolean mDetached;
+ private EGLConfigChooser mEGLConfigChooser;
+ private EGLContextFactory mEGLContextFactory;
+ private EGLWindowSurfaceFactory mEGLWindowSurfaceFactory;
+ private GLWrapper mGLWrapper;
+ private int mDebugFlags;
+ private int mEGLContextClientVersion;
+ private boolean mPreserveEGLContextOnPause;
+}
+
diff --git a/platform/android/java/lib/src/org/godotengine/godot/GodotRenderer.java b/platform/android/java/lib/src/org/godotengine/godot/gl/GodotRenderer.java
index e3956ac459..5c4fd00f6d 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/GodotRenderer.java
+++ b/platform/android/java/lib/src/org/godotengine/godot/gl/GodotRenderer.java
@@ -28,38 +28,38 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-package org.godotengine.godot;
+package org.godotengine.godot.gl;
+import org.godotengine.godot.GodotLib;
import org.godotengine.godot.plugin.GodotPlugin;
import org.godotengine.godot.plugin.GodotPluginRegistry;
-import org.godotengine.godot.utils.GLUtils;
-
-import android.opengl.GLSurfaceView;
import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;
/**
- * Godot's renderer implementation.
+ * Godot's GL renderer implementation.
*/
-class GodotRenderer implements GLSurfaceView.Renderer {
+public class GodotRenderer implements GLSurfaceView.Renderer {
private final GodotPluginRegistry pluginRegistry;
private boolean activityJustResumed = false;
- GodotRenderer() {
+ public GodotRenderer() {
this.pluginRegistry = GodotPluginRegistry.getPluginRegistry();
}
- public void onDrawFrame(GL10 gl) {
+ public boolean onDrawFrame(GL10 gl) {
if (activityJustResumed) {
GodotLib.onRendererResumed();
activityJustResumed = false;
}
- GodotLib.step();
+ boolean swapBuffers = GodotLib.step();
for (GodotPlugin plugin : pluginRegistry.getAllPlugins()) {
plugin.onGLDrawFrame(gl);
}
+
+ return swapBuffers;
}
public void onSurfaceChanged(GL10 gl, int width, int height) {
@@ -76,13 +76,13 @@ class GodotRenderer implements GLSurfaceView.Renderer {
}
}
- void onActivityResumed() {
+ public void onActivityResumed() {
// We defer invoking GodotLib.onRendererResumed() until the first draw frame call.
// This ensures we have a valid GL context and surface when we do so.
activityJustResumed = true;
}
- void onActivityPaused() {
+ public void onActivityPaused() {
GodotLib.onRendererPaused();
}
}
diff --git a/platform/android/java/lib/src/org/godotengine/godot/xr/ovr/OvrConfigChooser.java b/platform/android/java/lib/src/org/godotengine/godot/xr/ovr/OvrConfigChooser.java
index 4c1c84affb..e35d4f5828 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/xr/ovr/OvrConfigChooser.java
+++ b/platform/android/java/lib/src/org/godotengine/godot/xr/ovr/OvrConfigChooser.java
@@ -30,8 +30,9 @@
package org.godotengine.godot.xr.ovr;
+import org.godotengine.godot.gl.GLSurfaceView;
+
import android.opengl.EGLExt;
-import android.opengl.GLSurfaceView;
import javax.microedition.khronos.egl.EGL10;
import javax.microedition.khronos.egl.EGLConfig;
diff --git a/platform/android/java/lib/src/org/godotengine/godot/xr/ovr/OvrContextFactory.java b/platform/android/java/lib/src/org/godotengine/godot/xr/ovr/OvrContextFactory.java
index 2b4369b8a6..deb9c4bb1d 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/xr/ovr/OvrContextFactory.java
+++ b/platform/android/java/lib/src/org/godotengine/godot/xr/ovr/OvrContextFactory.java
@@ -30,8 +30,9 @@
package org.godotengine.godot.xr.ovr;
+import org.godotengine.godot.gl.GLSurfaceView;
+
import android.opengl.EGL14;
-import android.opengl.GLSurfaceView;
import javax.microedition.khronos.egl.EGL10;
import javax.microedition.khronos.egl.EGLConfig;
diff --git a/platform/android/java/lib/src/org/godotengine/godot/xr/ovr/OvrWindowSurfaceFactory.java b/platform/android/java/lib/src/org/godotengine/godot/xr/ovr/OvrWindowSurfaceFactory.java
index fbfe0a3a75..f087b7dc74 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/xr/ovr/OvrWindowSurfaceFactory.java
+++ b/platform/android/java/lib/src/org/godotengine/godot/xr/ovr/OvrWindowSurfaceFactory.java
@@ -30,7 +30,7 @@
package org.godotengine.godot.xr.ovr;
-import android.opengl.GLSurfaceView;
+import org.godotengine.godot.gl.GLSurfaceView;
import javax.microedition.khronos.egl.EGL10;
import javax.microedition.khronos.egl.EGLConfig;
diff --git a/platform/android/java/lib/src/org/godotengine/godot/xr/regular/RegularConfigChooser.java b/platform/android/java/lib/src/org/godotengine/godot/xr/regular/RegularConfigChooser.java
index 9fde1961ea..445238b1c2 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/xr/regular/RegularConfigChooser.java
+++ b/platform/android/java/lib/src/org/godotengine/godot/xr/regular/RegularConfigChooser.java
@@ -30,10 +30,9 @@
package org.godotengine.godot.xr.regular;
+import org.godotengine.godot.gl.GLSurfaceView;
import org.godotengine.godot.utils.GLUtils;
-import android.opengl.GLSurfaceView;
-
import javax.microedition.khronos.egl.EGL10;
import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.egl.EGLDisplay;
diff --git a/platform/android/java/lib/src/org/godotengine/godot/xr/regular/RegularContextFactory.java b/platform/android/java/lib/src/org/godotengine/godot/xr/regular/RegularContextFactory.java
index ce1184a75c..5d62723170 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/xr/regular/RegularContextFactory.java
+++ b/platform/android/java/lib/src/org/godotengine/godot/xr/regular/RegularContextFactory.java
@@ -30,9 +30,9 @@
package org.godotengine.godot.xr.regular;
+import org.godotengine.godot.gl.GLSurfaceView;
import org.godotengine.godot.utils.GLUtils;
-import android.opengl.GLSurfaceView;
import android.util.Log;
import javax.microedition.khronos.egl.EGL10;
diff --git a/platform/android/java/lib/src/org/godotengine/godot/xr/regular/RegularFallbackConfigChooser.java b/platform/android/java/lib/src/org/godotengine/godot/xr/regular/RegularFallbackConfigChooser.java
index 420dda45a0..68329c5c49 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/xr/regular/RegularFallbackConfigChooser.java
+++ b/platform/android/java/lib/src/org/godotengine/godot/xr/regular/RegularFallbackConfigChooser.java
@@ -30,8 +30,6 @@
package org.godotengine.godot.xr.regular;
-import org.godotengine.godot.utils.GLUtils;
-
import android.util.Log;
import javax.microedition.khronos.egl.EGL10;
diff --git a/platform/android/java/nativeSrcsConfigs/CMakeLists.txt b/platform/android/java/nativeSrcsConfigs/CMakeLists.txt
index 966c02f7d7..711f7cd502 100644
--- a/platform/android/java/nativeSrcsConfigs/CMakeLists.txt
+++ b/platform/android/java/nativeSrcsConfigs/CMakeLists.txt
@@ -16,3 +16,5 @@ add_executable(${PROJECT_NAME} ${SOURCES} ${HEADERS})
target_include_directories(${PROJECT_NAME}
SYSTEM PUBLIC
${GODOT_ROOT_DIR})
+
+add_definitions(-DUNIX_ENABLED -DVULKAN_ENABLED -DANDROID_ENABLED)
diff --git a/platform/android/java/nativeSrcsConfigs/build.gradle b/platform/android/java/nativeSrcsConfigs/build.gradle
index 158bb2b98e..0cb769b539 100644
--- a/platform/android/java/nativeSrcsConfigs/build.gradle
+++ b/platform/android/java/nativeSrcsConfigs/build.gradle
@@ -6,6 +6,7 @@ plugins {
android {
compileSdkVersion versions.compileSdk
buildToolsVersion versions.buildTools
+ ndkVersion versions.ndkVersion
defaultConfig {
minSdkVersion versions.minSdk
@@ -28,8 +29,6 @@ android {
}
}
- ndkVersion versions.ndkVersion
-
externalNativeBuild {
cmake {
path "CMakeLists.txt"
diff --git a/platform/android/java/scripts/publish-module.gradle b/platform/android/java/scripts/publish-module.gradle
index 6b2aea5caf..32b749e493 100644
--- a/platform/android/java/scripts/publish-module.gradle
+++ b/platform/android/java/scripts/publish-module.gradle
@@ -7,20 +7,15 @@ version = PUBLISH_VERSION
afterEvaluate {
publishing {
publications {
- release(MavenPublication) {
+ templateRelease(MavenPublication) {
+ from components.templateRelease
+
// The coordinates of the library, being set from variables that
// we'll set up later
groupId ossrhGroupId
artifactId PUBLISH_ARTIFACT_ID
version PUBLISH_VERSION
- // Two artifacts, the `aar` (or `jar`) and the sources
- if (project.plugins.findPlugin("com.android.library")) {
- from components.release
- } else {
- from components.java
- }
-
// Mostly self-explanatory metadata
pom {
name = PUBLISH_ARTIFACT_ID
diff --git a/platform/android/java/settings.gradle b/platform/android/java/settings.gradle
index 584b626900..56e1b6fd3a 100644
--- a/platform/android/java/settings.gradle
+++ b/platform/android/java/settings.gradle
@@ -4,6 +4,7 @@ rootProject.name = "Godot"
include ':app'
include ':lib'
include ':nativeSrcsConfigs'
+include ':editor'
include ':assetPacks:installTime'
project(':assetPacks:installTime').projectDir = file("app/assetPacks/installTime")
diff --git a/platform/android/java_godot_io_wrapper.cpp b/platform/android/java_godot_io_wrapper.cpp
index d6e3ad90b1..5b21e696c3 100644
--- a/platform/android/java_godot_io_wrapper.cpp
+++ b/platform/android/java_godot_io_wrapper.cpp
@@ -54,6 +54,7 @@ GodotIOJavaWrapper::GodotIOJavaWrapper(JNIEnv *p_env, jobject p_godot_io_instanc
_get_locale = p_env->GetMethodID(cls, "getLocale", "()Ljava/lang/String;");
_get_model = p_env->GetMethodID(cls, "getModel", "()Ljava/lang/String;");
_get_screen_DPI = p_env->GetMethodID(cls, "getScreenDPI", "()I");
+ _get_scaled_density = p_env->GetMethodID(cls, "getScaledDensity", "()F");
_get_screen_refresh_rate = p_env->GetMethodID(cls, "getScreenRefreshRate", "(D)D");
_screen_get_usable_rect = p_env->GetMethodID(cls, "screenGetUsableRect", "()[I"),
_get_unique_id = p_env->GetMethodID(cls, "getUniqueID", "()Ljava/lang/String;");
@@ -138,6 +139,16 @@ int GodotIOJavaWrapper::get_screen_dpi() {
}
}
+float GodotIOJavaWrapper::get_scaled_density() {
+ if (_get_scaled_density) {
+ JNIEnv *env = get_jni_env();
+ ERR_FAIL_COND_V(env == nullptr, 1.0f);
+ return env->CallFloatMethod(godot_io_instance, _get_scaled_density);
+ } else {
+ return 1.0f;
+ }
+}
+
float GodotIOJavaWrapper::get_screen_refresh_rate(float fallback) {
if (_get_screen_refresh_rate) {
JNIEnv *env = get_jni_env();
diff --git a/platform/android/java_godot_io_wrapper.h b/platform/android/java_godot_io_wrapper.h
index 38a2b710a9..08e3092afd 100644
--- a/platform/android/java_godot_io_wrapper.h
+++ b/platform/android/java_godot_io_wrapper.h
@@ -51,6 +51,7 @@ private:
jmethodID _get_locale = 0;
jmethodID _get_model = 0;
jmethodID _get_screen_DPI = 0;
+ jmethodID _get_scaled_density = 0;
jmethodID _get_screen_refresh_rate = 0;
jmethodID _screen_get_usable_rect = 0;
jmethodID _get_unique_id = 0;
@@ -72,6 +73,7 @@ public:
String get_locale();
String get_model();
int get_screen_dpi();
+ float get_scaled_density();
float get_screen_refresh_rate(float fallback);
void screen_get_usable_rect(int (&p_rect_xywh)[4]);
String get_unique_id();
diff --git a/platform/android/java_godot_lib_jni.cpp b/platform/android/java_godot_lib_jni.cpp
index 249717921f..ea72bc0e15 100644
--- a/platform/android/java_godot_lib_jni.cpp
+++ b/platform/android/java_godot_lib_jni.cpp
@@ -107,6 +107,9 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_initialize(JNIEnv *en
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_ondestroy(JNIEnv *env, jclass clazz) {
// lets cleanup
+ if (java_class_wrapper) {
+ memdelete(java_class_wrapper);
+ }
if (godot_io_java) {
delete godot_io_java;
}
@@ -117,6 +120,7 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_ondestroy(JNIEnv *env
delete input_handler;
}
if (os_android) {
+ os_android->main_loop_end();
delete os_android;
}
}
@@ -146,7 +150,7 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_setup(JNIEnv *env, jc
}
}
- Error err = Main::setup("apk", cmdlen, (char **)cmdline, false);
+ Error err = Main::setup(OS_Android::ANDROID_EXEC_PATH, cmdlen, (char **)cmdline, false);
if (cmdline) {
if (j_cmdline) {
for (int i = 0; i < cmdlen; ++i) {
@@ -209,9 +213,9 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_back(JNIEnv *env, jcl
}
}
-JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_step(JNIEnv *env, jclass clazz) {
+JNIEXPORT jboolean JNICALL Java_org_godotengine_godot_GodotLib_step(JNIEnv *env, jclass clazz) {
if (step.get() == -1) {
- return;
+ return true;
}
if (step.get() == 0) {
@@ -220,12 +224,12 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_step(JNIEnv *env, jcl
Main::setup2(Thread::get_caller_id());
input_handler = new AndroidInputHandler();
step.increment();
- return;
+ return true;
}
if (step.get() == 1) {
if (!Main::start()) {
- return; // should exit instead and print the error
+ return true; // should exit instead and print the error
}
godot_java->on_godot_setup_completed(env);
@@ -239,9 +243,12 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_step(JNIEnv *env, jcl
DisplayServerAndroid::get_singleton()->process_magnetometer(magnetometer);
DisplayServerAndroid::get_singleton()->process_gyroscope(gyroscope);
- if (os_android->main_loop_iterate()) {
+ bool should_swap_buffers = false;
+ if (os_android->main_loop_iterate(&should_swap_buffers)) {
godot_java->force_quit(env);
}
+
+ return should_swap_buffers;
}
void touch_preprocessing(JNIEnv *env, jclass clazz, jint input_device, jint ev, jint pointer, jint pointer_count, jfloatArray positions, jint buttons_mask, jfloat vertical_factor, jfloat horizontal_factor) {
diff --git a/platform/android/java_godot_lib_jni.h b/platform/android/java_godot_lib_jni.h
index 927b44ddb6..e686ee5c09 100644
--- a/platform/android/java_godot_lib_jni.h
+++ b/platform/android/java_godot_lib_jni.h
@@ -42,7 +42,7 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_ondestroy(JNIEnv *env
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_setup(JNIEnv *env, jclass clazz, jobjectArray p_cmdline);
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_resize(JNIEnv *env, jclass clazz, jobject p_surface, jint p_width, jint p_height);
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_newcontext(JNIEnv *env, jclass clazz, jobject p_surface);
-JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_step(JNIEnv *env, jclass clazz);
+JNIEXPORT jboolean JNICALL Java_org_godotengine_godot_GodotLib_step(JNIEnv *env, jclass clazz);
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_back(JNIEnv *env, jclass clazz);
void touch_preprocessing(JNIEnv *env, jclass clazz, jint input_device, jint ev, jint pointer, jint pointer_count, jfloatArray positions, jint buttons_mask = 0, jfloat vertical_factor = 0, jfloat horizontal_factor = 0);
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_touch__IIII_3F(JNIEnv *env, jclass clazz, jint input_device, jint ev, jint pointer, jint pointer_count, jfloatArray positions);
diff --git a/platform/android/java_godot_wrapper.cpp b/platform/android/java_godot_wrapper.cpp
index 754267c834..2c8378e685 100644
--- a/platform/android/java_godot_wrapper.cpp
+++ b/platform/android/java_godot_wrapper.cpp
@@ -77,6 +77,7 @@ GodotJavaWrapper::GodotJavaWrapper(JNIEnv *p_env, jobject p_activity, jobject p_
_get_input_fallback_mapping = p_env->GetMethodID(godot_class, "getInputFallbackMapping", "()Ljava/lang/String;");
_on_godot_setup_completed = p_env->GetMethodID(godot_class, "onGodotSetupCompleted", "()V");
_on_godot_main_loop_started = p_env->GetMethodID(godot_class, "onGodotMainLoopStarted", "()V");
+ _create_new_godot_instance = p_env->GetMethodID(godot_class, "createNewGodotInstance", "([Ljava/lang/String;)V");
// get some Activity method pointers...
_get_class_loader = p_env->GetMethodID(activity_class, "getClassLoader", "()Ljava/lang/ClassLoader;");
@@ -351,3 +352,16 @@ void GodotJavaWrapper::vibrate(int p_duration_ms) {
env->CallVoidMethod(godot_instance, _vibrate, p_duration_ms);
}
}
+
+void GodotJavaWrapper::create_new_godot_instance(List<String> args) {
+ if (_create_new_godot_instance) {
+ JNIEnv *env = get_jni_env();
+ ERR_FAIL_COND(env == nullptr);
+
+ jobjectArray jargs = env->NewObjectArray(args.size(), env->FindClass("java/lang/String"), env->NewStringUTF(""));
+ for (int i = 0; i < args.size(); i++) {
+ env->SetObjectArrayElement(jargs, i, env->NewStringUTF(args[i].utf8().get_data()));
+ }
+ env->CallVoidMethod(godot_instance, _create_new_godot_instance, jargs);
+ }
+}
diff --git a/platform/android/java_godot_wrapper.h b/platform/android/java_godot_wrapper.h
index 42ae91480f..f04fda7c3d 100644
--- a/platform/android/java_godot_wrapper.h
+++ b/platform/android/java_godot_wrapper.h
@@ -37,6 +37,7 @@
#include <android/log.h>
#include <jni.h>
+#include "core/templates/list.h"
#include "java_godot_view_wrapper.h"
#include "string_android.h"
@@ -70,6 +71,7 @@ private:
jmethodID _on_godot_setup_completed = 0;
jmethodID _on_godot_main_loop_started = 0;
jmethodID _get_class_loader = 0;
+ jmethodID _create_new_godot_instance = 0;
public:
GodotJavaWrapper(JNIEnv *p_env, jobject p_activity, jobject p_godot_instance);
@@ -103,6 +105,7 @@ public:
bool is_activity_resumed();
void vibrate(int p_duration_ms);
String get_input_fallback_mapping();
+ void create_new_godot_instance(List<String> args);
};
#endif /* !JAVA_GODOT_WRAPPER_H */
diff --git a/platform/android/os_android.cpp b/platform/android/os_android.cpp
index b17b0f3139..ef53415f16 100644
--- a/platform/android/os_android.cpp
+++ b/platform/android/os_android.cpp
@@ -35,6 +35,8 @@
#include "drivers/unix/file_access_unix.h"
#include "main/main.h"
#include "platform/android/display_server_android.h"
+#include "scene/main/scene_tree.h"
+#include "servers/rendering_server.h"
#include "dir_access_jandroid.h"
#include "file_access_android.h"
@@ -45,6 +47,8 @@
#include "java_godot_io_wrapper.h"
#include "java_godot_wrapper.h"
+const char *OS_Android::ANDROID_EXEC_PATH = "apk";
+
String _remove_symlink(const String &dir) {
// Workaround for Android 6.0+ using a symlink.
// Save the current directory.
@@ -81,18 +85,28 @@ void OS_Android::alert(const String &p_alert, const String &p_title) {
void OS_Android::initialize_core() {
OS_Unix::initialize_core();
+#ifdef TOOLS_ENABLED
+ FileAccess::make_default<FileAccessUnix>(FileAccess::ACCESS_RESOURCES);
+#else
if (use_apk_expansion) {
FileAccess::make_default<FileAccessUnix>(FileAccess::ACCESS_RESOURCES);
} else {
FileAccess::make_default<FileAccessAndroid>(FileAccess::ACCESS_RESOURCES);
}
+#endif
FileAccess::make_default<FileAccessUnix>(FileAccess::ACCESS_USERDATA);
FileAccess::make_default<FileAccessUnix>(FileAccess::ACCESS_FILESYSTEM);
+
+#ifdef TOOLS_ENABLED
+ DirAccess::make_default<DirAccessUnix>(DirAccess::ACCESS_RESOURCES);
+#else
if (use_apk_expansion) {
DirAccess::make_default<DirAccessUnix>(DirAccess::ACCESS_RESOURCES);
} else {
DirAccess::make_default<DirAccessJAndroid>(DirAccess::ACCESS_RESOURCES);
}
+#endif
+
DirAccess::make_default<DirAccessUnix>(DirAccess::ACCESS_USERDATA);
DirAccess::make_default<DirAccessUnix>(DirAccess::ACCESS_FILESYSTEM);
@@ -168,16 +182,26 @@ void OS_Android::main_loop_begin() {
}
}
-bool OS_Android::main_loop_iterate() {
+bool OS_Android::main_loop_iterate(bool *r_should_swap_buffers) {
if (!main_loop) {
return false;
}
DisplayServerAndroid::get_singleton()->process_events();
- return Main::iteration();
+ bool exit = Main::iteration();
+
+ if (r_should_swap_buffers) {
+ *r_should_swap_buffers = !is_in_low_processor_usage_mode() || RenderingServer::get_singleton()->has_changed();
+ }
+
+ return exit;
}
void OS_Android::main_loop_end() {
if (main_loop) {
+ SceneTree *scene_tree = Object::cast_to<SceneTree>(main_loop);
+ if (scene_tree) {
+ scene_tree->quit();
+ }
main_loop->finalize();
}
}
@@ -197,7 +221,11 @@ Error OS_Android::shell_open(String p_uri) {
}
String OS_Android::get_resource_dir() const {
+#ifdef TOOLS_ENABLED
+ return OS_Unix::get_resource_dir();
+#else
return "/"; //android has its own filesystem for resources inside the APK
+#endif
}
String OS_Android::get_locale() const {
@@ -222,6 +250,14 @@ String OS_Android::get_data_path() const {
return get_user_data_dir();
}
+String OS_Android::get_executable_path() const {
+ // Since unix process creation is restricted on Android, we bypass
+ // OS_Unix::get_executable_path() so we can return ANDROID_EXEC_PATH.
+ // Detection of ANDROID_EXEC_PATH allows to handle process creation in an Android compliant
+ // manner.
+ return OS::get_executable_path();
+}
+
String OS_Android::get_user_data_dir() const {
if (!data_dir_cache.is_empty()) {
return data_dir_cache;
@@ -294,6 +330,10 @@ void OS_Android::vibrate_handheld(int p_duration_ms) {
godot_java->vibrate(p_duration_ms);
}
+String OS_Android::get_config_path() const {
+ return get_user_data_dir().plus_file("config");
+}
+
bool OS_Android::_check_internal_feature_support(const String &p_feature) {
if (p_feature == "mobile") {
return true;
@@ -343,5 +383,26 @@ OS_Android::OS_Android(GodotJavaWrapper *p_godot_java, GodotIOJavaWrapper *p_god
DisplayServerAndroid::register_android_driver();
}
+Error OS_Android::execute(const String &p_path, const List<String> &p_arguments, String *r_pipe, int *r_exitcode, bool read_stderr, Mutex *p_pipe_mutex, bool p_open_console) {
+ if (p_path == ANDROID_EXEC_PATH) {
+ return create_instance(p_arguments);
+ } else {
+ return OS_Unix::execute(p_path, p_arguments, r_pipe, r_exitcode, read_stderr, p_pipe_mutex, p_open_console);
+ }
+}
+
+Error OS_Android::create_process(const String &p_path, const List<String> &p_arguments, ProcessID *r_child_id, bool p_open_console) {
+ if (p_path == ANDROID_EXEC_PATH) {
+ return create_instance(p_arguments, r_child_id);
+ } else {
+ return OS_Unix::create_process(p_path, p_arguments, r_child_id, p_open_console);
+ }
+}
+
+Error OS_Android::create_instance(const List<String> &p_arguments, ProcessID *r_child_id) {
+ godot_java->create_new_godot_instance(p_arguments);
+ return OK;
+}
+
OS_Android::~OS_Android() {
}
diff --git a/platform/android/os_android.h b/platform/android/os_android.h
index f523f172c6..a40e17dc2c 100644
--- a/platform/android/os_android.h
+++ b/platform/android/os_android.h
@@ -52,7 +52,7 @@ private:
#endif
#if defined(VULKAN_ENABLED)
- ANativeWindow *native_window;
+ ANativeWindow *native_window = nullptr;
#endif
mutable String data_dir_cache;
@@ -60,12 +60,14 @@ private:
AudioDriverOpenSL audio_driver_android;
- MainLoop *main_loop;
+ MainLoop *main_loop = nullptr;
- GodotJavaWrapper *godot_java;
- GodotIOJavaWrapper *godot_io_java;
+ GodotJavaWrapper *godot_java = nullptr;
+ GodotIOJavaWrapper *godot_io_java = nullptr;
public:
+ static const char *ANDROID_EXEC_PATH;
+
virtual void initialize_core() override;
virtual void initialize() override;
@@ -94,7 +96,7 @@ public:
virtual MainLoop *get_main_loop() const override;
void main_loop_begin();
- bool main_loop_iterate();
+ bool main_loop_iterate(bool *r_should_swap_buffers = nullptr);
void main_loop_end();
void main_loop_focusout();
void main_loop_focusin();
@@ -108,6 +110,7 @@ public:
ANativeWindow *get_native_window() const;
virtual Error shell_open(String p_uri) override;
+ virtual String get_executable_path() const override;
virtual String get_user_data_dir() const override;
virtual String get_data_path() const override;
virtual String get_cache_path() const override;
@@ -121,6 +124,12 @@ public:
void vibrate_handheld(int p_duration_ms) override;
+ virtual String get_config_path() const override;
+
+ virtual Error execute(const String &p_path, const List<String> &p_arguments, String *r_pipe = nullptr, int *r_exitcode = nullptr, bool read_stderr = false, Mutex *p_pipe_mutex = nullptr, bool p_open_console = false) override;
+ virtual Error create_process(const String &p_path, const List<String> &p_arguments, ProcessID *r_child_id = nullptr, bool p_open_console = false) override;
+ virtual Error create_instance(const List<String> &p_arguments, ProcessID *r_child_id = nullptr) override;
+
virtual bool _check_internal_feature_support(const String &p_feature) override;
OS_Android(GodotJavaWrapper *p_godot_java, GodotIOJavaWrapper *p_godot_io_java, bool p_use_apk_expansion);
~OS_Android();
diff --git a/platform/iphone/export/export_plugin.cpp b/platform/iphone/export/export_plugin.cpp
index ac5886e620..57bee59523 100644
--- a/platform/iphone/export/export_plugin.cpp
+++ b/platform/iphone/export/export_plugin.cpp
@@ -530,8 +530,8 @@ Error EditorExportPlatformIOS::_export_icons(const Ref<EditorExportPreset> &p_pr
String json_description = "{\"images\":[";
String sizes;
- DirAccessRef da = DirAccess::open(p_iconset_dir);
- ERR_FAIL_COND_V_MSG(!da, ERR_CANT_OPEN, "Cannot open directory '" + p_iconset_dir + "'.");
+ Ref<DirAccess> da = DirAccess::open(p_iconset_dir);
+ ERR_FAIL_COND_V_MSG(da.is_null(), ERR_CANT_OPEN, "Cannot open directory '" + p_iconset_dir + "'.");
for (uint64_t i = 0; i < (sizeof(icon_infos) / sizeof(icon_infos[0])); ++i) {
IconInfo info = icon_infos[i];
@@ -588,17 +588,15 @@ Error EditorExportPlatformIOS::_export_icons(const Ref<EditorExportPreset> &p_pr
}
json_description += "]}";
- FileAccess *json_file = FileAccess::open(p_iconset_dir + "Contents.json", FileAccess::WRITE);
- ERR_FAIL_COND_V(!json_file, ERR_CANT_CREATE);
+ Ref<FileAccess> json_file = FileAccess::open(p_iconset_dir + "Contents.json", FileAccess::WRITE);
+ ERR_FAIL_COND_V(json_file.is_null(), ERR_CANT_CREATE);
CharString json_utf8 = json_description.utf8();
json_file->store_buffer((const uint8_t *)json_utf8.get_data(), json_utf8.length());
- memdelete(json_file);
- FileAccess *sizes_file = FileAccess::open(p_iconset_dir + "sizes", FileAccess::WRITE);
- ERR_FAIL_COND_V(!sizes_file, ERR_CANT_CREATE);
+ Ref<FileAccess> sizes_file = FileAccess::open(p_iconset_dir + "sizes", FileAccess::WRITE);
+ ERR_FAIL_COND_V(sizes_file.is_null(), ERR_CANT_CREATE);
CharString sizes_utf8 = sizes.utf8();
sizes_file->store_buffer((const uint8_t *)sizes_utf8.get_data(), sizes_utf8.length());
- memdelete(sizes_file);
return OK;
}
@@ -672,8 +670,8 @@ Error EditorExportPlatformIOS::_export_loading_screen_file(const Ref<EditorExpor
}
Error EditorExportPlatformIOS::_export_loading_screen_images(const Ref<EditorExportPreset> &p_preset, const String &p_dest_dir) {
- DirAccessRef da = DirAccess::open(p_dest_dir);
- ERR_FAIL_COND_V_MSG(!da, ERR_CANT_OPEN, "Cannot open directory '" + p_dest_dir + "'.");
+ Ref<DirAccess> da = DirAccess::open(p_dest_dir);
+ ERR_FAIL_COND_V_MSG(da.is_null(), ERR_CANT_OPEN, "Cannot open directory '" + p_dest_dir + "'.");
for (uint64_t i = 0; i < sizeof(loading_screen_infos) / sizeof(loading_screen_infos[0]); ++i) {
LoadingScreenInfo info = loading_screen_infos[i];
@@ -761,7 +759,7 @@ Error EditorExportPlatformIOS::_export_loading_screen_images(const Ref<EditorExp
return OK;
}
-Error EditorExportPlatformIOS::_walk_dir_recursive(DirAccess *p_da, FileHandler p_handler, void *p_userdata) {
+Error EditorExportPlatformIOS::_walk_dir_recursive(Ref<DirAccess> &p_da, FileHandler p_handler, void *p_userdata) {
Vector<String> dirs;
String current_dir = p_da->get_current_dir();
p_da->list_dir_begin();
@@ -807,7 +805,7 @@ struct CodesignData {
Error EditorExportPlatformIOS::_codesign(String p_file, void *p_userdata) {
if (p_file.ends_with(".dylib")) {
- CodesignData *data = (CodesignData *)p_userdata;
+ CodesignData *data = static_cast<CodesignData *>(p_userdata);
print_line(String("Signing ") + p_file);
String sign_id;
@@ -964,8 +962,8 @@ void EditorExportPlatformIOS::_add_assets_to_project(const Ref<EditorExportPrese
Error EditorExportPlatformIOS::_copy_asset(const String &p_out_dir, const String &p_asset, const String *p_custom_file_name, bool p_is_framework, bool p_should_embed, Vector<IOSExportAsset> &r_exported_assets) {
String binary_name = p_out_dir.get_file().get_basename();
- DirAccessRef da = DirAccess::create_for_path(p_asset);
- if (!da) {
+ Ref<DirAccess> da = DirAccess::create_for_path(p_asset);
+ if (da.is_null()) {
ERR_FAIL_V_MSG(ERR_CANT_CREATE, "Can't create directory: " + p_asset + ".");
}
bool file_exists = da->file_exists(p_asset);
@@ -1030,8 +1028,8 @@ Error EditorExportPlatformIOS::_copy_asset(const String &p_out_dir, const String
destination = p_out_dir.plus_file(asset_path);
}
- DirAccessRef filesystem_da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
- ERR_FAIL_COND_V_MSG(!filesystem_da, ERR_CANT_CREATE, "Cannot create DirAccess for path '" + p_out_dir + "'.");
+ Ref<DirAccess> filesystem_da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
+ ERR_FAIL_COND_V_MSG(filesystem_da.is_null(), ERR_CANT_CREATE, "Cannot create DirAccess for path '" + p_out_dir + "'.");
if (!filesystem_da->dir_exists(destination_dir)) {
Error make_dir_err = filesystem_da->make_dir_recursive(destination_dir);
@@ -1097,11 +1095,9 @@ Error EditorExportPlatformIOS::_copy_asset(const String &p_out_dir, const String
String info_plist = info_plist_format.replace("$name", file_name);
- FileAccess *f = FileAccess::open(destination_dir.plus_file("Info.plist"), FileAccess::WRITE);
- if (f) {
+ Ref<FileAccess> f = FileAccess::open(destination_dir.plus_file("Info.plist"), FileAccess::WRITE);
+ if (f.is_valid()) {
f->store_string(info_plist);
- f->close();
- memdelete(f);
}
}
}
@@ -1411,8 +1407,8 @@ Error EditorExportPlatformIOS::export_project(const Ref<EditorExportPreset> &p_p
}
{
- DirAccessRef da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
- if (da) {
+ Ref<DirAccess> da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
+ if (da.is_valid()) {
String current_dir = da->get_current_dir();
// remove leftovers from last export so they don't interfere
@@ -1488,12 +1484,11 @@ Error EditorExportPlatformIOS::export_project(const Ref<EditorExportPreset> &p_p
Vector<IOSExportAsset> assets;
- DirAccessRef tmp_app_path = DirAccess::create_for_path(dest_dir);
- ERR_FAIL_COND_V(!tmp_app_path, ERR_CANT_CREATE);
+ Ref<DirAccess> tmp_app_path = DirAccess::create_for_path(dest_dir);
+ ERR_FAIL_COND_V(tmp_app_path.is_null(), ERR_CANT_CREATE);
print_line("Unzipping...");
- FileAccess *src_f = nullptr;
- zlib_filefunc_def io = zipio_create_io_from_file(&src_f);
+ zlib_filefunc_def io = zipio_create_io();
unzFile src_pkg_zip = unzOpen2(src_pkg_name.utf8().get_data(), &io);
if (!src_pkg_zip) {
EditorNode::add_io_error("Could not open export template (not a zip file?):\n" + src_pkg_name);
@@ -1515,6 +1510,9 @@ Error EditorExportPlatformIOS::export_project(const Ref<EditorExportPreset> &p_p
unz_file_info info;
char fname[16384];
ret = unzGetCurrentFileInfo(src_pkg_zip, &info, fname, 16384, nullptr, 0, nullptr, 0);
+ if (ret != UNZ_OK) {
+ break;
+ }
String file = String::utf8(fname);
@@ -1572,15 +1570,15 @@ Error EditorExportPlatformIOS::export_project(const Ref<EditorExportPreset> &p_p
}
/* write the file */
- FileAccess *f = FileAccess::open(file, FileAccess::WRITE);
- if (!f) {
- ERR_PRINT("Can't write '" + file + "'.");
- unzClose(src_pkg_zip);
- return ERR_CANT_CREATE;
- };
- f->store_buffer(data.ptr(), data.size());
- f->close();
- memdelete(f);
+ {
+ Ref<FileAccess> f = FileAccess::open(file, FileAccess::WRITE);
+ if (f.is_null()) {
+ ERR_PRINT("Can't write '" + file + "'.");
+ unzClose(src_pkg_zip);
+ return ERR_CANT_CREATE;
+ };
+ f->store_buffer(data.ptr(), data.size());
+ }
#if defined(OSX_ENABLED) || defined(X11_ENABLED)
if (is_execute) {
@@ -1611,7 +1609,7 @@ Error EditorExportPlatformIOS::export_project(const Ref<EditorExportPreset> &p_p
{
String fname = dest_dir + binary_name + "/en.lproj";
tmp_app_path->make_dir_recursive(fname);
- FileAccessRef f = FileAccess::open(fname + "/InfoPlist.strings", FileAccess::WRITE);
+ Ref<FileAccess> f = FileAccess::open(fname + "/InfoPlist.strings", FileAccess::WRITE);
f->store_line("/* Localized versions of Info.plist keys */");
f->store_line("");
f->store_line("CFBundleDisplayName = \"" + ProjectSettings::get_singleton()->get("application/config/name").operator String() + "\";");
@@ -1626,7 +1624,7 @@ Error EditorExportPlatformIOS::export_project(const Ref<EditorExportPreset> &p_p
String lang = tr->get_locale();
String fname = dest_dir + binary_name + "/" + lang + ".lproj";
tmp_app_path->make_dir_recursive(fname);
- FileAccessRef f = FileAccess::open(fname + "/InfoPlist.strings", FileAccess::WRITE);
+ Ref<FileAccess> f = FileAccess::open(fname + "/InfoPlist.strings", FileAccess::WRITE);
f->store_line("/* Localized versions of Info.plist keys */");
f->store_line("");
if (appnames.has(lang)) {
@@ -1680,9 +1678,8 @@ Error EditorExportPlatformIOS::export_project(const Ref<EditorExportPreset> &p_p
String launch_image_path = dest_dir + binary_name + "/Images.xcassets/LaunchImage.launchimage/";
String splash_image_path = dest_dir + binary_name + "/Images.xcassets/SplashImage.imageset/";
- DirAccessRef launch_screen_da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
-
- if (!launch_screen_da) {
+ Ref<DirAccess> launch_screen_da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
+ if (launch_screen_da.is_null()) {
return ERR_CANT_CREATE;
}
@@ -1719,25 +1716,24 @@ Error EditorExportPlatformIOS::export_project(const Ref<EditorExportPreset> &p_p
_export_additional_assets(dest_dir + binary_name, libraries, assets);
_add_assets_to_project(p_preset, project_file_data, assets);
String project_file_name = dest_dir + binary_name + ".xcodeproj/project.pbxproj";
- FileAccess *f = FileAccess::open(project_file_name, FileAccess::WRITE);
- if (!f) {
- ERR_PRINT("Can't write '" + project_file_name + "'.");
- return ERR_CANT_CREATE;
- };
- f->store_buffer(project_file_data.ptr(), project_file_data.size());
- f->close();
- memdelete(f);
+ {
+ Ref<FileAccess> f = FileAccess::open(project_file_name, FileAccess::WRITE);
+ if (f.is_null()) {
+ ERR_PRINT("Can't write '" + project_file_name + "'.");
+ return ERR_CANT_CREATE;
+ };
+ f->store_buffer(project_file_data.ptr(), project_file_data.size());
+ }
#ifdef OSX_ENABLED
{
if (ep.step("Code-signing dylibs", 2)) {
return ERR_SKIP;
}
- DirAccess *dylibs_dir = DirAccess::open(dest_dir + binary_name + "/dylibs");
- ERR_FAIL_COND_V(!dylibs_dir, ERR_CANT_OPEN);
+ Ref<DirAccess> dylibs_dir = DirAccess::open(dest_dir + binary_name + "/dylibs");
+ ERR_FAIL_COND_V(dylibs_dir.is_null(), ERR_CANT_OPEN);
CodesignData codesign_data(p_preset, p_debug);
err = _walk_dir_recursive(dylibs_dir, _codesign, &codesign_data);
- memdelete(dylibs_dir);
ERR_FAIL_COND_V(err, err);
}
diff --git a/platform/iphone/export/export_plugin.h b/platform/iphone/export/export_plugin.h
index c01983e39f..3831f3bff2 100644
--- a/platform/iphone/export/export_plugin.h
+++ b/platform/iphone/export/export_plugin.h
@@ -65,7 +65,7 @@ class EditorExportPlatformIOS : public EditorExportPlatform {
Vector<PluginConfigIOS> plugins;
typedef Error (*FileHandler)(String p_file, void *p_userdata);
- static Error _walk_dir_recursive(DirAccess *p_da, FileHandler p_handler, void *p_userdata);
+ static Error _walk_dir_recursive(Ref<DirAccess> &p_da, FileHandler p_handler, void *p_userdata);
static Error _codesign(String p_file, void *p_userdata);
void _blend_and_rotate(Ref<Image> &p_dst, Ref<Image> &p_src, bool p_rot);
@@ -141,7 +141,7 @@ class EditorExportPlatformIOS : public EditorExportPlatform {
}
static void _check_for_changes_poll_thread(void *ud) {
- EditorExportPlatformIOS *ea = (EditorExportPlatformIOS *)ud;
+ EditorExportPlatformIOS *ea = static_cast<EditorExportPlatformIOS *>(ud);
while (!ea->quit_request.is_set()) {
// Nothing to do if we already know the plugins have changed.
@@ -215,8 +215,8 @@ public:
/// List the gdip files in the directory specified by the p_path parameter.
static Vector<String> list_plugin_config_files(const String &p_path, bool p_check_directories) {
Vector<String> dir_files;
- DirAccessRef da = DirAccess::open(p_path);
- if (da) {
+ Ref<DirAccess> da = DirAccess::open(p_path);
+ if (da.is_valid()) {
da->list_dir_begin();
while (true) {
String file = da->get_next();
diff --git a/platform/iphone/os_iphone.h b/platform/iphone/os_iphone.h
index 3281ff0cdb..6a61f3a910 100644
--- a/platform/iphone/os_iphone.h
+++ b/platform/iphone/os_iphone.h
@@ -52,11 +52,11 @@ private:
AudioDriverCoreAudio audio_driver;
- iOS *ios;
+ iOS *ios = nullptr;
- JoypadIPhone *joypad_iphone;
+ JoypadIPhone *joypad_iphone = nullptr;
- MainLoop *main_loop;
+ MainLoop *main_loop = nullptr;
virtual void initialize_core() override;
virtual void initialize() override;
diff --git a/platform/iphone/os_iphone.mm b/platform/iphone/os_iphone.mm
index 1d990b5625..ea1bc0ef64 100644
--- a/platform/iphone/os_iphone.mm
+++ b/platform/iphone/os_iphone.mm
@@ -259,7 +259,7 @@ Error OSIPhone::shell_open(String p_uri) {
}
void OSIPhone::set_user_data_dir(String p_dir) {
- DirAccessRef da = DirAccess::open(p_dir);
+ Ref<DirAccess> da = DirAccess::open(p_dir);
user_data_dir = da->get_current_dir();
printf("setting data dir to %s from %s\n", user_data_dir.utf8().get_data(), p_dir.utf8().get_data());
}
diff --git a/platform/javascript/api/javascript_tools_editor_plugin.cpp b/platform/javascript/api/javascript_tools_editor_plugin.cpp
index 0442a1eaeb..31ce71127d 100644
--- a/platform/javascript/api/javascript_tools_editor_plugin.cpp
+++ b/platform/javascript/api/javascript_tools_editor_plugin.cpp
@@ -64,8 +64,7 @@ void JavaScriptToolsEditorPlugin::_download_zip(Variant p_v) {
}
String resource_path = ProjectSettings::get_singleton()->get_resource_path();
- FileAccess *src_f;
- zlib_filefunc_def io = zipio_create_io_from_file(&src_f);
+ zlib_filefunc_def io = zipio_create_io();
// Name the downloaded ZIP file to contain the project name and download date for easier organization.
// Replace characters not allowed (or risky) in Windows file names with safe characters.
@@ -82,22 +81,22 @@ void JavaScriptToolsEditorPlugin::_download_zip(Variant p_v) {
const String base_path = resource_path.substr(0, resource_path.rfind("/")) + "/";
_zip_recursive(resource_path, base_path, zip);
zipClose(zip, nullptr);
- FileAccess *f = FileAccess::open(output_path, FileAccess::READ);
- ERR_FAIL_COND_MSG(!f, "Unable to create ZIP file.");
- Vector<uint8_t> buf;
- buf.resize(f->get_length());
- f->get_buffer(buf.ptrw(), buf.size());
- godot_js_os_download_buffer(buf.ptr(), buf.size(), output_name.utf8().get_data(), "application/zip");
+ {
+ Ref<FileAccess> f = FileAccess::open(output_path, FileAccess::READ);
+ ERR_FAIL_COND_MSG(f.is_null(), "Unable to create ZIP file.");
+ Vector<uint8_t> buf;
+ buf.resize(f->get_length());
+ f->get_buffer(buf.ptrw(), buf.size());
+ godot_js_os_download_buffer(buf.ptr(), buf.size(), output_name.utf8().get_data(), "application/zip");
+ }
- f->close();
- memdelete(f);
// Remove the temporary file since it was sent to the user's native filesystem as a download.
DirAccess::remove_file_or_error(output_path);
}
void JavaScriptToolsEditorPlugin::_zip_file(String p_path, String p_base_path, zipFile p_zip) {
- FileAccess *f = FileAccess::open(p_path, FileAccess::READ);
- if (!f) {
+ Ref<FileAccess> f = FileAccess::open(p_path, FileAccess::READ);
+ if (f.is_null()) {
WARN_PRINT("Unable to open file for zipping: " + p_path);
return;
}
@@ -105,8 +104,6 @@ void JavaScriptToolsEditorPlugin::_zip_file(String p_path, String p_base_path, z
uint64_t len = f->get_length();
data.resize(len);
f->get_buffer(data.ptrw(), len);
- f->close();
- memdelete(f);
String path = p_path.replace_first(p_base_path, "");
zipOpenNewFileInZip(p_zip,
@@ -124,7 +121,7 @@ void JavaScriptToolsEditorPlugin::_zip_file(String p_path, String p_base_path, z
}
void JavaScriptToolsEditorPlugin::_zip_recursive(String p_path, String p_base_path, zipFile p_zip) {
- DirAccessRef dir = DirAccess::open(p_path);
+ Ref<DirAccess> dir = DirAccess::open(p_path);
if (!dir) {
WARN_PRINT("Unable to open directory for zipping: " + p_path);
return;
diff --git a/platform/javascript/export/export_plugin.cpp b/platform/javascript/export/export_plugin.cpp
index 4448acccc2..66d93d7c49 100644
--- a/platform/javascript/export/export_plugin.cpp
+++ b/platform/javascript/export/export_plugin.cpp
@@ -33,8 +33,7 @@
#include "core/config/project_settings.h"
Error EditorExportPlatformJavaScript::_extract_template(const String &p_template, const String &p_dir, const String &p_name, bool pwa) {
- FileAccess *src_f = nullptr;
- zlib_filefunc_def io = zipio_create_io_from_file(&src_f);
+ zlib_filefunc_def io = zipio_create_io();
unzFile pkg = unzOpen2(p_template.utf8().get_data(), &io);
if (!pkg) {
@@ -70,14 +69,13 @@ Error EditorExportPlatformJavaScript::_extract_template(const String &p_template
//write
String dst = p_dir.plus_file(file.replace("godot", p_name));
- FileAccess *f = FileAccess::open(dst, FileAccess::WRITE);
- if (!f) {
+ Ref<FileAccess> f = FileAccess::open(dst, FileAccess::WRITE);
+ if (f.is_null()) {
EditorNode::get_singleton()->show_warning(TTR("Could not write file:") + "\n" + dst);
unzClose(pkg);
return ERR_FILE_CANT_WRITE;
}
f->store_buffer(data.ptr(), data.size());
- memdelete(f);
} while (unzGoToNextFile(pkg) == UNZ_OK);
unzClose(pkg);
@@ -85,13 +83,12 @@ Error EditorExportPlatformJavaScript::_extract_template(const String &p_template
}
Error EditorExportPlatformJavaScript::_write_or_error(const uint8_t *p_content, int p_size, String p_path) {
- FileAccess *f = FileAccess::open(p_path, FileAccess::WRITE);
- if (!f) {
+ Ref<FileAccess> f = FileAccess::open(p_path, FileAccess::WRITE);
+ if (f.is_null()) {
EditorNode::get_singleton()->show_warning(TTR("Could not write file:") + "\n" + p_path);
return ERR_FILE_CANT_WRITE;
}
f->store_buffer(p_content, p_size);
- memdelete(f);
return OK;
}
@@ -233,15 +230,13 @@ Error EditorExportPlatformJavaScript::_build_pwa(const Ref<EditorExportPreset> &
const String sw_path = dir.plus_file(name + ".service.worker.js");
Vector<uint8_t> sw;
{
- FileAccess *f = FileAccess::open(sw_path, FileAccess::READ);
- if (!f) {
+ Ref<FileAccess> f = FileAccess::open(sw_path, FileAccess::READ);
+ if (f.is_null()) {
EditorNode::get_singleton()->show_warning(TTR("Could not read file:") + "\n" + sw_path);
return ERR_FILE_CANT_READ;
}
sw.resize(f->get_length());
f->get_buffer(sw.ptrw(), sw.size());
- memdelete(f);
- f = nullptr;
}
_replace_strings(replaces, sw);
Error err = _write_or_error(sw.ptr(), sw.size(), dir.plus_file(name + ".service.worker.js"));
@@ -252,7 +247,7 @@ Error EditorExportPlatformJavaScript::_build_pwa(const Ref<EditorExportPreset> &
// Custom offline page
const String offline_page = p_preset->get("progressive_web_app/offline_page");
if (!offline_page.is_empty()) {
- DirAccessRef da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
+ Ref<DirAccess> da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
const String offline_dest = dir.plus_file(name + ".offline.html");
err = da->copy(ProjectSettings::get_singleton()->globalize_path(offline_page), offline_dest);
if (err != OK) {
@@ -360,6 +355,15 @@ Ref<Texture2D> EditorExportPlatformJavaScript::get_logo() const {
}
bool EditorExportPlatformJavaScript::can_export(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates) const {
+#ifndef DEV_ENABLED
+ // We don't provide export templates for the HTML5 platform currently as there
+ // is no suitable renderer to use with them. So we forbid exporting and tell
+ // users why. This is skipped in DEV_ENABLED so that contributors can still test
+ // the pipeline once we start having WebGL or WebGPU support.
+ r_error = "The HTML5 platform is currently not supported in Godot 4.0, as there is no suitable renderer for it.\n";
+ return false;
+#endif
+
String err;
bool valid = false;
ExportMode mode = (ExportMode)(int)p_preset->get("variant/export_type");
@@ -447,7 +451,7 @@ Error EditorExportPlatformJavaScript::export_project(const Ref<EditorExportPrese
}
{
- DirAccessRef da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
+ Ref<DirAccess> da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
for (int i = 0; i < shared_objects.size(); i++) {
String dst = base_dir.plus_file(shared_objects[i].path.get_file());
error = da->copy(shared_objects[i].path, dst);
@@ -466,32 +470,25 @@ Error EditorExportPlatformJavaScript::export_project(const Ref<EditorExportPrese
// Parse generated file sizes (pck and wasm, to help show a meaningful loading bar).
Dictionary file_sizes;
- FileAccess *f = nullptr;
- f = FileAccess::open(pck_path, FileAccess::READ);
- if (f) {
+ Ref<FileAccess> f = FileAccess::open(pck_path, FileAccess::READ);
+ if (f.is_valid()) {
file_sizes[pck_path.get_file()] = (uint64_t)f->get_length();
- memdelete(f);
- f = nullptr;
}
f = FileAccess::open(base_path + ".wasm", FileAccess::READ);
- if (f) {
+ if (f.is_valid()) {
file_sizes[base_name + ".wasm"] = (uint64_t)f->get_length();
- memdelete(f);
- f = nullptr;
}
// Read the HTML shell file (custom or from template).
const String html_path = custom_html.is_empty() ? base_path + ".html" : custom_html;
Vector<uint8_t> html;
f = FileAccess::open(html_path, FileAccess::READ);
- if (!f) {
+ if (f.is_null()) {
EditorNode::get_singleton()->show_warning(TTR("Could not read HTML shell:") + "\n" + html_path);
return ERR_FILE_CANT_READ;
}
html.resize(f->get_length());
f->get_buffer(html.ptrw(), html.size());
- memdelete(f);
- f = nullptr;
// Generate HTML file with replaced strings.
_fix_html(html, p_preset, base_name, p_debug, p_flags, shared_objects, file_sizes);
@@ -577,7 +574,7 @@ Error EditorExportPlatformJavaScript::run(const Ref<EditorExportPreset> &p_prese
}
const String dest = EditorPaths::get_singleton()->get_cache_dir().plus_file("web");
- DirAccessRef da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
+ Ref<DirAccess> da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
if (!da->dir_exists(dest)) {
Error err = da->make_dir_recursive(dest);
if (err != OK) {
@@ -642,7 +639,7 @@ Ref<Texture2D> EditorExportPlatformJavaScript::get_run_icon() const {
}
void EditorExportPlatformJavaScript::_server_thread_poll(void *data) {
- EditorExportPlatformJavaScript *ej = (EditorExportPlatformJavaScript *)data;
+ EditorExportPlatformJavaScript *ej = static_cast<EditorExportPlatformJavaScript *>(data);
while (!ej->server_quit) {
OS::get_singleton()->delay_usec(6900);
{
diff --git a/platform/javascript/export/export_server.h b/platform/javascript/export/export_server.h
index 1b908ad9b0..f77ac3d1ad 100644
--- a/platform/javascript/export/export_server.h
+++ b/platform/javascript/export/export_server.h
@@ -153,8 +153,8 @@ public:
}
const String ctype = mimes[req_ext];
- FileAccess *f = FileAccess::open(filepath, FileAccess::READ);
- ERR_FAIL_COND(!f);
+ Ref<FileAccess> f = FileAccess::open(filepath, FileAccess::READ);
+ ERR_FAIL_COND(f.is_null());
String s = "HTTP/1.1 200 OK\r\n";
s += "Connection: Close\r\n";
s += "Content-Type: " + ctype + "\r\n";
@@ -166,7 +166,6 @@ public:
CharString cs = s.utf8();
Error err = peer->put_data((const uint8_t *)cs.get_data(), cs.size() - 1);
if (err != OK) {
- memdelete(f);
ERR_FAIL();
}
@@ -178,11 +177,9 @@ public:
}
err = peer->put_data(bytes, read);
if (err != OK) {
- memdelete(f);
ERR_FAIL();
}
}
- memdelete(f);
}
void poll() {
diff --git a/platform/linuxbsd/detect.py b/platform/linuxbsd/detect.py
index 03c85d09ad..39f9b99108 100644
--- a/platform/linuxbsd/detect.py
+++ b/platform/linuxbsd/detect.py
@@ -115,7 +115,7 @@ def configure(env):
## Architecture
- is64 = sys.maxsize > 2 ** 32
+ is64 = sys.maxsize > 2**32
if env["bits"] == "default":
env["bits"] = "64" if is64 else "32"
@@ -162,6 +162,7 @@ def configure(env):
if env["use_ubsan"] or env["use_asan"] or env["use_lsan"] or env["use_tsan"] or env["use_msan"]:
env.extra_suffix += ".san"
+ env.Append(CCFLAGS=["-DSANITIZERS_ENABLED"])
if env["use_ubsan"]:
env.Append(
diff --git a/platform/linuxbsd/display_server_x11.cpp b/platform/linuxbsd/display_server_x11.cpp
index 07cb6a23e8..db2fe274d9 100644
--- a/platform/linuxbsd/display_server_x11.cpp
+++ b/platform/linuxbsd/display_server_x11.cpp
@@ -363,15 +363,15 @@ DisplayServerX11::MouseMode DisplayServerX11::mouse_get_mode() const {
return mouse_mode;
}
-void DisplayServerX11::mouse_warp_to_position(const Point2i &p_to) {
+void DisplayServerX11::warp_mouse(const Point2i &p_position) {
_THREAD_SAFE_METHOD_
if (mouse_mode == MOUSE_MODE_CAPTURED) {
- last_mouse_pos = p_to;
+ last_mouse_pos = p_position;
} else {
WindowID window_id = windows.has(last_focused_window) ? last_focused_window : MAIN_WINDOW_ID;
XWarpPointer(x11_display, None, windows[window_id].x11_window,
- 0, 0, 0, 0, (int)p_to.x, (int)p_to.y);
+ 0, 0, 0, 0, (int)p_position.x, (int)p_position.y);
}
}
@@ -1732,8 +1732,15 @@ bool DisplayServerX11::_window_maximize_check(WindowID p_window, const char *p_a
if (result == Success && data) {
Atom *atoms = (Atom *)data;
- Atom wm_act_max_horz = XInternAtom(x11_display, "_NET_WM_ACTION_MAXIMIZE_HORZ", False);
- Atom wm_act_max_vert = XInternAtom(x11_display, "_NET_WM_ACTION_MAXIMIZE_VERT", False);
+ Atom wm_act_max_horz;
+ Atom wm_act_max_vert;
+ if (strcmp(p_atom_name, "_NET_WM_STATE") == 0) {
+ wm_act_max_horz = XInternAtom(x11_display, "_NET_WM_STATE_MAXIMIZED_HORZ", False);
+ wm_act_max_vert = XInternAtom(x11_display, "_NET_WM_STATE_MAXIMIZED_VERT", False);
+ } else {
+ wm_act_max_horz = XInternAtom(x11_display, "_NET_WM_ACTION_MAXIMIZE_HORZ", False);
+ wm_act_max_vert = XInternAtom(x11_display, "_NET_WM_ACTION_MAXIMIZE_VERT", False);
+ }
bool found_wm_act_max_horz = false;
bool found_wm_act_max_vert = false;
@@ -3045,7 +3052,7 @@ void DisplayServerX11::_window_changed(XEvent *event) {
}
void DisplayServerX11::_dispatch_input_events(const Ref<InputEvent> &p_event) {
- ((DisplayServerX11 *)(get_singleton()))->_dispatch_input_event(p_event);
+ static_cast<DisplayServerX11 *>(get_singleton())->_dispatch_input_event(p_event);
}
void DisplayServerX11::_dispatch_input_event(const Ref<InputEvent> &p_event) {
@@ -3099,7 +3106,7 @@ void DisplayServerX11::_send_window_event(const WindowData &wd, WindowEvent p_ev
}
void DisplayServerX11::_poll_events_thread(void *ud) {
- DisplayServerX11 *display_server = (DisplayServerX11 *)ud;
+ DisplayServerX11 *display_server = static_cast<DisplayServerX11 *>(ud);
display_server->_poll_events();
}
@@ -3201,20 +3208,24 @@ Rect2i DisplayServerX11::window_get_popup_safe_rect(WindowID p_window) const {
}
void DisplayServerX11::popup_open(WindowID p_window) {
+ _THREAD_SAFE_METHOD_
+
WindowData &wd = windows[p_window];
if (wd.is_popup) {
- // Close all popups, up to current popup parent, or every popup if new window is not transient.
+ // Find current popup parent, or root popup if new window is not transient.
+ List<WindowID>::Element *C = nullptr;
List<WindowID>::Element *E = popup_list.back();
while (E) {
if (wd.transient_parent != E->get() || wd.transient_parent == INVALID_WINDOW_ID) {
- _send_window_event(windows[E->get()], DisplayServerX11::WINDOW_EVENT_CLOSE_REQUEST);
- List<WindowID>::Element *F = E->prev();
- popup_list.erase(E);
- E = F;
+ C = E;
+ E = E->prev();
} else {
break;
}
}
+ if (C) {
+ _send_window_event(windows[C->get()], DisplayServerX11::WINDOW_EVENT_CLOSE_REQUEST);
+ }
time_since_popup = OS::get_singleton()->get_ticks_msec();
popup_list.push_back(p_window);
@@ -3222,16 +3233,22 @@ void DisplayServerX11::popup_open(WindowID p_window) {
}
void DisplayServerX11::popup_close(WindowID p_window) {
+ _THREAD_SAFE_METHOD_
+
List<WindowID>::Element *E = popup_list.find(p_window);
while (E) {
- _send_window_event(windows[E->get()], DisplayServerX11::WINDOW_EVENT_CLOSE_REQUEST);
List<WindowID>::Element *F = E->next();
+ WindowID win_id = E->get();
popup_list.erase(E);
+
+ _send_window_event(windows[win_id], DisplayServerX11::WINDOW_EVENT_CLOSE_REQUEST);
E = F;
}
}
void DisplayServerX11::mouse_process_popups() {
+ _THREAD_SAFE_METHOD_
+
if (popup_list.is_empty()) {
return;
}
@@ -3252,7 +3269,9 @@ void DisplayServerX11::mouse_process_popups() {
Vector2i pos = Vector2i(root_attrs.x + root_x, root_attrs.y + root_y);
if ((pos != last_mouse_monitor_pos) || (mask != last_mouse_monitor_mask)) {
if (((mask & Button1Mask) || (mask & Button2Mask) || (mask & Button3Mask) || (mask & Button4Mask) || (mask & Button5Mask))) {
+ List<WindowID>::Element *C = nullptr;
List<WindowID>::Element *E = popup_list.back();
+ // Find top popup to close.
while (E) {
// Popup window area.
Rect2i win_rect = Rect2i(window_get_position(E->get()), window_get_size(E->get()));
@@ -3263,12 +3282,13 @@ void DisplayServerX11::mouse_process_popups() {
} else if (safe_rect != Rect2i() && safe_rect.has_point(pos)) {
break;
} else {
- _send_window_event(windows[E->get()], DisplayServerX11::WINDOW_EVENT_CLOSE_REQUEST);
- List<WindowID>::Element *F = E->prev();
- popup_list.erase(E);
- E = F;
+ C = E;
+ E = E->prev();
}
}
+ if (C) {
+ _send_window_event(windows[C->get()], DisplayServerX11::WINDOW_EVENT_CLOSE_REQUEST);
+ }
}
}
last_mouse_monitor_mask = mask;
diff --git a/platform/linuxbsd/display_server_x11.h b/platform/linuxbsd/display_server_x11.h
index 63d32d939d..cd673d94d9 100644
--- a/platform/linuxbsd/display_server_x11.h
+++ b/platform/linuxbsd/display_server_x11.h
@@ -108,7 +108,7 @@ class DisplayServerX11 : public DisplayServer {
#endif
#if defined(DBUS_ENABLED)
- FreeDesktopScreenSaver *screensaver;
+ FreeDesktopScreenSaver *screensaver = nullptr;
bool keep_screen_on = false;
#endif
@@ -168,7 +168,7 @@ class DisplayServerX11 : public DisplayServer {
String internal_clipboard_primary;
Window xdnd_source_window;
::Display *x11_display;
- char *xmbstring;
+ char *xmbstring = nullptr;
int xmblen;
unsigned long last_timestamp;
::Time last_keyrelease_time;
@@ -249,7 +249,7 @@ class DisplayServerX11 : public DisplayServer {
typedef void (*xrr_free_monitors_t)(xrr_monitor_info *monitors);
xrr_get_monitors_t xrr_get_monitors;
xrr_free_monitors_t xrr_free_monitors;
- void *xrandr_handle;
+ void *xrandr_handle = nullptr;
Bool xrandr_ext_ok;
struct Property {
@@ -301,7 +301,7 @@ public:
virtual void mouse_set_mode(MouseMode p_mode) override;
virtual MouseMode mouse_get_mode() const override;
- virtual void mouse_warp_to_position(const Point2i &p_to) override;
+ virtual void warp_mouse(const Point2i &p_position) override;
virtual Point2i mouse_get_position() const override;
virtual MouseButton mouse_get_button_state() const override;
diff --git a/platform/linuxbsd/export/export_plugin.cpp b/platform/linuxbsd/export/export_plugin.cpp
index 24906fa3fb..9f7fab6ee8 100644
--- a/platform/linuxbsd/export/export_plugin.cpp
+++ b/platform/linuxbsd/export/export_plugin.cpp
@@ -34,8 +34,8 @@
#include "editor/editor_node.h"
Error EditorExportPlatformLinuxBSD::_export_debug_script(const Ref<EditorExportPreset> &p_preset, const String &p_app_name, const String &p_pkg_name, const String &p_path) {
- FileAccessRef f = FileAccess::open(p_path, FileAccess::WRITE);
- ERR_FAIL_COND_V(!f, ERR_CANT_CREATE);
+ Ref<FileAccess> f = FileAccess::open(p_path, FileAccess::WRITE);
+ ERR_FAIL_COND_V(f.is_null(), ERR_CANT_CREATE);
f->store_line("#!/bin/sh");
f->store_line("echo -ne '\\033c\\033]0;" + p_app_name + "\\a'");
@@ -101,8 +101,8 @@ List<String> EditorExportPlatformLinuxBSD::get_binary_extensions(const Ref<Edito
Error EditorExportPlatformLinuxBSD::fixup_embedded_pck(const String &p_path, int64_t p_embedded_start, int64_t p_embedded_size) const {
// Patch the header of the "pck" section in the ELF file so that it corresponds to the embedded data
- FileAccess *f = FileAccess::open(p_path, FileAccess::READ_WRITE);
- if (!f) {
+ Ref<FileAccess> f = FileAccess::open(p_path, FileAccess::READ_WRITE);
+ if (f.is_null()) {
return ERR_CANT_OPEN;
}
@@ -110,7 +110,6 @@ Error EditorExportPlatformLinuxBSD::fixup_embedded_pck(const String &p_path, int
{
uint32_t magic = f->get_32();
if (magic != 0x464c457f) { // 0x7F + "ELF"
- f->close();
return ERR_FILE_CORRUPT;
}
}
@@ -120,7 +119,6 @@ Error EditorExportPlatformLinuxBSD::fixup_embedded_pck(const String &p_path, int
int bits = f->get_8() * 32;
if (bits == 32 && p_embedded_size >= 0x100000000) {
- f->close();
ERR_FAIL_V_MSG(ERR_INVALID_DATA, "32-bit executables cannot have embedded data >= 4 GiB.");
}
@@ -165,7 +163,6 @@ Error EditorExportPlatformLinuxBSD::fixup_embedded_pck(const String &p_path, int
f->seek(string_data_pos);
strings = (uint8_t *)memalloc(string_data_size);
if (!strings) {
- f->close();
return ERR_OUT_OF_MEMORY;
}
f->get_buffer(strings, string_data_size);
@@ -198,7 +195,6 @@ Error EditorExportPlatformLinuxBSD::fixup_embedded_pck(const String &p_path, int
}
memfree(strings);
- f->close();
return found ? OK : ERR_FILE_CORRUPT;
}
diff --git a/platform/linuxbsd/gl_manager_x11.h b/platform/linuxbsd/gl_manager_x11.h
index 0bb0a446ab..fb2c74a2b6 100644
--- a/platform/linuxbsd/gl_manager_x11.h
+++ b/platform/linuxbsd/gl_manager_x11.h
@@ -52,21 +52,20 @@ public:
private:
// any data specific to the window
struct GLWindow {
- GLWindow() { in_use = false; }
- bool in_use;
+ bool in_use = false;
// the external ID .. should match the GL window number .. unused I think
- DisplayServer::WindowID window_id;
- int width;
- int height;
+ DisplayServer::WindowID window_id = DisplayServer::INVALID_WINDOW_ID;
+ int width = 0;
+ int height = 0;
::Window x11_window;
- int gldisplay_id;
+ int gldisplay_id = 0;
};
struct GLDisplay {
GLDisplay() { context = nullptr; }
~GLDisplay();
- GLManager_X11_Private *context;
+ GLManager_X11_Private *context = nullptr;
::Display *x11_display;
XVisualInfo x_vi;
XSetWindowAttributes x_swa;
@@ -82,7 +81,7 @@ private:
LocalVector<GLWindow> _windows;
LocalVector<GLDisplay> _displays;
- GLWindow *_current_window;
+ GLWindow *_current_window = nullptr;
void _internal_set_current_window(GLWindow *p_win);
diff --git a/platform/linuxbsd/godot_linuxbsd.cpp b/platform/linuxbsd/godot_linuxbsd.cpp
index 7c9f81bd3f..9fe00568fb 100644
--- a/platform/linuxbsd/godot_linuxbsd.cpp
+++ b/platform/linuxbsd/godot_linuxbsd.cpp
@@ -33,10 +33,20 @@
#include <stdlib.h>
#include <unistd.h>
+#if defined(SANITIZERS_ENABLED)
+#include <sys/resource.h>
+#endif
+
#include "main/main.h"
#include "os_linuxbsd.h"
int main(int argc, char *argv[]) {
+#if defined(SANITIZERS_ENABLED)
+ // Note: Set stack size to be at least 30 MB (vs 8 MB default) to avoid overflow, address sanitizer can increase stack usage up to 3 times.
+ struct rlimit stack_lim = { 0x1E00000, 0x1E00000 };
+ setrlimit(RLIMIT_STACK, &stack_lim);
+#endif
+
OS_LinuxBSD os;
setlocale(LC_CTYPE, "");
diff --git a/platform/linuxbsd/joypad_linux.cpp b/platform/linuxbsd/joypad_linux.cpp
index 65d53b266f..22a9518a25 100644
--- a/platform/linuxbsd/joypad_linux.cpp
+++ b/platform/linuxbsd/joypad_linux.cpp
@@ -95,7 +95,7 @@ JoypadLinux::~JoypadLinux() {
void JoypadLinux::joy_thread_func(void *p_user) {
if (p_user) {
- JoypadLinux *joy = (JoypadLinux *)p_user;
+ JoypadLinux *joy = static_cast<JoypadLinux *>(p_user);
joy->run_joypad_thread();
}
}
diff --git a/platform/linuxbsd/os_linuxbsd.cpp b/platform/linuxbsd/os_linuxbsd.cpp
index 86874687ad..bf142f8804 100644
--- a/platform/linuxbsd/os_linuxbsd.cpp
+++ b/platform/linuxbsd/os_linuxbsd.cpp
@@ -131,20 +131,19 @@ void OS_LinuxBSD::initialize_joypads() {
String OS_LinuxBSD::get_unique_id() const {
static String machine_id;
if (machine_id.is_empty()) {
- if (FileAccess *f = FileAccess::open("/etc/machine-id", FileAccess::READ)) {
+ Ref<FileAccess> f = FileAccess::open("/etc/machine-id", FileAccess::READ);
+ if (f.is_valid()) {
while (machine_id.is_empty() && !f->eof_reached()) {
machine_id = f->get_line().strip_edges();
}
- f->close();
- memdelete(f);
}
}
return machine_id;
}
String OS_LinuxBSD::get_processor_name() const {
- FileAccessRef f = FileAccess::open("/proc/cpuinfo", FileAccess::READ);
- ERR_FAIL_COND_V_MSG(!f, "", String("Couldn't open `/proc/cpuinfo` to get the CPU model name. Returning an empty string."));
+ Ref<FileAccess> f = FileAccess::open("/proc/cpuinfo", FileAccess::READ);
+ ERR_FAIL_COND_V_MSG(f.is_null(), "", String("Couldn't open `/proc/cpuinfo` to get the CPU model name. Returning an empty string."));
while (!f->eof_reached()) {
const String line = f->get_line();
@@ -465,7 +464,7 @@ Error OS_LinuxBSD::move_to_trash(const String &p_path) {
// Create needed directories for decided trash can location.
{
- DirAccessRef dir_access = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
+ Ref<DirAccess> dir_access = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
Error err = dir_access->make_dir_recursive(trash_path);
// Issue an error if trash can is not created properly.
@@ -512,13 +511,14 @@ Error OS_LinuxBSD::move_to_trash(const String &p_path) {
String trash_info = "[Trash Info]\nPath=" + path.uri_encode() + "\nDeletionDate=" + timestamp + "\n";
{
Error err;
- FileAccessRef file = FileAccess::open(trash_path + "/info/" + file_name + ".trashinfo", FileAccess::WRITE, &err);
- ERR_FAIL_COND_V_MSG(err != OK, err, "Can't create trashinfo file: \"" + trash_path + "/info/" + file_name + ".trashinfo\"");
- file->store_string(trash_info);
- file->close();
+ {
+ Ref<FileAccess> file = FileAccess::open(trash_path + "/info/" + file_name + ".trashinfo", FileAccess::WRITE, &err);
+ ERR_FAIL_COND_V_MSG(err != OK, err, "Can't create trashinfo file: \"" + trash_path + "/info/" + file_name + ".trashinfo\"");
+ file->store_string(trash_info);
+ }
// Rename our resource before moving it to the trash can.
- DirAccessRef dir_access = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
+ Ref<DirAccess> dir_access = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
err = dir_access->rename(path, renamed_path);
ERR_FAIL_COND_V_MSG(err != OK, err, "Can't rename file \"" + path + "\" to \"" + renamed_path + "\"");
}
@@ -535,7 +535,7 @@ Error OS_LinuxBSD::move_to_trash(const String &p_path) {
// Issue an error if "mv" failed to move the given resource to the trash can.
if (err != OK || retval != 0) {
ERR_PRINT("move_to_trash: Could not move the resource \"" + path + "\" to the trash can \"" + trash_path + "/files\"");
- DirAccessRef dir_access = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
+ Ref<DirAccess> dir_access = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
err = dir_access->rename(renamed_path, path);
ERR_FAIL_COND_V_MSG(err != OK, err, "Could not rename \"" + renamed_path + "\" back to its original name: \"" + path + "\"");
return FAILED;
diff --git a/platform/linuxbsd/os_linuxbsd.h b/platform/linuxbsd/os_linuxbsd.h
index d3857e85f8..7b912ddee3 100644
--- a/platform/linuxbsd/os_linuxbsd.h
+++ b/platform/linuxbsd/os_linuxbsd.h
@@ -63,7 +63,7 @@ class OS_LinuxBSD : public OS_Unix {
CrashHandler crash_handler;
- MainLoop *main_loop;
+ MainLoop *main_loop = nullptr;
protected:
virtual void initialize() override;
diff --git a/platform/osx/detect.py b/platform/osx/detect.py
index 0ff93bedb4..8d848d2094 100644
--- a/platform/osx/detect.py
+++ b/platform/osx/detect.py
@@ -127,6 +127,7 @@ def configure(env):
if env["use_ubsan"] or env["use_asan"] or env["use_tsan"]:
env.extra_suffix += ".san"
+ env.Append(CCFLAGS=["-DSANITIZERS_ENABLED"])
if env["use_ubsan"]:
env.Append(
diff --git a/platform/osx/display_server_osx.h b/platform/osx/display_server_osx.h
index d9a0d2ce17..fa3091ff81 100644
--- a/platform/osx/display_server_osx.h
+++ b/platform/osx/display_server_osx.h
@@ -271,7 +271,7 @@ public:
virtual MouseMode mouse_get_mode() const override;
bool update_mouse_wrap(WindowData &p_wd, NSPoint &r_delta, NSPoint &r_mpos, NSTimeInterval p_timestamp);
- virtual void mouse_warp_to_position(const Point2i &p_to) override;
+ virtual void warp_mouse(const Point2i &p_position) override;
virtual Point2i mouse_get_position() const override;
void mouse_set_button_state(MouseButton p_state);
virtual MouseButton mouse_get_button_state() const override;
diff --git a/platform/osx/display_server_osx.mm b/platform/osx/display_server_osx.mm
index 8820201c10..c7381f06e3 100644
--- a/platform/osx/display_server_osx.mm
+++ b/platform/osx/display_server_osx.mm
@@ -1646,7 +1646,7 @@ bool DisplayServerOSX::update_mouse_wrap(WindowData &p_wd, NSPoint &r_delta, NSP
return false;
}
-void DisplayServerOSX::mouse_warp_to_position(const Point2i &p_to) {
+void DisplayServerOSX::warp_mouse(const Point2i &p_position) {
_THREAD_SAFE_METHOD_
if (mouse_mode != MOUSE_MODE_CAPTURED) {
@@ -1656,7 +1656,7 @@ void DisplayServerOSX::mouse_warp_to_position(const Point2i &p_to) {
// Local point in window coords.
const NSRect contentRect = [wd.window_view frame];
const float scale = screen_get_max_scale();
- NSRect pointInWindowRect = NSMakeRect(p_to.x / scale, contentRect.size.height - (p_to.y / scale - 1), 0, 0);
+ NSRect pointInWindowRect = NSMakeRect(p_position.x / scale, contentRect.size.height - (p_position.y / scale - 1), 0, 0);
NSPoint pointOnScreen = [[wd.window_view window] convertRectToScreen:pointInWindowRect].origin;
// Point in scren coords.
@@ -2892,14 +2892,13 @@ void DisplayServerOSX::swap_buffers() {
void DisplayServerOSX::set_native_icon(const String &p_filename) {
_THREAD_SAFE_METHOD_
- FileAccess *f = FileAccess::open(p_filename, FileAccess::READ);
- ERR_FAIL_COND(!f);
+ Ref<FileAccess> f = FileAccess::open(p_filename, FileAccess::READ);
+ ERR_FAIL_COND(f.is_null());
Vector<uint8_t> data;
uint64_t len = f->get_length();
data.resize(len);
f->get_buffer((uint8_t *)&data.write[0], len);
- memdelete(f);
NSData *icon_data = [[NSData alloc] initWithBytes:&data.write[0] length:len];
ERR_FAIL_COND_MSG(!icon_data, "Error reading icon data.");
@@ -3000,21 +2999,25 @@ Rect2i DisplayServerOSX::window_get_popup_safe_rect(WindowID p_window) const {
}
void DisplayServerOSX::popup_open(WindowID p_window) {
+ _THREAD_SAFE_METHOD_
+
WindowData &wd = windows[p_window];
if (wd.is_popup) {
bool was_empty = popup_list.is_empty();
- // Close all popups, up to current popup parent, or every popup if new window is not transient.
+ // Find current popup parent, or root popup if new window is not transient.
+ List<WindowID>::Element *C = nullptr;
List<WindowID>::Element *E = popup_list.back();
while (E) {
if (wd.transient_parent != E->get() || wd.transient_parent == INVALID_WINDOW_ID) {
- send_window_event(windows[E->get()], DisplayServerOSX::WINDOW_EVENT_CLOSE_REQUEST);
- List<WindowID>::Element *F = E->prev();
- popup_list.erase(E);
- E = F;
+ C = E;
+ E = E->prev();
} else {
break;
}
}
+ if (C) {
+ send_window_event(windows[C->get()], DisplayServerOSX::WINDOW_EVENT_CLOSE_REQUEST);
+ }
if (was_empty && popup_list.is_empty()) {
// Inform OS that popup was opened, to close other native popups.
@@ -3026,12 +3029,16 @@ void DisplayServerOSX::popup_open(WindowID p_window) {
}
void DisplayServerOSX::popup_close(WindowID p_window) {
+ _THREAD_SAFE_METHOD_
+
bool was_empty = popup_list.is_empty();
List<WindowID>::Element *E = popup_list.find(p_window);
while (E) {
- send_window_event(windows[E->get()], DisplayServerOSX::WINDOW_EVENT_CLOSE_REQUEST);
List<WindowID>::Element *F = E->next();
+ WindowID win_id = E->get();
popup_list.erase(E);
+
+ send_window_event(windows[win_id], DisplayServerOSX::WINDOW_EVENT_CLOSE_REQUEST);
E = F;
}
if (!was_empty && popup_list.is_empty()) {
@@ -3047,11 +3054,8 @@ void DisplayServerOSX::mouse_process_popups(bool p_close) {
if (p_close) {
// Close all popups.
List<WindowID>::Element *E = popup_list.front();
- while (E) {
+ if (E) {
send_window_event(windows[E->get()], DisplayServerOSX::WINDOW_EVENT_CLOSE_REQUEST);
- List<WindowID>::Element *F = E->next();
- popup_list.erase(E);
- E = F;
}
if (!was_empty) {
// Inform OS that all popups are closed.
@@ -3064,7 +3068,9 @@ void DisplayServerOSX::mouse_process_popups(bool p_close) {
}
Point2i pos = mouse_get_position();
+ List<WindowID>::Element *C = nullptr;
List<WindowID>::Element *E = popup_list.back();
+ // Find top popup to close.
while (E) {
// Popup window area.
Rect2i win_rect = Rect2i(window_get_position(E->get()), window_get_size(E->get()));
@@ -3075,12 +3081,13 @@ void DisplayServerOSX::mouse_process_popups(bool p_close) {
} else if (safe_rect != Rect2i() && safe_rect.has_point(pos)) {
break;
} else {
- send_window_event(windows[E->get()], DisplayServerOSX::WINDOW_EVENT_CLOSE_REQUEST);
- List<WindowID>::Element *F = E->prev();
- popup_list.erase(E);
- E = F;
+ C = E;
+ E = E->prev();
}
}
+ if (C) {
+ send_window_event(windows[C->get()], DisplayServerOSX::WINDOW_EVENT_CLOSE_REQUEST);
+ }
if (!was_empty && popup_list.is_empty()) {
// Inform OS that all popups are closed.
[[NSDistributedNotificationCenter defaultCenter] postNotificationName:@"com.apple.HIToolbox.endMenuTrackingNotification" object:@"org.godotengine.godot.popup_window"];
diff --git a/platform/osx/export/codesign.cpp b/platform/osx/export/codesign.cpp
index b609a21c44..fd044c00cc 100644
--- a/platform/osx/export/codesign.cpp
+++ b/platform/osx/export/codesign.cpp
@@ -49,8 +49,8 @@
/*************************************************************************/
String CodeSignCodeResources::hash_sha1_base64(const String &p_path) {
- FileAccessRef fa = FileAccess::open(p_path, FileAccess::READ);
- ERR_FAIL_COND_V_MSG(!fa, String(), vformat("CodeSign/CodeResources: Can't open file: \"%s\".", p_path));
+ Ref<FileAccess> fa = FileAccess::open(p_path, FileAccess::READ);
+ ERR_FAIL_COND_V_MSG(fa.is_null(), String(), vformat("CodeSign/CodeResources: Can't open file: \"%s\".", p_path));
CryptoCore::SHA1Context ctx;
ctx.start();
@@ -68,14 +68,13 @@ String CodeSignCodeResources::hash_sha1_base64(const String &p_path) {
unsigned char hash[0x14];
ctx.finish(hash);
- fa->close();
return CryptoCore::b64_encode_str(hash, 0x14);
}
String CodeSignCodeResources::hash_sha256_base64(const String &p_path) {
- FileAccessRef fa = FileAccess::open(p_path, FileAccess::READ);
- ERR_FAIL_COND_V_MSG(!fa, String(), vformat("CodeSign/CodeResources: Can't open file: \"%s\".", p_path));
+ Ref<FileAccess> fa = FileAccess::open(p_path, FileAccess::READ);
+ ERR_FAIL_COND_V_MSG(fa.is_null(), String(), vformat("CodeSign/CodeResources: Can't open file: \"%s\".", p_path));
CryptoCore::SHA256Context ctx;
ctx.start();
@@ -93,7 +92,6 @@ String CodeSignCodeResources::hash_sha256_base64(const String &p_path) {
unsigned char hash[0x20];
ctx.finish(hash);
- fa->close();
return CryptoCore::b64_encode_str(hash, 0x20);
}
@@ -211,16 +209,14 @@ bool CodeSignCodeResources::add_nested_file(const String &p_root, const String &
} \
}
- DirAccessRef da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
- ERR_FAIL_COND_V(!da, false);
+ Ref<DirAccess> da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
+ ERR_FAIL_COND_V(da.is_null(), false);
Vector<String> files_to_add;
if (LipO::is_lipo(p_exepath)) {
String tmp_path_name = EditorPaths::get_singleton()->get_cache_dir().plus_file("_lipo");
Error err = da->make_dir_recursive(tmp_path_name);
- if (err != OK) {
- ERR_FAIL_V_MSG(false, vformat("CodeSign/CodeResources: Failed to create \"%s\" subfolder.", tmp_path_name));
- }
+ ERR_FAIL_COND_V_MSG(err != OK, false, vformat("CodeSign/CodeResources: Failed to create \"%s\" subfolder.", tmp_path_name));
LipO lip;
if (lip.open_file(p_exepath)) {
for (int i = 0; i < lip.get_arch_count(); i++) {
@@ -287,8 +283,8 @@ bool CodeSignCodeResources::add_nested_file(const String &p_root, const String &
}
bool CodeSignCodeResources::add_folder_recursive(const String &p_root, const String &p_path, const String &p_main_exe_path) {
- DirAccessRef da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
- ERR_FAIL_COND_V(!da, false);
+ Ref<DirAccess> da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
+ ERR_FAIL_COND_V(da.is_null(), false);
Error err = da->change_dir(p_root.plus_file(p_path));
ERR_FAIL_COND_V(err != OK, false);
@@ -431,12 +427,11 @@ bool CodeSignCodeResources::save_to_file(const String &p_path) {
String text = pl.save_text();
ERR_FAIL_COND_V_MSG(text.is_empty(), false, "CodeSign/CodeResources: Generating resources PList failed.");
- FileAccessRef fa = FileAccess::open(p_path, FileAccess::WRITE);
- ERR_FAIL_COND_V_MSG(!fa, false, vformat("CodeSign/CodeResources: Can't open file: \"%s\".", p_path));
+ Ref<FileAccess> fa = FileAccess::open(p_path, FileAccess::WRITE);
+ ERR_FAIL_COND_V_MSG(fa.is_null(), false, vformat("CodeSign/CodeResources: Can't open file: \"%s\".", p_path));
CharString cs = text.utf8();
fa->store_buffer((const uint8_t *)cs.ptr(), cs.length());
- fa->close();
return true;
}
@@ -809,8 +804,8 @@ int CodeSignRequirements::get_size() const {
return blob.size();
}
-void CodeSignRequirements::write_to_file(FileAccess *p_file) const {
- ERR_FAIL_COND_MSG(!p_file, "CodeSign/Requirements: Invalid file handle.");
+void CodeSignRequirements::write_to_file(Ref<FileAccess> p_file) const {
+ ERR_FAIL_COND_MSG(p_file.is_null(), "CodeSign/Requirements: Invalid file handle.");
p_file->store_buffer(blob.ptr(), blob.size());
}
@@ -863,8 +858,8 @@ int CodeSignEntitlementsText::get_size() const {
return blob.size();
}
-void CodeSignEntitlementsText::write_to_file(FileAccess *p_file) const {
- ERR_FAIL_COND_MSG(!p_file, "CodeSign/EntitlementsText: Invalid file handle.");
+void CodeSignEntitlementsText::write_to_file(Ref<FileAccess> p_file) const {
+ ERR_FAIL_COND_MSG(p_file.is_null(), "CodeSign/EntitlementsText: Invalid file handle.");
p_file->store_buffer(blob.ptr(), blob.size());
}
@@ -918,8 +913,8 @@ int CodeSignEntitlementsBinary::get_size() const {
return blob.size();
}
-void CodeSignEntitlementsBinary::write_to_file(FileAccess *p_file) const {
- ERR_FAIL_COND_MSG(!p_file, "CodeSign/EntitlementsBinary: Invalid file handle.");
+void CodeSignEntitlementsBinary::write_to_file(Ref<FileAccess> p_file) const {
+ ERR_FAIL_COND_MSG(p_file.is_null(), "CodeSign/EntitlementsBinary: Invalid file handle.");
p_file->store_buffer(blob.ptr(), blob.size());
}
@@ -947,7 +942,7 @@ CodeSignCodeDirectory::CodeSignCodeDirectory(uint8_t p_hash_size, uint8_t p_hash
}
blob.resize(cd_size);
memset(blob.ptrw() + 8, 0x00, cd_size - 8);
- CodeDirectoryHeader *cd = (CodeDirectoryHeader *)(blob.ptrw() + 8);
+ CodeDirectoryHeader *cd = reinterpret_cast<CodeDirectoryHeader *>(blob.ptrw() + 8);
bool is_64_cl = (p_code_limit >= std::numeric_limits<uint32_t>::max());
@@ -1040,8 +1035,8 @@ int CodeSignCodeDirectory::get_size() const {
return blob.size();
}
-void CodeSignCodeDirectory::write_to_file(FileAccess *p_file) const {
- ERR_FAIL_COND_MSG(!p_file, "CodeSign/CodeDirectory: Invalid file handle.");
+void CodeSignCodeDirectory::write_to_file(Ref<FileAccess> p_file) const {
+ ERR_FAIL_COND_MSG(p_file.is_null(), "CodeSign/CodeDirectory: Invalid file handle.");
p_file->store_buffer(blob.ptr(), blob.size());
}
@@ -1086,8 +1081,8 @@ int CodeSignSignature::get_size() const {
return blob.size();
}
-void CodeSignSignature::write_to_file(FileAccess *p_file) const {
- ERR_FAIL_COND_MSG(!p_file, "CodeSign/Signature: Invalid file handle.");
+void CodeSignSignature::write_to_file(Ref<FileAccess> p_file) const {
+ ERR_FAIL_COND_MSG(p_file.is_null(), "CodeSign/Signature: Invalid file handle.");
p_file->store_buffer(blob.ptr(), blob.size());
}
@@ -1115,8 +1110,8 @@ int CodeSignSuperBlob::get_size() const {
return size;
}
-void CodeSignSuperBlob::write_to_file(FileAccess *p_file) const {
- ERR_FAIL_COND_MSG(!p_file, "CodeSign/SuperBlob: Invalid file handle.");
+void CodeSignSuperBlob::write_to_file(Ref<FileAccess> p_file) const {
+ ERR_FAIL_COND_MSG(p_file.is_null(), "CodeSign/SuperBlob: Invalid file handle.");
uint32_t size = get_size();
uint32_t data_offset = 12 + blobs.size() * 8;
@@ -1147,8 +1142,8 @@ void CodeSignSuperBlob::write_to_file(FileAccess *p_file) const {
PackedByteArray CodeSign::file_hash_sha1(const String &p_path) {
PackedByteArray file_hash;
- FileAccessRef f = FileAccess::open(p_path, FileAccess::READ);
- ERR_FAIL_COND_V_MSG(!f, PackedByteArray(), vformat("CodeSign: Can't open file: \"%s\".", p_path));
+ Ref<FileAccess> f = FileAccess::open(p_path, FileAccess::READ);
+ ERR_FAIL_COND_V_MSG(f.is_null(), PackedByteArray(), vformat("CodeSign: Can't open file: \"%s\".", p_path));
CryptoCore::SHA1Context ctx;
ctx.start();
@@ -1171,8 +1166,8 @@ PackedByteArray CodeSign::file_hash_sha1(const String &p_path) {
PackedByteArray CodeSign::file_hash_sha256(const String &p_path) {
PackedByteArray file_hash;
- FileAccessRef f = FileAccess::open(p_path, FileAccess::READ);
- ERR_FAIL_COND_V_MSG(!f, PackedByteArray(), vformat("CodeSign: Can't open file: \"%s\".", p_path));
+ Ref<FileAccess> f = FileAccess::open(p_path, FileAccess::READ);
+ ERR_FAIL_COND_V_MSG(f.is_null(), PackedByteArray(), vformat("CodeSign: Can't open file: \"%s\".", p_path));
CryptoCore::SHA256Context ctx;
ctx.start();
@@ -1208,8 +1203,8 @@ Error CodeSign::_codesign_file(bool p_use_hardened_runtime, bool p_force, const
String id;
String main_exe = p_exe_path;
- DirAccessRef da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
- if (!da) {
+ Ref<DirAccess> da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
+ if (da.is_null()) {
r_error_msg = TTR("Can't get filesystem access.");
ERR_FAIL_V_MSG(ERR_CANT_CREATE, "CodeSign: Can't get filesystem access.");
}
@@ -1522,8 +1517,8 @@ Error CodeSign::_codesign_file(bool p_use_hardened_runtime, bool p_force, const
}
Error CodeSign::codesign(bool p_use_hardened_runtime, bool p_force, const String &p_path, const String &p_ent_path, String &r_error_msg) {
- DirAccessRef da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
- if (!da) {
+ Ref<DirAccess> da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
+ if (da.is_null()) {
r_error_msg = TTR("Can't get filesystem access.");
ERR_FAIL_V_MSG(ERR_CANT_CREATE, "CodeSign: Can't get filesystem access.");
}
diff --git a/platform/osx/export/codesign.h b/platform/osx/export/codesign.h
index e5e9be5c28..3a08c0ea86 100644
--- a/platform/osx/export/codesign.h
+++ b/platform/osx/export/codesign.h
@@ -132,7 +132,7 @@ public:
virtual int get_size() const = 0;
virtual uint32_t get_index_type() const = 0;
- virtual void write_to_file(FileAccess *p_file) const = 0;
+ virtual void write_to_file(Ref<FileAccess> p_file) const = 0;
};
/*************************************************************************/
@@ -168,7 +168,7 @@ public:
virtual int get_size() const override;
virtual uint32_t get_index_type() const override { return 0x00000002; };
- virtual void write_to_file(FileAccess *p_file) const override;
+ virtual void write_to_file(Ref<FileAccess> p_file) const override;
};
/*************************************************************************/
@@ -190,7 +190,7 @@ public:
virtual int get_size() const override;
virtual uint32_t get_index_type() const override { return 0x00000005; };
- virtual void write_to_file(FileAccess *p_file) const override;
+ virtual void write_to_file(Ref<FileAccess> p_file) const override;
};
/*************************************************************************/
@@ -212,7 +212,7 @@ public:
virtual int get_size() const override;
virtual uint32_t get_index_type() const override { return 0x00000007; };
- virtual void write_to_file(FileAccess *p_file) const override;
+ virtual void write_to_file(Ref<FileAccess> p_file) const override;
};
/*************************************************************************/
@@ -314,7 +314,7 @@ public:
virtual int get_size() const override;
virtual uint32_t get_index_type() const override { return 0x00000000; };
- virtual void write_to_file(FileAccess *p_file) const override;
+ virtual void write_to_file(Ref<FileAccess> p_file) const override;
};
/*************************************************************************/
@@ -333,7 +333,7 @@ public:
virtual int get_size() const override;
virtual uint32_t get_index_type() const override { return 0x00010000; };
- virtual void write_to_file(FileAccess *p_file) const override;
+ virtual void write_to_file(Ref<FileAccess> p_file) const override;
};
/*************************************************************************/
@@ -347,7 +347,7 @@ public:
bool add_blob(const Ref<CodeSignBlob> &p_blob);
int get_size() const;
- void write_to_file(FileAccess *p_file) const;
+ void write_to_file(Ref<FileAccess> p_file) const;
};
/*************************************************************************/
diff --git a/platform/osx/export/export_plugin.cpp b/platform/osx/export/export_plugin.cpp
index 4f06342fac..94ef875072 100644
--- a/platform/osx/export/export_plugin.cpp
+++ b/platform/osx/export/export_plugin.cpp
@@ -256,22 +256,23 @@ void EditorExportPlatformOSX::_make_icon(const Ref<Image> &p_icon, Vector<uint8_
String path = EditorPaths::get_singleton()->get_cache_dir().plus_file("icon.png");
ResourceSaver::save(path, it);
- FileAccess *f = FileAccess::open(path, FileAccess::READ);
- if (!f) {
- // Clean up generated file.
- DirAccess::remove_file_or_error(path);
- ERR_FAIL();
- }
+ {
+ Ref<FileAccess> f = FileAccess::open(path, FileAccess::READ);
+ if (f.is_null()) {
+ // Clean up generated file.
+ DirAccess::remove_file_or_error(path);
+ ERR_FAIL();
+ }
- int ofs = data.size();
- uint64_t len = f->get_length();
- data.resize(data.size() + len + 8);
- f->get_buffer(&data.write[ofs + 8], len);
- memdelete(f);
- len += 8;
- len = BSWAP32(len);
- memcpy(&data.write[ofs], icon_infos[i].name, 4);
- encode_uint32(len, &data.write[ofs + 4]);
+ int ofs = data.size();
+ uint64_t len = f->get_length();
+ data.resize(data.size() + len + 8);
+ f->get_buffer(&data.write[ofs + 8], len);
+ len += 8;
+ len = BSWAP32(len);
+ memcpy(&data.write[ofs], icon_infos[i].name, 4);
+ encode_uint32(len, &data.write[ofs + 4]);
+ }
// Clean up generated file.
DirAccess::remove_file_or_error(path);
@@ -565,7 +566,7 @@ Error EditorExportPlatformOSX::_code_sign_directory(const Ref<EditorExportPreset
}
Error dir_access_error;
- DirAccessRef dir_access{ DirAccess::open(p_path, &dir_access_error) };
+ Ref<DirAccess> dir_access{ DirAccess::open(p_path, &dir_access_error) };
if (dir_access_error != OK) {
return dir_access_error;
@@ -603,7 +604,7 @@ Error EditorExportPlatformOSX::_code_sign_directory(const Ref<EditorExportPreset
return OK;
}
-Error EditorExportPlatformOSX::_copy_and_sign_files(DirAccessRef &dir_access, const String &p_src_path,
+Error EditorExportPlatformOSX::_copy_and_sign_files(Ref<DirAccess> &dir_access, const String &p_src_path,
const String &p_in_app_path, bool p_sign_enabled,
const Ref<EditorExportPreset> &p_preset, const String &p_ent_path,
bool p_should_error_on_non_code_sign) {
@@ -633,7 +634,7 @@ Error EditorExportPlatformOSX::_copy_and_sign_files(DirAccessRef &dir_access, co
}
Error EditorExportPlatformOSX::_export_osx_plugins_for(Ref<EditorExportPlugin> p_editor_export_plugin,
- const String &p_app_path_name, DirAccessRef &dir_access,
+ const String &p_app_path_name, Ref<DirAccess> &dir_access,
bool p_sign_enabled, const Ref<EditorExportPreset> &p_preset,
const String &p_ent_path) {
Error error{ OK };
@@ -683,8 +684,8 @@ Error EditorExportPlatformOSX::_create_dmg(const String &p_dmg_path, const Strin
}
Error EditorExportPlatformOSX::_export_debug_script(const Ref<EditorExportPreset> &p_preset, const String &p_app_name, const String &p_pkg_name, const String &p_path) {
- FileAccessRef f = FileAccess::open(p_path, FileAccess::WRITE);
- ERR_FAIL_COND_V(!f, ERR_CANT_CREATE);
+ Ref<FileAccess> f = FileAccess::open(p_path, FileAccess::WRITE);
+ ERR_FAIL_COND_V(f.is_null(), ERR_CANT_CREATE);
f->store_line("#!/bin/sh");
f->store_line("echo -ne '\\033c\\033]0;" + p_app_name + "\\a'");
@@ -721,8 +722,7 @@ Error EditorExportPlatformOSX::export_project(const Ref<EditorExportPreset> &p_p
return ERR_FILE_BAD_PATH;
}
- FileAccess *src_f = nullptr;
- zlib_filefunc_def io = zipio_create_io_from_file(&src_f);
+ zlib_filefunc_def io = zipio_create_io();
if (ep.step(TTR("Creating app bundle"), 0)) {
return ERR_SKIP;
@@ -778,8 +778,8 @@ Error EditorExportPlatformOSX::export_project(const Ref<EditorExportPreset> &p_p
Error err = OK;
- DirAccessRef tmp_app_dir = DirAccess::create_for_path(tmp_base_path_name);
- if (!tmp_app_dir) {
+ Ref<DirAccess> tmp_app_dir = DirAccess::create_for_path(tmp_base_path_name);
+ if (tmp_app_dir.is_null()) {
err = ERR_CANT_CREATE;
}
@@ -832,7 +832,7 @@ Error EditorExportPlatformOSX::export_project(const Ref<EditorExportPreset> &p_p
{
String fname = tmp_app_path_name + "/Contents/Resources/en.lproj";
tmp_app_dir->make_dir_recursive(fname);
- FileAccessRef f = FileAccess::open(fname + "/InfoPlist.strings", FileAccess::WRITE);
+ Ref<FileAccess> f = FileAccess::open(fname + "/InfoPlist.strings", FileAccess::WRITE);
f->store_line("/* Localized versions of Info.plist keys */");
f->store_line("");
f->store_line("CFBundleDisplayName = \"" + ProjectSettings::get_singleton()->get("application/config/name").operator String() + "\";");
@@ -878,7 +878,7 @@ Error EditorExportPlatformOSX::export_project(const Ref<EditorExportPreset> &p_p
String lang = tr->get_locale();
String fname = tmp_app_path_name + "/Contents/Resources/" + lang + ".lproj";
tmp_app_dir->make_dir_recursive(fname);
- FileAccessRef f = FileAccess::open(fname + "/InfoPlist.strings", FileAccess::WRITE);
+ Ref<FileAccess> f = FileAccess::open(fname + "/InfoPlist.strings", FileAccess::WRITE);
f->store_line("/* Localized versions of Info.plist keys */");
f->store_line("");
if (appnames.has(lang)) {
@@ -935,6 +935,9 @@ Error EditorExportPlatformOSX::export_project(const Ref<EditorExportPreset> &p_p
unz_file_info info;
char fname[16384];
ret = unzGetCurrentFileInfo(src_pkg_zip, &info, fname, 16384, nullptr, 0, nullptr, 0);
+ if (ret != UNZ_OK) {
+ break;
+ }
String file = String::utf8(fname);
@@ -993,12 +996,10 @@ Error EditorExportPlatformOSX::export_project(const Ref<EditorExportPreset> &p_p
if (!iconpath.is_empty()) {
if (iconpath.get_extension() == "icns") {
- FileAccess *icon = FileAccess::open(iconpath, FileAccess::READ);
- if (icon) {
+ Ref<FileAccess> icon = FileAccess::open(iconpath, FileAccess::READ);
+ if (icon.is_valid()) {
data.resize(icon->get_length());
icon->get_buffer(&data.write[0], icon->get_length());
- icon->close();
- memdelete(icon);
}
} else {
Ref<Image> icon;
@@ -1039,15 +1040,13 @@ Error EditorExportPlatformOSX::export_project(const Ref<EditorExportPreset> &p_p
err = tmp_app_dir->make_dir_recursive(file.get_base_dir());
}
if (err == OK) {
- FileAccess *f = FileAccess::open(file, FileAccess::WRITE);
- if (f) {
+ Ref<FileAccess> f = FileAccess::open(file, FileAccess::WRITE);
+ if (f.is_valid()) {
f->store_buffer(data.ptr(), data.size());
- f->close();
if (is_execute) {
// chmod with 0755 if the file is executable.
FileAccess::set_unix_permissions(file, 0755);
}
- memdelete(f);
} else {
err = ERR_CANT_CREATE;
}
@@ -1091,8 +1090,8 @@ Error EditorExportPlatformOSX::export_project(const Ref<EditorExportPreset> &p_p
if (sign_enabled && (ent_path.is_empty())) {
ent_path = EditorPaths::get_singleton()->get_cache_dir().plus_file(pkg_name + ".entitlements");
- FileAccess *ent_f = FileAccess::open(ent_path, FileAccess::WRITE);
- if (ent_f) {
+ Ref<FileAccess> ent_f = FileAccess::open(ent_path, FileAccess::WRITE);
+ if (ent_f.is_valid()) {
ent_f->store_line("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
ent_f->store_line("<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">");
ent_f->store_line("<plist version=\"1.0\">");
@@ -1213,16 +1212,13 @@ Error EditorExportPlatformOSX::export_project(const Ref<EditorExportPreset> &p_p
ent_f->store_line("</dict>");
ent_f->store_line("</plist>");
-
- ent_f->close();
- memdelete(ent_f);
} else {
err = ERR_CANT_CREATE;
}
if ((err == OK) && helpers.size() > 0) {
ent_f = FileAccess::open(hlp_ent_path, FileAccess::WRITE);
- if (ent_f) {
+ if (ent_f.is_valid()) {
ent_f->store_line("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
ent_f->store_line("<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">");
ent_f->store_line("<plist version=\"1.0\">");
@@ -1233,9 +1229,6 @@ Error EditorExportPlatformOSX::export_project(const Ref<EditorExportPreset> &p_p
ent_f->store_line("<true/>");
ent_f->store_line("</dict>");
ent_f->store_line("</plist>");
-
- ent_f->close();
- memdelete(ent_f);
} else {
err = ERR_CANT_CREATE;
}
@@ -1243,7 +1236,7 @@ Error EditorExportPlatformOSX::export_project(const Ref<EditorExportPreset> &p_p
}
if ((err == OK) && helpers.size() > 0) {
- DirAccessRef da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
+ Ref<DirAccess> da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
for (int i = 0; i < helpers.size(); i++) {
String hlp_path = helpers[i];
err = da->copy(hlp_path, tmp_app_path_name + "/Contents/Helpers/" + hlp_path.get_file());
@@ -1270,7 +1263,7 @@ Error EditorExportPlatformOSX::export_project(const Ref<EditorExportPreset> &p_p
}
if (err == OK) {
- DirAccessRef da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
+ Ref<DirAccess> da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
for (int i = 0; i < shared_objects.size(); i++) {
String src_path = ProjectSettings::get_singleton()->globalize_path(shared_objects[i].path);
if (shared_objects[i].target.is_empty()) {
@@ -1334,8 +1327,7 @@ Error EditorExportPlatformOSX::export_project(const Ref<EditorExportPreset> &p_p
OS::get_singleton()->move_to_trash(p_path);
}
- FileAccess *dst_f = nullptr;
- zlib_filefunc_def io_dst = zipio_create_io_from_file(&dst_f);
+ zlib_filefunc_def io_dst = zipio_create_io();
zipFile zip = zipOpen2(p_path.utf8().get_data(), APPEND_STATUS_CREATE, nullptr, &io_dst);
_zip_folder_recursive(zip, tmp_base_path_name, "", pkg_name);
@@ -1380,7 +1372,7 @@ Error EditorExportPlatformOSX::export_project(const Ref<EditorExportPreset> &p_p
void EditorExportPlatformOSX::_zip_folder_recursive(zipFile &p_zip, const String &p_root_path, const String &p_folder, const String &p_pkg_name) {
String dir = p_folder.is_empty() ? p_root_path : p_root_path.plus_file(p_folder);
- DirAccessRef da = DirAccess::open(dir);
+ Ref<DirAccess> da = DirAccess::open(dir);
da->list_dir_begin();
String f = da->get_next();
while (!f.is_empty()) {
@@ -1471,8 +1463,8 @@ void EditorExportPlatformOSX::_zip_folder_recursive(zipFile &p_zip, const String
0x0314, // "version made by", 0x03 - Unix, 0x14 - ZIP specification version 2.0, required to store Unix file permissions
0);
- FileAccessRef fa = FileAccess::open(dir.plus_file(f), FileAccess::READ);
- if (!fa) {
+ Ref<FileAccess> fa = FileAccess::open(dir.plus_file(f), FileAccess::READ);
+ if (fa.is_null()) {
ERR_FAIL_MSG(vformat("Can't open file to read from path \"%s\".", dir.plus_file(f)));
}
const int bufsize = 16384;
diff --git a/platform/osx/export/export_plugin.h b/platform/osx/export/export_plugin.h
index b3edfb7f90..013e5eaa71 100644
--- a/platform/osx/export/export_plugin.h
+++ b/platform/osx/export/export_plugin.h
@@ -58,11 +58,11 @@ class EditorExportPlatformOSX : public EditorExportPlatform {
Error _notarize(const Ref<EditorExportPreset> &p_preset, const String &p_path);
Error _code_sign(const Ref<EditorExportPreset> &p_preset, const String &p_path, const String &p_ent_path, bool p_warn = true);
Error _code_sign_directory(const Ref<EditorExportPreset> &p_preset, const String &p_path, const String &p_ent_path, bool p_should_error_on_non_code = true);
- Error _copy_and_sign_files(DirAccessRef &dir_access, const String &p_src_path, const String &p_in_app_path,
+ Error _copy_and_sign_files(Ref<DirAccess> &dir_access, const String &p_src_path, const String &p_in_app_path,
bool p_sign_enabled, const Ref<EditorExportPreset> &p_preset, const String &p_ent_path,
bool p_should_error_on_non_code_sign);
Error _export_osx_plugins_for(Ref<EditorExportPlugin> p_editor_export_plugin, const String &p_app_path_name,
- DirAccessRef &dir_access, bool p_sign_enabled, const Ref<EditorExportPreset> &p_preset,
+ Ref<DirAccess> &dir_access, bool p_sign_enabled, const Ref<EditorExportPreset> &p_preset,
const String &p_ent_path);
Error _create_dmg(const String &p_dmg_path, const String &p_pkg_name, const String &p_app_path_name);
void _zip_folder_recursive(zipFile &p_zip, const String &p_root_path, const String &p_folder, const String &p_pkg_name);
diff --git a/platform/osx/export/lipo.cpp b/platform/osx/export/lipo.cpp
index 66d2ecdbcf..82baf18c52 100644
--- a/platform/osx/export/lipo.cpp
+++ b/platform/osx/export/lipo.cpp
@@ -35,8 +35,8 @@
#ifdef MODULE_REGEX_ENABLED
bool LipO::is_lipo(const String &p_path) {
- FileAccessRef fb = FileAccess::open(p_path, FileAccess::READ);
- ERR_FAIL_COND_V_MSG(!fb, false, vformat("LipO: Can't open file: \"%s\".", p_path));
+ Ref<FileAccess> fb = FileAccess::open(p_path, FileAccess::READ);
+ ERR_FAIL_COND_V_MSG(fb.is_null(), false, vformat("LipO: Can't open file: \"%s\".", p_path));
uint32_t magic = fb->get_32();
return (magic == 0xbebafeca || magic == 0xcafebabe || magic == 0xbfbafeca || magic == 0xcafebabf);
}
@@ -45,7 +45,7 @@ bool LipO::create_file(const String &p_output_path, const PackedStringArray &p_f
close();
fa = FileAccess::open(p_output_path, FileAccess::WRITE);
- ERR_FAIL_COND_V_MSG(!fa, false, vformat("LipO: Can't open file: \"%s\".", p_output_path));
+ ERR_FAIL_COND_V_MSG(fa.is_null(), false, vformat("LipO: Can't open file: \"%s\".", p_output_path));
uint64_t max_size = 0;
for (int i = 0; i < p_files.size(); i++) {
@@ -64,8 +64,8 @@ bool LipO::create_file(const String &p_output_path, const PackedStringArray &p_f
archs.push_back(arch);
- FileAccessRef fb = FileAccess::open(p_files[i], FileAccess::READ);
- if (!fb) {
+ Ref<FileAccess> fb = FileAccess::open(p_files[i], FileAccess::READ);
+ if (fb.is_null()) {
close();
ERR_FAIL_V_MSG(false, vformat("LipO: Can't open file: \"%s.\"", p_files[i]));
}
@@ -101,8 +101,8 @@ bool LipO::create_file(const String &p_output_path, const PackedStringArray &p_f
// Write files and padding.
for (int i = 0; i < archs.size(); i++) {
- FileAccessRef fb = FileAccess::open(p_files[i], FileAccess::READ);
- if (!fb) {
+ Ref<FileAccess> fb = FileAccess::open(p_files[i], FileAccess::READ);
+ if (fb.is_null()) {
close();
ERR_FAIL_V_MSG(false, vformat("LipO: Can't open file: \"%s.\"", p_files[i]));
}
@@ -123,7 +123,6 @@ bool LipO::create_file(const String &p_output_path, const PackedStringArray &p_f
if (br > 0) {
fa->store_buffer(step, br);
}
- fb->close();
}
return true;
}
@@ -132,7 +131,7 @@ bool LipO::open_file(const String &p_path) {
close();
fa = FileAccess::open(p_path, FileAccess::READ);
- ERR_FAIL_COND_V_MSG(!fa, false, vformat("LipO: Can't open file: \"%s\".", p_path));
+ ERR_FAIL_COND_V_MSG(fa.is_null(), false, vformat("LipO: Can't open file: \"%s\".", p_path));
uint32_t magic = fa->get_32();
if (magic == 0xbebafeca) {
@@ -197,16 +196,16 @@ bool LipO::open_file(const String &p_path) {
}
int LipO::get_arch_count() const {
- ERR_FAIL_COND_V_MSG(!fa, 0, "LipO: File not opened.");
+ ERR_FAIL_COND_V_MSG(fa.is_null(), 0, "LipO: File not opened.");
return archs.size();
}
bool LipO::extract_arch(int p_index, const String &p_path) {
- ERR_FAIL_COND_V_MSG(!fa, false, "LipO: File not opened.");
+ ERR_FAIL_COND_V_MSG(fa.is_null(), false, "LipO: File not opened.");
ERR_FAIL_INDEX_V(p_index, archs.size(), false);
- FileAccessRef fb = FileAccess::open(p_path, FileAccess::WRITE);
- ERR_FAIL_COND_V_MSG(!fb, false, vformat("LipO: Can't open file: \"%s\".", p_path));
+ Ref<FileAccess> fb = FileAccess::open(p_path, FileAccess::WRITE);
+ ERR_FAIL_COND_V_MSG(fb.is_null(), false, vformat("LipO: Can't open file: \"%s\".", p_path));
fa->seek(archs[p_index].offset);
@@ -223,16 +222,10 @@ bool LipO::extract_arch(int p_index, const String &p_path) {
if (br > 0) {
fb->store_buffer(step, br);
}
- fb->close();
return true;
}
void LipO::close() {
- if (fa) {
- fa->close();
- memdelete(fa);
- fa = nullptr;
- }
archs.clear();
}
diff --git a/platform/osx/export/lipo.h b/platform/osx/export/lipo.h
index 68bbe42dd6..0e419be17e 100644
--- a/platform/osx/export/lipo.h
+++ b/platform/osx/export/lipo.h
@@ -50,7 +50,7 @@ class LipO : public RefCounted {
uint32_t align;
};
- FileAccess *fa = nullptr;
+ Ref<FileAccess> fa;
Vector<FatArch> archs;
static inline size_t PAD(size_t s, size_t a) {
diff --git a/platform/osx/export/macho.cpp b/platform/osx/export/macho.cpp
index 08f2a855b0..e6e67eff06 100644
--- a/platform/osx/export/macho.cpp
+++ b/platform/osx/export/macho.cpp
@@ -49,7 +49,7 @@ uint32_t MachO::seg_align(uint64_t p_vmaddr, uint32_t p_min, uint32_t p_max) {
}
bool MachO::alloc_signature(uint64_t p_size) {
- ERR_FAIL_COND_V_MSG(!fa, false, "MachO: File not opened.");
+ ERR_FAIL_COND_V_MSG(fa.is_null(), false, "MachO: File not opened.");
if (signature_offset != 0) {
// Nothing to do, already have signature load command.
return true;
@@ -103,15 +103,15 @@ bool MachO::alloc_signature(uint64_t p_size) {
}
bool MachO::is_macho(const String &p_path) {
- FileAccessRef fb = FileAccess::open(p_path, FileAccess::READ);
- ERR_FAIL_COND_V_MSG(!fb, false, vformat("MachO: Can't open file: \"%s\".", p_path));
+ Ref<FileAccess> fb = FileAccess::open(p_path, FileAccess::READ);
+ ERR_FAIL_COND_V_MSG(fb.is_null(), false, vformat("MachO: Can't open file: \"%s\".", p_path));
uint32_t magic = fb->get_32();
return (magic == 0xcefaedfe || magic == 0xfeedface || magic == 0xcffaedfe || magic == 0xfeedfacf);
}
bool MachO::open_file(const String &p_path) {
fa = FileAccess::open(p_path, FileAccess::READ_WRITE);
- ERR_FAIL_COND_V_MSG(!fa, false, vformat("MachO: Can't open file: \"%s\".", p_path));
+ ERR_FAIL_COND_V_MSG(fa.is_null(), false, vformat("MachO: Can't open file: \"%s\".", p_path));
uint32_t magic = fa->get_32();
MachHeader mach_header;
@@ -232,37 +232,37 @@ bool MachO::open_file(const String &p_path) {
}
uint64_t MachO::get_exe_base() {
- ERR_FAIL_COND_V_MSG(!fa, 0, "MachO: File not opened.");
+ ERR_FAIL_COND_V_MSG(fa.is_null(), 0, "MachO: File not opened.");
return exe_base;
}
uint64_t MachO::get_exe_limit() {
- ERR_FAIL_COND_V_MSG(!fa, 0, "MachO: File not opened.");
+ ERR_FAIL_COND_V_MSG(fa.is_null(), 0, "MachO: File not opened.");
return exe_limit;
}
int32_t MachO::get_align() {
- ERR_FAIL_COND_V_MSG(!fa, 0, "MachO: File not opened.");
+ ERR_FAIL_COND_V_MSG(fa.is_null(), 0, "MachO: File not opened.");
return align;
}
uint32_t MachO::get_cputype() {
- ERR_FAIL_COND_V_MSG(!fa, 0, "MachO: File not opened.");
+ ERR_FAIL_COND_V_MSG(fa.is_null(), 0, "MachO: File not opened.");
return cputype;
}
uint32_t MachO::get_cpusubtype() {
- ERR_FAIL_COND_V_MSG(!fa, 0, "MachO: File not opened.");
+ ERR_FAIL_COND_V_MSG(fa.is_null(), 0, "MachO: File not opened.");
return cpusubtype;
}
uint64_t MachO::get_size() {
- ERR_FAIL_COND_V_MSG(!fa, 0, "MachO: File not opened.");
+ ERR_FAIL_COND_V_MSG(fa.is_null(), 0, "MachO: File not opened.");
return fa->get_length();
}
uint64_t MachO::get_signature_offset() {
- ERR_FAIL_COND_V_MSG(!fa, 0, "MachO: File not opened.");
+ ERR_FAIL_COND_V_MSG(fa.is_null(), 0, "MachO: File not opened.");
ERR_FAIL_COND_V_MSG(signature_offset == 0, 0, "MachO: No signature load command.");
fa->seek(signature_offset + 8);
@@ -274,7 +274,7 @@ uint64_t MachO::get_signature_offset() {
}
uint64_t MachO::get_code_limit() {
- ERR_FAIL_COND_V_MSG(!fa, 0, "MachO: File not opened.");
+ ERR_FAIL_COND_V_MSG(fa.is_null(), 0, "MachO: File not opened.");
if (signature_offset == 0) {
return fa->get_length() + PAD(fa->get_length(), 16);
@@ -284,7 +284,7 @@ uint64_t MachO::get_code_limit() {
}
uint64_t MachO::get_signature_size() {
- ERR_FAIL_COND_V_MSG(!fa, 0, "MachO: File not opened.");
+ ERR_FAIL_COND_V_MSG(fa.is_null(), 0, "MachO: File not opened.");
ERR_FAIL_COND_V_MSG(signature_offset == 0, 0, "MachO: No signature load command.");
fa->seek(signature_offset + 12);
@@ -296,7 +296,7 @@ uint64_t MachO::get_signature_size() {
}
bool MachO::is_signed() {
- ERR_FAIL_COND_V_MSG(!fa, false, "MachO: File not opened.");
+ ERR_FAIL_COND_V_MSG(fa.is_null(), false, "MachO: File not opened.");
if (signature_offset == 0) {
return false;
}
@@ -325,7 +325,7 @@ bool MachO::is_signed() {
}
PackedByteArray MachO::get_cdhash_sha1() {
- ERR_FAIL_COND_V_MSG(!fa, PackedByteArray(), "MachO: File not opened.");
+ ERR_FAIL_COND_V_MSG(fa.is_null(), PackedByteArray(), "MachO: File not opened.");
if (signature_offset == 0) {
return PackedByteArray();
}
@@ -372,7 +372,7 @@ PackedByteArray MachO::get_cdhash_sha1() {
}
PackedByteArray MachO::get_cdhash_sha256() {
- ERR_FAIL_COND_V_MSG(!fa, PackedByteArray(), "MachO: File not opened.");
+ ERR_FAIL_COND_V_MSG(fa.is_null(), PackedByteArray(), "MachO: File not opened.");
if (signature_offset == 0) {
return PackedByteArray();
}
@@ -419,7 +419,7 @@ PackedByteArray MachO::get_cdhash_sha256() {
}
PackedByteArray MachO::get_requirements() {
- ERR_FAIL_COND_V_MSG(!fa, PackedByteArray(), "MachO: File not opened.");
+ ERR_FAIL_COND_V_MSG(fa.is_null(), PackedByteArray(), "MachO: File not opened.");
if (signature_offset == 0) {
return PackedByteArray();
}
@@ -451,16 +451,16 @@ PackedByteArray MachO::get_requirements() {
return PackedByteArray();
}
-const FileAccess *MachO::get_file() const {
+const Ref<FileAccess> MachO::get_file() const {
return fa;
}
-FileAccess *MachO::get_file() {
+Ref<FileAccess> MachO::get_file() {
return fa;
}
bool MachO::set_signature_size(uint64_t p_size) {
- ERR_FAIL_COND_V_MSG(!fa, false, "MachO: File not opened.");
+ ERR_FAIL_COND_V_MSG(fa.is_null(), false, "MachO: File not opened.");
// Ensure signature load command exists.
ERR_FAIL_COND_V_MSG(link_edit_offset == 0, false, "MachO: No __LINKEDIT segment found.");
@@ -545,12 +545,4 @@ bool MachO::set_signature_size(uint64_t p_size) {
return true;
}
-MachO::~MachO() {
- if (fa) {
- fa->close();
- memdelete(fa);
- fa = nullptr;
- }
-}
-
#endif // MODULE_REGEX_ENABLED
diff --git a/platform/osx/export/macho.h b/platform/osx/export/macho.h
index e09906898b..6cfc3c44f5 100644
--- a/platform/osx/export/macho.h
+++ b/platform/osx/export/macho.h
@@ -161,7 +161,7 @@ class MachO : public RefCounted {
uint32_t reserved3;
};
- FileAccess *fa = nullptr;
+ Ref<FileAccess> fa;
bool swap = false;
uint64_t lc_limit = 0;
@@ -203,13 +203,11 @@ public:
PackedByteArray get_requirements();
- const FileAccess *get_file() const;
- FileAccess *get_file();
+ const Ref<FileAccess> get_file() const;
+ Ref<FileAccess> get_file();
uint64_t get_signature_size();
bool set_signature_size(uint64_t p_size);
-
- ~MachO();
};
#endif // MODULE_REGEX_ENABLED
diff --git a/platform/osx/export/plist.cpp b/platform/osx/export/plist.cpp
index 553b864180..d089233b80 100644
--- a/platform/osx/export/plist.cpp
+++ b/platform/osx/export/plist.cpp
@@ -343,8 +343,8 @@ PList::PList(const String &p_string) {
bool PList::load_file(const String &p_filename) {
root = Ref<PListNode>();
- FileAccessRef fb = FileAccess::open(p_filename, FileAccess::READ);
- if (!fb) {
+ Ref<FileAccess> fb = FileAccess::open(p_filename, FileAccess::READ);
+ if (fb.is_null()) {
return false;
}
@@ -398,7 +398,6 @@ bool PList::load_string(const String &p_string) {
}
if (token == "/plist") {
- in_plist = false;
done_plist = true;
break;
}
diff --git a/platform/osx/godot_main_osx.mm b/platform/osx/godot_main_osx.mm
index f3db363151..053a7f4a1d 100644
--- a/platform/osx/godot_main_osx.mm
+++ b/platform/osx/godot_main_osx.mm
@@ -35,12 +35,22 @@
#include <string.h>
#include <unistd.h>
+#if defined(SANITIZERS_ENABLED)
+#include <sys/resource.h>
+#endif
+
int main(int argc, char **argv) {
#if defined(VULKAN_ENABLED)
// MoltenVK - enable full component swizzling support.
setenv("MVK_CONFIG_FULL_IMAGE_VIEW_SWIZZLE", "1", 1);
#endif
+#if defined(SANITIZERS_ENABLED)
+ // Note: Set stack size to be at least 30 MB (vs 8 MB default) to avoid overflow, address sanitizer can increase stack usage up to 3 times.
+ struct rlimit stack_lim = { 0x1E00000, 0x1E00000 };
+ setrlimit(RLIMIT_STACK, &stack_lim);
+#endif
+
int first_arg = 1;
const char *dbg_arg = "-NSDocumentRevisionsDebugMode";
printf("arguments\n");
diff --git a/platform/osx/joypad_osx.cpp b/platform/osx/joypad_osx.cpp
index 7d31ede61d..be9567e17c 100644
--- a/platform/osx/joypad_osx.cpp
+++ b/platform/osx/joypad_osx.cpp
@@ -195,7 +195,7 @@ void joypad::add_hid_element(IOHIDElementRef p_element) {
}
static void hid_element_added(const void *p_value, void *p_parameter) {
- joypad *joy = (joypad *)p_parameter;
+ joypad *joy = static_cast<joypad *>(p_parameter);
joy->add_hid_element((IOHIDElementRef)p_value);
}
@@ -540,10 +540,10 @@ static CFDictionaryRef create_match_dictionary(const UInt32 page, const UInt32 u
CFDictionaryRef retval = nullptr;
CFNumberRef pageNumRef = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &page);
CFNumberRef usageNumRef = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &usage);
- const void *keys[2] = { (void *)CFSTR(kIOHIDDeviceUsagePageKey), (void *)CFSTR(kIOHIDDeviceUsageKey) };
- const void *vals[2] = { (void *)pageNumRef, (void *)usageNumRef };
if (pageNumRef && usageNumRef) {
+ const void *keys[2] = { (void *)CFSTR(kIOHIDDeviceUsagePageKey), (void *)CFSTR(kIOHIDDeviceUsageKey) };
+ const void *vals[2] = { (void *)pageNumRef, (void *)usageNumRef };
retval = CFDictionaryCreate(kCFAllocatorDefault, keys, vals, 2, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
}
diff --git a/platform/osx/joypad_osx.h b/platform/osx/joypad_osx.h
index 4ca7fb1698..b09d5ce34a 100644
--- a/platform/osx/joypad_osx.h
+++ b/platform/osx/joypad_osx.h
@@ -94,7 +94,7 @@ class JoypadOSX {
};
private:
- Input *input;
+ Input *input = nullptr;
IOHIDManagerRef hid_manager;
Vector<joypad> device_list;
diff --git a/platform/osx/os_osx.mm b/platform/osx/os_osx.mm
index 7e0cf9f9cc..33fee01c08 100644
--- a/platform/osx/os_osx.mm
+++ b/platform/osx/os_osx.mm
@@ -47,7 +47,7 @@
_FORCE_INLINE_ String OS_OSX::get_framework_executable(const String &p_path) {
// Append framework executable name, or return as is if p_path is not a framework.
- DirAccessRef da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
+ Ref<DirAccess> da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
if (da->dir_exists(p_path) && da->file_exists(p_path.plus_file(p_path.get_file().get_basename()))) {
return p_path.plus_file(p_path.get_file().get_basename());
} else {
@@ -313,21 +313,22 @@ String OS_OSX::get_executable_path() const {
}
Error OS_OSX::create_process(const String &p_path, const List<String> &p_arguments, ProcessID *r_child_id, bool p_open_console) {
- if (@available(macOS 10.15, *)) {
- // Use NSWorkspace if path is an .app bundle.
- NSURL *url = [NSURL fileURLWithPath:@(p_path.utf8().get_data())];
- NSBundle *bundle = [NSBundle bundleWithURL:url];
- if (bundle) {
- NSMutableArray *arguments = [[NSMutableArray alloc] init];
- for (const List<String>::Element *E = p_arguments.front(); E; E = E->next()) {
- [arguments addObject:[NSString stringWithUTF8String:E->get().utf8().get_data()]];
- }
+ // Use NSWorkspace if path is an .app bundle.
+ NSURL *url = [NSURL fileURLWithPath:@(p_path.utf8().get_data())];
+ NSBundle *bundle = [NSBundle bundleWithURL:url];
+ if (bundle) {
+ NSMutableArray *arguments = [[NSMutableArray alloc] init];
+ for (const String &arg : p_arguments) {
+ [arguments addObject:[NSString stringWithUTF8String:arg.utf8().get_data()]];
+ }
+ if (@available(macOS 10.15, *)) {
NSWorkspaceOpenConfiguration *configuration = [[NSWorkspaceOpenConfiguration alloc] init];
[configuration setArguments:arguments];
[configuration setCreatesNewApplicationInstance:YES];
__block dispatch_semaphore_t lock = dispatch_semaphore_create(0);
__block Error err = ERR_TIMEOUT;
__block pid_t pid = 0;
+
[[NSWorkspace sharedWorkspace] openApplicationAtURL:url
configuration:configuration
completionHandler:^(NSRunningApplication *app, NSError *error) {
@@ -350,7 +351,19 @@ Error OS_OSX::create_process(const String &p_path, const List<String> &p_argumen
return err;
} else {
- return OS_Unix::create_process(p_path, p_arguments, r_child_id, p_open_console);
+ Error err = ERR_TIMEOUT;
+ NSError *error = nullptr;
+ NSRunningApplication *app = [[NSWorkspace sharedWorkspace] launchApplicationAtURL:url options:NSWorkspaceLaunchNewInstance configuration:[NSDictionary dictionaryWithObject:arguments forKey:NSWorkspaceLaunchConfigurationArguments] error:&error];
+ if (error) {
+ err = ERR_CANT_FORK;
+ NSLog(@"Failed to execute: %@", error.localizedDescription);
+ } else {
+ if (r_child_id) {
+ *r_child_id = (ProcessID)[app processIdentifier];
+ }
+ err = OK;
+ }
+ return err;
}
} else {
return OS_Unix::create_process(p_path, p_arguments, r_child_id, p_open_console);
diff --git a/platform/uwp/export/app_packager.cpp b/platform/uwp/export/app_packager.cpp
index e7978ff74d..2f70c3e74c 100644
--- a/platform/uwp/export/app_packager.cpp
+++ b/platform/uwp/export/app_packager.cpp
@@ -46,7 +46,7 @@ String AppxPackager::hash_block(const uint8_t *p_block_data, size_t p_block_len)
}
void AppxPackager::make_block_map(const String &p_path) {
- FileAccess *tmp_file = FileAccess::open(p_path, FileAccess::WRITE);
+ Ref<FileAccess> tmp_file = FileAccess::open(p_path, FileAccess::WRITE);
tmp_file->store_string("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>");
tmp_file->store_string("<BlockMap xmlns=\"http://schemas.microsoft.com/appx/2010/blockmap\" HashMethod=\"http://www.w3.org/2001/04/xmlenc#sha256\">");
@@ -69,9 +69,6 @@ void AppxPackager::make_block_map(const String &p_path) {
}
tmp_file->store_string("</BlockMap>");
-
- tmp_file->close();
- memdelete(tmp_file);
}
String AppxPackager::content_type(String p_extension) {
@@ -89,7 +86,7 @@ String AppxPackager::content_type(String p_extension) {
}
void AppxPackager::make_content_types(const String &p_path) {
- FileAccess *tmp_file = FileAccess::open(p_path, FileAccess::WRITE);
+ Ref<FileAccess> tmp_file = FileAccess::open(p_path, FileAccess::WRITE);
tmp_file->store_string("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
tmp_file->store_string("<Types xmlns=\"http://schemas.openxmlformats.org/package/2006/content-types\">");
@@ -118,9 +115,6 @@ void AppxPackager::make_content_types(const String &p_path) {
tmp_file->store_string("<Override PartName=\"/AppxMetadata/CodeIntegrity.cat\" ContentType=\"application/vnd.ms-pkiseccat\" />");
tmp_file->store_string("</Types>");
-
- tmp_file->close();
- memdelete(tmp_file);
}
Vector<uint8_t> AppxPackager::make_file_header(FileMeta p_file_meta) {
@@ -285,7 +279,7 @@ Vector<uint8_t> AppxPackager::make_end_of_central_record() {
return buf;
}
-void AppxPackager::init(FileAccess *p_fa) {
+void AppxPackager::init(Ref<FileAccess> p_fa) {
package = p_fa;
central_dir_offset = 0;
end_of_central_dir_offset = 0;
@@ -301,7 +295,6 @@ Error AppxPackager::add_file(String p_file_name, const uint8_t *p_buffer, size_t
FileMeta meta;
meta.name = p_file_name;
meta.uncompressed_size = p_len;
- meta.compressed_size = p_len;
meta.compressed = p_compress;
meta.zip_offset = package->get_position();
@@ -309,7 +302,6 @@ Error AppxPackager::add_file(String p_file_name, const uint8_t *p_buffer, size_t
// Data for compression
z_stream strm;
- FileAccess *strm_f = nullptr;
Vector<uint8_t> strm_in;
strm_in.resize(BLOCK_SIZE);
Vector<uint8_t> strm_out;
@@ -317,7 +309,7 @@ Error AppxPackager::add_file(String p_file_name, const uint8_t *p_buffer, size_t
if (p_compress) {
strm.zalloc = zipio_alloc;
strm.zfree = zipio_free;
- strm.opaque = &strm_f;
+ strm.opaque = Z_NULL;
strm_out.resize(BLOCK_SIZE + 8);
@@ -419,16 +411,15 @@ void AppxPackager::finish() {
const String &tmp_blockmap_file_path = EditorPaths::get_singleton()->get_cache_dir().plus_file("tmpblockmap.xml");
make_block_map(tmp_blockmap_file_path);
- FileAccess *blockmap_file = FileAccess::open(tmp_blockmap_file_path, FileAccess::READ);
- Vector<uint8_t> blockmap_buffer;
- blockmap_buffer.resize(blockmap_file->get_length());
-
- blockmap_file->get_buffer(blockmap_buffer.ptrw(), blockmap_buffer.size());
+ {
+ Ref<FileAccess> blockmap_file = FileAccess::open(tmp_blockmap_file_path, FileAccess::READ);
+ Vector<uint8_t> blockmap_buffer;
+ blockmap_buffer.resize(blockmap_file->get_length());
- add_file("AppxBlockMap.xml", blockmap_buffer.ptr(), blockmap_buffer.size(), -1, -1, true);
+ blockmap_file->get_buffer(blockmap_buffer.ptrw(), blockmap_buffer.size());
- blockmap_file->close();
- memdelete(blockmap_file);
+ add_file("AppxBlockMap.xml", blockmap_buffer.ptr(), blockmap_buffer.size(), -1, -1, true);
+ }
// Add content types
@@ -437,16 +428,15 @@ void AppxPackager::finish() {
const String &tmp_content_types_file_path = EditorPaths::get_singleton()->get_cache_dir().plus_file("tmpcontenttypes.xml");
make_content_types(tmp_content_types_file_path);
- FileAccess *types_file = FileAccess::open(tmp_content_types_file_path, FileAccess::READ);
- Vector<uint8_t> types_buffer;
- types_buffer.resize(types_file->get_length());
-
- types_file->get_buffer(types_buffer.ptrw(), types_buffer.size());
+ {
+ Ref<FileAccess> types_file = FileAccess::open(tmp_content_types_file_path, FileAccess::READ);
+ Vector<uint8_t> types_buffer;
+ types_buffer.resize(types_file->get_length());
- add_file("[Content_Types].xml", types_buffer.ptr(), types_buffer.size(), -1, -1, true);
+ types_file->get_buffer(types_buffer.ptrw(), types_buffer.size());
- types_file->close();
- memdelete(types_file);
+ add_file("[Content_Types].xml", types_buffer.ptr(), types_buffer.size(), -1, -1, true);
+ }
// Cleanup generated files.
DirAccess::remove_file_or_error(tmp_blockmap_file_path);
@@ -467,9 +457,7 @@ void AppxPackager::finish() {
Vector<uint8_t> end_record = make_end_of_central_record();
package->store_buffer(end_record.ptr(), end_record.size());
- package->close();
- memdelete(package);
- package = nullptr;
+ package.unref();
}
AppxPackager::AppxPackager() {}
diff --git a/platform/uwp/export/app_packager.h b/platform/uwp/export/app_packager.h
index da118449c7..a32b78bf04 100644
--- a/platform/uwp/export/app_packager.h
+++ b/platform/uwp/export/app_packager.h
@@ -87,7 +87,7 @@ class AppxPackager {
};
String progress_task;
- FileAccess *package = nullptr;
+ Ref<FileAccess> package;
Set<String> mime_types;
@@ -138,7 +138,7 @@ class AppxPackager {
public:
void set_progress_task(String p_task) { progress_task = p_task; }
- void init(FileAccess *p_fa);
+ void init(Ref<FileAccess> p_fa);
Error add_file(String p_file_name, const uint8_t *p_buffer, size_t p_len, int p_file_no, int p_total_files, bool p_compress = false);
void finish();
diff --git a/platform/uwp/export/export_plugin.cpp b/platform/uwp/export/export_plugin.cpp
index 230e5c749c..7e06bf01e3 100644
--- a/platform/uwp/export/export_plugin.cpp
+++ b/platform/uwp/export/export_plugin.cpp
@@ -131,6 +131,14 @@ void EditorExportPlatformUWP::get_export_options(List<ExportOption> *r_options)
}
bool EditorExportPlatformUWP::can_export(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates) const {
+#ifndef DEV_ENABLED
+ // We don't provide export templates for the UWP platform currently as it
+ // has not been ported for Godot 4.0. This is skipped in DEV_ENABLED so that
+ // contributors can still test the pipeline if/when we can build it again.
+ r_error = "The UWP platform is currently not supported in Godot 4.0.\n";
+ return false;
+#endif
+
String err;
bool valid = false;
@@ -287,14 +295,13 @@ Error EditorExportPlatformUWP::export_project(const Ref<EditorExportPreset> &p_p
Error err = OK;
- FileAccess *fa_pack = FileAccess::open(p_path, FileAccess::WRITE, &err);
+ Ref<FileAccess> fa_pack = FileAccess::open(p_path, FileAccess::WRITE, &err);
ERR_FAIL_COND_V_MSG(err != OK, ERR_CANT_CREATE, "Cannot create file '" + p_path + "'.");
AppxPackager packager;
packager.init(fa_pack);
- FileAccess *src_f = nullptr;
- zlib_filefunc_def io = zipio_create_io_from_file(&src_f);
+ zlib_filefunc_def io = zipio_create_io();
if (ep.step("Creating package...", 0)) {
return ERR_SKIP;
@@ -324,6 +331,9 @@ Error EditorExportPlatformUWP::export_project(const Ref<EditorExportPreset> &p_p
unz_file_info info;
char fname[16834];
ret = unzGetCurrentFileInfo(pkg, &info, fname, 16834, nullptr, 0, nullptr, 0);
+ if (ret != UNZ_OK) {
+ break;
+ }
String path = String::utf8(fname);
diff --git a/platform/uwp/export/export_plugin.h b/platform/uwp/export/export_plugin.h
index bf89b10ffa..ceb6d613b3 100644
--- a/platform/uwp/export/export_plugin.h
+++ b/platform/uwp/export/export_plugin.h
@@ -346,21 +346,21 @@ class EditorExportPlatformUWP : public EditorExportPlatform {
ERR_FAIL_V_MSG(data, err_string);
}
- FileAccess *f = FileAccess::open(tmp_path, FileAccess::READ, &err);
+ {
+ Ref<FileAccess> f = FileAccess::open(tmp_path, FileAccess::READ, &err);
+
+ if (err != OK) {
+ String err_string = "Couldn't open temp logo file.";
+ // Cleanup generated file.
+ DirAccess::remove_file_or_error(tmp_path);
+ EditorNode::add_io_error(err_string);
+ ERR_FAIL_V_MSG(data, err_string);
+ }
- if (err != OK) {
- String err_string = "Couldn't open temp logo file.";
- // Cleanup generated file.
- DirAccess::remove_file_or_error(tmp_path);
- EditorNode::add_io_error(err_string);
- ERR_FAIL_V_MSG(data, err_string);
+ data.resize(f->get_length());
+ f->get_buffer(data.ptrw(), data.size());
}
- data.resize(f->get_length());
- f->get_buffer(data.ptrw(), data.size());
-
- f->close();
- memdelete(f);
DirAccess::remove_file_or_error(tmp_path);
return data;
@@ -417,7 +417,7 @@ class EditorExportPlatformUWP : public EditorExportPlatform {
}
static Error save_appx_file(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) {
- AppxPackager *packager = (AppxPackager *)p_userdata;
+ AppxPackager *packager = static_cast<AppxPackager *>(p_userdata);
String dst_path = p_path.replace_first("res://", "game/");
return packager->add_file(dst_path, p_data.ptr(), p_data.size(), p_file, p_total, _should_compress_asset(p_path, p_data));
diff --git a/platform/uwp/joypad_uwp.h b/platform/uwp/joypad_uwp.h
index 29f5109056..0869f1961d 100644
--- a/platform/uwp/joypad_uwp.h
+++ b/platform/uwp/joypad_uwp.h
@@ -68,7 +68,7 @@ private:
ControllerDevice controllers[MAX_CONTROLLERS];
- InputDefault *input;
+ InputDefault *input = nullptr;
void OnGamepadAdded(Platform::Object ^ sender, Windows::Gaming::Input::Gamepad ^ value);
void OnGamepadRemoved(Platform::Object ^ sender, Windows::Gaming::Input::Gamepad ^ value);
diff --git a/platform/uwp/os_uwp.h b/platform/uwp/os_uwp.h
index 573d86af7c..f955be1da9 100644
--- a/platform/uwp/os_uwp.h
+++ b/platform/uwp/os_uwp.h
@@ -74,7 +74,7 @@ private:
KEY_EVENT_BUFFER_SIZE = 512
};
- FILE *stdo;
+ FILE *stdo = nullptr;
KeyEvent key_event_buffer[KEY_EVENT_BUFFER_SIZE];
int key_event_pos;
@@ -87,16 +87,16 @@ private:
bool outside;
int old_x, old_y;
Point2i center;
- RenderingServer *rendering_server;
+ RenderingServer *rendering_server = nullptr;
int pressrc;
- ContextEGL_UWP *gl_context;
+ ContextEGL_UWP *gl_context = nullptr;
Windows::UI::Core::CoreWindow ^ window;
VideoMode video_mode;
int video_driver_index;
- MainLoop *main_loop;
+ MainLoop *main_loop = nullptr;
AudioDriverXAudio2 audio_driver;
@@ -111,7 +111,7 @@ private:
CursorShape cursor_shape;
- InputDefault *input;
+ InputDefault *input = nullptr;
JoypadUWP ^ joypad;
diff --git a/platform/windows/display_server_windows.cpp b/platform/windows/display_server_windows.cpp
index d243d4c05d..736dc7d383 100644
--- a/platform/windows/display_server_windows.cpp
+++ b/platform/windows/display_server_windows.cpp
@@ -150,7 +150,7 @@ DisplayServer::MouseMode DisplayServerWindows::mouse_get_mode() const {
return mouse_mode;
}
-void DisplayServerWindows::mouse_warp_to_position(const Point2i &p_to) {
+void DisplayServerWindows::warp_mouse(const Point2i &p_position) {
_THREAD_SAFE_METHOD_
if (!windows.has(last_focused_window)) {
@@ -158,12 +158,12 @@ void DisplayServerWindows::mouse_warp_to_position(const Point2i &p_to) {
}
if (mouse_mode == MOUSE_MODE_CAPTURED) {
- old_x = p_to.x;
- old_y = p_to.y;
+ old_x = p_position.x;
+ old_y = p_position.y;
} else {
POINT p;
- p.x = p_to.x;
- p.y = p_to.y;
+ p.x = p_position.x;
+ p.y = p_position.y;
ClientToScreen(windows[last_focused_window].hWnd, &p);
SetCursorPos(p.x, p.y);
@@ -430,9 +430,8 @@ static int QueryDpiForMonitor(HMONITOR hmon, _MonitorDpiType dpiType = MDT_Defau
}
UINT x = 0, y = 0;
- HRESULT hr = E_FAIL;
if (hmon && (Shcore != (HMODULE)INVALID_HANDLE_VALUE)) {
- hr = getDPIForMonitor(hmon, dpiType /*MDT_Effective_DPI*/, &x, &y);
+ HRESULT hr = getDPIForMonitor(hmon, dpiType /*MDT_Effective_DPI*/, &x, &y);
if (SUCCEEDED(hr) && (x > 0) && (y > 0)) {
dpiX = (int)x;
dpiY = (int)y;
@@ -844,8 +843,8 @@ void DisplayServerWindows::window_set_exclusive(WindowID p_window, bool p_exclus
if (wd.exclusive != p_exclusive) {
wd.exclusive = p_exclusive;
if (wd.transient_parent != INVALID_WINDOW_ID) {
- WindowData &wd_parent = windows[wd.transient_parent];
if (wd.exclusive) {
+ WindowData &wd_parent = windows[wd.transient_parent];
SetWindowLongPtr(wd.hWnd, GWLP_HWNDPARENT, (LONG_PTR)wd_parent.hWnd);
} else {
SetWindowLongPtr(wd.hWnd, GWLP_HWNDPARENT, (LONG_PTR) nullptr);
@@ -1281,7 +1280,7 @@ void DisplayServerWindows::window_request_attention(WindowID p_window) {
_THREAD_SAFE_METHOD_
ERR_FAIL_COND(!windows.has(p_window));
- WindowData &wd = windows[p_window];
+ const WindowData &wd = windows[p_window];
FLASHWINFO info;
info.cbSize = sizeof(FLASHWINFO);
@@ -1562,13 +1561,8 @@ void DisplayServerWindows::cursor_set_custom_image(const RES &p_cursor, CursorSh
}
}
- if (hAndMask != nullptr) {
- DeleteObject(hAndMask);
- }
-
- if (hXorMask != nullptr) {
- DeleteObject(hXorMask);
- }
+ DeleteObject(hAndMask);
+ DeleteObject(hXorMask);
memfree(buffer);
DeleteObject(bitmap);
@@ -1771,8 +1765,8 @@ void DisplayServerWindows::swap_buffers() {
void DisplayServerWindows::set_native_icon(const String &p_filename) {
_THREAD_SAFE_METHOD_
- FileAccess *f = FileAccess::open(p_filename, FileAccess::READ);
- ERR_FAIL_COND_MSG(!f, "Cannot open file with icon '" + p_filename + "'.");
+ Ref<FileAccess> f = FileAccess::open(p_filename, FileAccess::READ);
+ ERR_FAIL_COND_MSG(f.is_null(), "Cannot open file with icon '" + p_filename + "'.");
ICONDIR *icon_dir = (ICONDIR *)memalloc(sizeof(ICONDIR));
int pos = 0;
@@ -1858,7 +1852,6 @@ void DisplayServerWindows::set_native_icon(const String &p_filename) {
err = GetLastError();
ERR_FAIL_COND_MSG(err, "Error setting ICON_BIG: " + format_error_message(err) + ".");
- memdelete(f);
memdelete(icon_dir);
}
@@ -1994,7 +1987,7 @@ void DisplayServerWindows::_send_window_event(const WindowData &wd, WindowEvent
}
void DisplayServerWindows::_dispatch_input_events(const Ref<InputEvent> &p_event) {
- ((DisplayServerWindows *)(get_singleton()))->_dispatch_input_event(p_event);
+ static_cast<DisplayServerWindows *>(get_singleton())->_dispatch_input_event(p_event);
}
void DisplayServerWindows::_dispatch_input_event(const Ref<InputEvent> &p_event) {
@@ -2081,20 +2074,24 @@ Rect2i DisplayServerWindows::window_get_popup_safe_rect(WindowID p_window) const
}
void DisplayServerWindows::popup_open(WindowID p_window) {
- WindowData &wd = windows[p_window];
+ _THREAD_SAFE_METHOD_
+
+ const WindowData &wd = windows[p_window];
if (wd.is_popup) {
- // Close all popups, up to current popup parent, or every popup if new window is not transient.
+ // Find current popup parent, or root popup if new window is not transient.
+ List<WindowID>::Element *C = nullptr;
List<WindowID>::Element *E = popup_list.back();
while (E) {
if (wd.transient_parent != E->get() || wd.transient_parent == INVALID_WINDOW_ID) {
- _send_window_event(windows[E->get()], DisplayServerWindows::WINDOW_EVENT_CLOSE_REQUEST);
- List<WindowID>::Element *F = E->prev();
- popup_list.erase(E);
- E = F;
+ C = E;
+ E = E->prev();
} else {
break;
}
}
+ if (C) {
+ _send_window_event(windows[C->get()], DisplayServerWindows::WINDOW_EVENT_CLOSE_REQUEST);
+ }
time_since_popup = OS::get_singleton()->get_ticks_msec();
popup_list.push_back(p_window);
@@ -2102,17 +2099,22 @@ void DisplayServerWindows::popup_open(WindowID p_window) {
}
void DisplayServerWindows::popup_close(WindowID p_window) {
+ _THREAD_SAFE_METHOD_
+
List<WindowID>::Element *E = popup_list.find(p_window);
while (E) {
- _send_window_event(windows[E->get()], DisplayServerWindows::WINDOW_EVENT_CLOSE_REQUEST);
List<WindowID>::Element *F = E->next();
+ WindowID win_id = E->get();
popup_list.erase(E);
+
+ _send_window_event(windows[win_id], DisplayServerWindows::WINDOW_EVENT_CLOSE_REQUEST);
E = F;
}
}
LRESULT DisplayServerWindows::MouseProc(int code, WPARAM wParam, LPARAM lParam) {
_THREAD_SAFE_METHOD_
+
uint64_t delta = OS::get_singleton()->get_ticks_msec() - time_since_popup;
if (delta > 250) {
switch (wParam) {
@@ -2123,7 +2125,9 @@ LRESULT DisplayServerWindows::MouseProc(int code, WPARAM wParam, LPARAM lParam)
case WM_MBUTTONDOWN: {
MOUSEHOOKSTRUCT *ms = (MOUSEHOOKSTRUCT *)lParam;
Point2i pos = Point2i(ms->pt.x, ms->pt.y);
+ List<WindowID>::Element *C = nullptr;
List<WindowID>::Element *E = popup_list.back();
+ // Find top popup to close.
while (E) {
// Popup window area.
Rect2i win_rect = Rect2i(window_get_position(E->get()), window_get_size(E->get()));
@@ -2134,13 +2138,13 @@ LRESULT DisplayServerWindows::MouseProc(int code, WPARAM wParam, LPARAM lParam)
} else if (safe_rect != Rect2i() && safe_rect.has_point(pos)) {
break;
} else {
- _send_window_event(windows[E->get()], DisplayServerWindows::WINDOW_EVENT_CLOSE_REQUEST);
- List<WindowID>::Element *F = E->prev();
- popup_list.erase(E);
- E = F;
+ C = E;
+ E = E->prev();
}
}
-
+ if (C) {
+ _send_window_event(windows[C->get()], DisplayServerWindows::WINDOW_EVENT_CLOSE_REQUEST);
+ }
} break;
}
}
@@ -2849,7 +2853,7 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA
}
}
} else {
- // For reasons unknown to mankind, wheel comes in screen coordinates.
+ // For reasons unknown to humanity, wheel comes in screen coordinates.
POINT coords;
coords.x = mb->get_position().x;
coords.y = mb->get_position().y;
@@ -3483,7 +3487,7 @@ DisplayServerWindows::DisplayServerWindows(const String &p_rendering_driver, Win
shift_mem = false;
control_mem = false;
meta_mem = false;
- hInstance = ((OS_Windows *)OS::get_singleton())->get_hinstance();
+ hInstance = static_cast<OS_Windows *>(OS::get_singleton())->get_hinstance();
pressrc = 0;
old_invalid = true;
@@ -3626,7 +3630,11 @@ DisplayServerWindows::DisplayServerWindows(const String &p_rendering_driver, Win
}
#endif
- if (!OS::get_singleton()->is_in_low_processor_usage_mode()) {
+ if (!Engine::get_singleton()->is_editor_hint() && !OS::get_singleton()->is_in_low_processor_usage_mode()) {
+ // Increase priority for projects that are not in low-processor mode (typically games)
+ // to reduce the risk of frame stuttering.
+ // This is not done for the editor to prevent importers or resource bakers
+ // from making the system unresponsive.
SetPriorityClass(GetCurrentProcess(), ABOVE_NORMAL_PRIORITY_CLASS);
DWORD index = 0;
HANDLE handle = AvSetMmThreadCharacteristics("Games", &index);
@@ -3647,7 +3655,7 @@ DisplayServerWindows::DisplayServerWindows(const String &p_rendering_driver, Win
r_error = OK;
- ((OS_Windows *)OS::get_singleton())->set_main_window(windows[MAIN_WINDOW_ID].hWnd);
+ static_cast<OS_Windows *>(OS::get_singleton())->set_main_window(windows[MAIN_WINDOW_ID].hWnd);
Input::get_singleton()->set_event_dispatch_function(_dispatch_input_events);
}
diff --git a/platform/windows/display_server_windows.h b/platform/windows/display_server_windows.h
index a56a2b83ac..fcf4b5a728 100644
--- a/platform/windows/display_server_windows.h
+++ b/platform/windows/display_server_windows.h
@@ -392,7 +392,7 @@ class DisplayServerWindows : public DisplayServer {
Rect2i parent_safe_rect;
};
- JoypadWindows *joypad;
+ JoypadWindows *joypad = nullptr;
HHOOK mouse_monitor = nullptr;
List<WindowID> popup_list;
uint64_t time_since_popup = 0;
@@ -457,7 +457,7 @@ public:
virtual void mouse_set_mode(MouseMode p_mode) override;
virtual MouseMode mouse_get_mode() const override;
- virtual void mouse_warp_to_position(const Point2i &p_to) override;
+ virtual void warp_mouse(const Point2i &p_position) override;
virtual Point2i mouse_get_position() const override;
virtual MouseButton mouse_get_button_state() const override;
diff --git a/platform/windows/export/export_plugin.cpp b/platform/windows/export/export_plugin.cpp
index e627253739..917a0af90b 100644
--- a/platform/windows/export/export_plugin.cpp
+++ b/platform/windows/export/export_plugin.cpp
@@ -42,8 +42,8 @@ Error EditorExportPlatformWindows::sign_shared_object(const Ref<EditorExportPres
}
Error EditorExportPlatformWindows::_export_debug_script(const Ref<EditorExportPreset> &p_preset, const String &p_app_name, const String &p_pkg_name, const String &p_path) {
- FileAccessRef f = FileAccess::open(p_path, FileAccess::WRITE);
- ERR_FAIL_COND_V(!f, ERR_CANT_CREATE);
+ Ref<FileAccess> f = FileAccess::open(p_path, FileAccess::WRITE);
+ ERR_FAIL_COND_V(f.is_null(), ERR_CANT_CREATE);
f->store_line("@echo off");
f->store_line("title \"" + p_app_name + "\"");
@@ -359,7 +359,7 @@ Error EditorExportPlatformWindows::_code_sign(const Ref<EditorExportPreset> &p_p
}
#ifndef WINDOWS_ENABLED
- DirAccessRef tmp_dir = DirAccess::create_for_path(p_path.get_base_dir());
+ Ref<DirAccess> tmp_dir = DirAccess::create_for_path(p_path.get_base_dir());
err = tmp_dir->remove(p_path);
ERR_FAIL_COND_V(err != OK, err);
@@ -417,8 +417,8 @@ bool EditorExportPlatformWindows::can_export(const Ref<EditorExportPreset> &p_pr
Error EditorExportPlatformWindows::fixup_embedded_pck(const String &p_path, int64_t p_embedded_start, int64_t p_embedded_size) const {
// Patch the header of the "pck" section in the PE file so that it corresponds to the embedded data
- FileAccess *f = FileAccess::open(p_path, FileAccess::READ_WRITE);
- if (!f) {
+ Ref<FileAccess> f = FileAccess::open(p_path, FileAccess::READ_WRITE);
+ if (f.is_null()) {
return ERR_CANT_OPEN;
}
@@ -430,7 +430,6 @@ Error EditorExportPlatformWindows::fixup_embedded_pck(const String &p_path, int6
f->seek(pe_pos);
uint32_t magic = f->get_32();
if (magic != 0x00004550) {
- f->close();
return ERR_FILE_CORRUPT;
}
}
@@ -480,7 +479,5 @@ Error EditorExportPlatformWindows::fixup_embedded_pck(const String &p_path, int6
}
}
- f->close();
-
return found ? OK : ERR_FILE_CORRUPT;
}
diff --git a/platform/windows/gl_manager_windows.h b/platform/windows/gl_manager_windows.h
index 6423c54855..dc411983e8 100644
--- a/platform/windows/gl_manager_windows.h
+++ b/platform/windows/gl_manager_windows.h
@@ -52,19 +52,18 @@ public:
private:
// any data specific to the window
struct GLWindow {
- GLWindow() { in_use = false; }
- bool in_use;
+ bool in_use = false;
// the external ID .. should match the GL window number .. unused I think
- DisplayServer::WindowID window_id;
- int width;
- int height;
+ DisplayServer::WindowID window_id = DisplayServer::INVALID_WINDOW_ID;
+ int width = 0;
+ int height = 0;
// windows specific
HDC hDC;
HWND hwnd;
- int gldisplay_id;
+ int gldisplay_id = 0;
};
struct GLDisplay {
@@ -75,7 +74,7 @@ private:
LocalVector<GLWindow> _windows;
LocalVector<GLDisplay> _displays;
- GLWindow *_current_window;
+ GLWindow *_current_window = nullptr;
PFNWGLSWAPINTERVALEXTPROC wglSwapIntervalEXT;
PFNWGLGETSWAPINTERVALEXTPROC wglGetSwapIntervalEXT;
diff --git a/platform/windows/joypad_windows.cpp b/platform/windows/joypad_windows.cpp
index 494e0b9105..d039fd13a7 100644
--- a/platform/windows/joypad_windows.cpp
+++ b/platform/windows/joypad_windows.cpp
@@ -250,7 +250,7 @@ void JoypadWindows::setup_joypad_object(const DIDEVICEOBJECTINSTANCE *ob, int p_
}
BOOL CALLBACK JoypadWindows::enumCallback(const DIDEVICEINSTANCE *p_instance, void *p_context) {
- JoypadWindows *self = (JoypadWindows *)p_context;
+ JoypadWindows *self = static_cast<JoypadWindows *>(p_context);
if (self->is_xinput_device(&p_instance->guidProduct)) {
return DIENUM_CONTINUE;
}
@@ -258,9 +258,9 @@ BOOL CALLBACK JoypadWindows::enumCallback(const DIDEVICEINSTANCE *p_instance, vo
return DIENUM_CONTINUE;
}
-BOOL CALLBACK JoypadWindows::objectsCallback(const DIDEVICEOBJECTINSTANCE *instance, void *context) {
- JoypadWindows *self = (JoypadWindows *)context;
- self->setup_joypad_object(instance, self->id_to_change);
+BOOL CALLBACK JoypadWindows::objectsCallback(const DIDEVICEOBJECTINSTANCE *p_instance, void *p_context) {
+ JoypadWindows *self = static_cast<JoypadWindows *>(p_context);
+ self->setup_joypad_object(p_instance, self->id_to_change);
return DIENUM_CONTINUE;
}
@@ -404,7 +404,7 @@ void JoypadWindows::process_joypads() {
// on mingw, these constants are not constants
int count = 8;
- LONG axes[] = { DIJOFS_X, DIJOFS_Y, DIJOFS_Z, DIJOFS_RX, DIJOFS_RY, DIJOFS_RZ, (LONG)DIJOFS_SLIDER(0), (LONG)DIJOFS_SLIDER(1) };
+ const LONG axes[] = { DIJOFS_X, DIJOFS_Y, DIJOFS_Z, DIJOFS_RX, DIJOFS_RY, DIJOFS_RZ, (LONG)DIJOFS_SLIDER(0), (LONG)DIJOFS_SLIDER(1) };
int values[] = { js.lX, js.lY, js.lZ, js.lRx, js.lRy, js.lRz, js.rglSlider[0], js.rglSlider[1] };
for (int j = 0; j < joy->joy_axis.size(); j++) {
diff --git a/platform/windows/joypad_windows.h b/platform/windows/joypad_windows.h
index 4f15bcf080..d239471a5c 100644
--- a/platform/windows/joypad_windows.h
+++ b/platform/windows/joypad_windows.h
@@ -105,10 +105,10 @@ private:
typedef DWORD(WINAPI *XInputGetState_t)(DWORD dwUserIndex, XINPUT_STATE *pState);
typedef DWORD(WINAPI *XInputSetState_t)(DWORD dwUserIndex, XINPUT_VIBRATION *pVibration);
- HWND *hWnd;
+ HWND *hWnd = nullptr;
HANDLE xinput_dll;
LPDIRECTINPUT8 dinput;
- Input *input;
+ Input *input = nullptr;
int id_to_change;
int slider_count;
diff --git a/platform/windows/os_windows.h b/platform/windows/os_windows.h
index 5bfd24327e..adeecf37c5 100644
--- a/platform/windows/os_windows.h
+++ b/platform/windows/os_windows.h
@@ -60,14 +60,14 @@
class JoypadWindows;
class OS_Windows : public OS {
#ifdef STDOUT_FILE
- FILE *stdo;
+ FILE *stdo = nullptr;
#endif
uint64_t ticks_start;
uint64_t ticks_per_second;
HINSTANCE hInstance;
- MainLoop *main_loop;
+ MainLoop *main_loop = nullptr;
#ifdef WASAPI_ENABLED
AudioDriverWASAPI driver_wasapi;