summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.editorconfig4
-rw-r--r--.gitignore22
-rw-r--r--SConstruct2
-rw-r--r--core/bind/core_bind.cpp30
-rw-r--r--core/bind/core_bind.h12
-rw-r--r--core/command_queue_mt.cpp7
-rw-r--r--core/command_queue_mt.h10
-rw-r--r--core/error_macros.h20
-rw-r--r--core/io/config_file.cpp18
-rw-r--r--core/io/config_file.h2
-rw-r--r--core/io/file_access_network.cpp37
-rw-r--r--core/io/file_access_network.h6
-rw-r--r--core/io/file_access_pack.cpp2
-rw-r--r--core/io/file_access_pack.h2
-rw-r--r--core/io/ip.cpp23
-rw-r--r--core/io/resource_format_binary.cpp2
-rw-r--r--core/io/resource_loader.cpp1
-rw-r--r--core/io/resource_loader.h2
-rw-r--r--core/math/rect2.h42
-rw-r--r--core/message_queue.cpp5
-rw-r--r--core/os/dir_access.cpp5
-rw-r--r--core/os/dir_access.h3
-rw-r--r--core/os/mutex.h2
-rw-r--r--core/os/semaphore.cpp14
-rw-r--r--core/os/semaphore.h31
-rw-r--r--core/os/thread_dummy.cpp8
-rw-r--r--core/os/thread_dummy.h12
-rw-r--r--core/project_settings.cpp3
-rw-r--r--core/register_core_types.cpp2
-rw-r--r--core/rid_owner.h4
-rw-r--r--core/variant_call.cpp4
-rw-r--r--core/variant_parser.cpp10
-rw-r--r--core/variant_parser.h2
-rw-r--r--doc/classes/ARVRPositionalTracker.xml7
-rw-r--r--doc/classes/ConcavePolygonShape.xml1
-rw-r--r--doc/classes/ConfigFile.xml25
-rw-r--r--doc/classes/Directory.xml3
-rw-r--r--doc/classes/EditorPlugin.xml14
-rw-r--r--doc/classes/Node.xml4
-rw-r--r--doc/classes/ProjectSettings.xml3
-rw-r--r--doc/classes/Rect2.xml5
-rw-r--r--doc/classes/Semaphore.xml13
-rw-r--r--doc/classes/SpringArm.xml6
-rw-r--r--doc/classes/Tween.xml36
-rw-r--r--drivers/unix/dir_access_unix.cpp7
-rw-r--r--drivers/unix/dir_access_unix.h3
-rw-r--r--drivers/unix/os_unix.cpp7
-rw-r--r--drivers/vulkan/rendering_device_vulkan.cpp7
-rw-r--r--drivers/vulkan/vulkan_context.cpp81
-rw-r--r--drivers/windows/dir_access_windows.cpp8
-rw-r--r--drivers/windows/dir_access_windows.h2
-rw-r--r--editor/debugger/editor_debugger_inspector.cpp1
-rw-r--r--editor/debugger/script_editor_debugger.cpp4
-rw-r--r--editor/editor_file_dialog.cpp25
-rw-r--r--editor/editor_file_dialog.h2
-rw-r--r--editor/editor_folding.cpp2
-rw-r--r--editor/editor_inspector.cpp9
-rw-r--r--editor/editor_inspector.h2
-rw-r--r--editor/editor_node.cpp2
-rw-r--r--editor/editor_plugin.cpp2
-rw-r--r--editor/editor_properties_array_dict.cpp20
-rw-r--r--editor/editor_properties_array_dict.h8
-rw-r--r--editor/editor_resource_preview.cpp10
-rw-r--r--editor/editor_resource_preview.h2
-rw-r--r--editor/editor_settings.h2
-rw-r--r--editor/editor_visual_profiler.cpp2
-rw-r--r--editor/export_template_manager.cpp24
-rw-r--r--editor/filesystem_dock.cpp8
-rw-r--r--editor/filesystem_dock.h2
-rw-r--r--editor/groups_editor.cpp1
-rw-r--r--editor/inspector_dock.h2
-rw-r--r--editor/plugins/asset_library_editor_plugin.cpp2
-rw-r--r--editor/plugins/gi_probe_editor_plugin.cpp23
-rw-r--r--editor/plugins/sprite_frames_editor_plugin.cpp2
-rw-r--r--editor/plugins/visual_shader_editor_plugin.cpp10
-rw-r--r--editor/project_settings_editor.h2
-rw-r--r--editor/scene_tree_dock.cpp4
-rw-r--r--editor/spatial_editor_gizmos.cpp5
-rw-r--r--main/tests/test_string.cpp2
-rw-r--r--methods.py7
-rw-r--r--modules/mono/build_scripts/mono_configure.py2
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/Ides/Rider/RiderPathLocator.cs31
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/GD.cs2
-rw-r--r--modules/stb_vorbis/audio_stream_ogg_vorbis.cpp3
-rw-r--r--platform/android/SCsub4
-rw-r--r--platform/android/android_keys_utils.cpp (renamed from drivers/windows/semaphore_windows.h)35
-rw-r--r--platform/android/android_keys_utils.h280
-rw-r--r--platform/android/audio_driver_opensl.cpp4
-rw-r--r--platform/android/detect.py1
-rw-r--r--platform/android/dir_access_jandroid.cpp2
-rw-r--r--platform/android/dir_access_jandroid.h2
-rw-r--r--platform/android/export/export.cpp268
-rw-r--r--platform/android/java/app/AndroidManifest.xml25
-rw-r--r--platform/android/java/app/build.gradle68
-rw-r--r--platform/android/java/app/config.gradle72
-rw-r--r--platform/android/java/build.gradle97
-rw-r--r--platform/android/java/gradle/wrapper/gradle-wrapper.properties2
-rw-r--r--platform/android/java/lib/build.gradle2
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/Godot.java175
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/GodotLib.java16
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/GodotRenderer.java20
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/payments/GodotPaymentInterface.java (renamed from platform/iphone/semaphore_iphone.cpp)101
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/payments/PaymentsManager.java67
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/payments/ValidateTask.java9
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/plugin/GodotPlugin.java256
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/plugin/GodotPluginRegistry.java196
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/vulkan/VkRenderer.kt (renamed from drivers/windows/semaphore_windows.cpp)123
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/vulkan/VkSurfaceView.kt136
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/vulkan/VkThread.kt230
-rw-r--r--platform/android/java/plugins/godotpayment/build.gradle31
-rw-r--r--platform/android/java/plugins/godotpayment/src/main/AndroidManifest.xml11
-rw-r--r--platform/android/java/plugins/godotpayment/src/main/java/org/godotengine/godot/plugin/payment/GodotPayment.java (renamed from platform/android/java/lib/src/org/godotengine/godot/GodotPaymentV3.java)81
-rw-r--r--platform/android/java/settings.gradle1
-rw-r--r--platform/android/java_godot_lib_jni.cpp1023
-rw-r--r--platform/android/java_godot_lib_jni.h58
-rw-r--r--platform/android/java_godot_wrapper.cpp10
-rw-r--r--platform/android/java_godot_wrapper.h2
-rw-r--r--platform/android/jni_utils.cpp434
-rw-r--r--platform/android/jni_utils.h242
-rw-r--r--platform/android/os_android.cpp36
-rw-r--r--platform/android/plugin/godot_plugin_jni.cpp115
-rw-r--r--platform/android/plugin/godot_plugin_jni.h (renamed from platform/osx/semaphore_osx.h)38
-rw-r--r--platform/android/vulkan/vk_renderer_jni.cpp (renamed from drivers/unix/semaphore_posix.cpp)59
-rw-r--r--platform/android/vulkan/vk_renderer_jni.h (renamed from drivers/unix/semaphore_posix.h)40
-rw-r--r--platform/iphone/os_iphone.cpp1
-rw-r--r--platform/iphone/semaphore_iphone.h59
-rw-r--r--platform/javascript/os_javascript.cpp8
-rw-r--r--platform/osx/SCsub1
-rw-r--r--platform/osx/detect.py6
-rw-r--r--platform/osx/joypad_osx.cpp20
-rw-r--r--platform/osx/joypad_osx.h3
-rw-r--r--platform/osx/os_osx.mm3
-rw-r--r--platform/osx/semaphore_osx.cpp107
-rw-r--r--platform/server/SCsub1
-rw-r--r--platform/server/os_server.cpp4
-rw-r--r--platform/uwp/os_uwp.cpp1
-rw-r--r--platform/windows/os_windows.cpp4
-rw-r--r--platform/x11/detect.py2
-rw-r--r--scene/2d/camera_2d.cpp4
-rw-r--r--scene/3d/collision_shape.cpp8
-rw-r--r--scene/3d/physics_body.cpp1
-rw-r--r--scene/animation/tween.cpp202
-rw-r--r--scene/animation/tween.h40
-rw-r--r--scene/gui/file_dialog.cpp19
-rw-r--r--scene/gui/file_dialog.h2
-rw-r--r--scene/resources/resource_format_text.cpp12
-rw-r--r--servers/arvr/arvr_positional_tracker.cpp1
-rw-r--r--servers/physics_2d/physics_2d_server_wrap_mt.cpp15
-rw-r--r--servers/physics_2d/physics_2d_server_wrap_mt.h2
-rw-r--r--servers/visual/rasterizer.h1
-rw-r--r--servers/visual/rasterizer_rd/rasterizer_canvas_rd.cpp2
-rw-r--r--servers/visual/rasterizer_rd/rasterizer_effects_rd.cpp28
-rw-r--r--servers/visual/rasterizer_rd/rasterizer_effects_rd.h6
-rw-r--r--servers/visual/rasterizer_rd/rasterizer_scene_rd.cpp60
-rw-r--r--servers/visual/rasterizer_rd/rasterizer_scene_rd.h3
-rw-r--r--servers/visual/rasterizer_rd/rasterizer_storage_rd.cpp6
-rw-r--r--servers/visual/rasterizer_rd/shaders/cubemap_roughness.glsl73
-rw-r--r--servers/visual/visual_server_canvas.cpp2
-rw-r--r--thirdparty/README.md5
-rw-r--r--thirdparty/mbedtls/include/mbedtls/config.h4
-rw-r--r--thirdparty/mbedtls/include/mbedtls/version.h8
-rw-r--r--thirdparty/mbedtls/library/bignum.c5
-rw-r--r--thirdparty/mbedtls/library/cipher.c14
-rw-r--r--thirdparty/mbedtls/library/ecdsa.c4
-rw-r--r--thirdparty/mbedtls/library/entropy_poll.c29
-rw-r--r--thirdparty/mbedtls/library/pkparse.c120
-rw-r--r--thirdparty/mbedtls/library/rsa.c11
-rw-r--r--thirdparty/mbedtls/library/x509_crt.c32
-rw-r--r--thirdparty/mbedtls/library/x509write_csr.c4
-rw-r--r--thirdparty/mbedtls/patches/padlock.diff13
170 files changed, 3371 insertions, 2763 deletions
diff --git a/.editorconfig b/.editorconfig
index 56cc2e9c2d..f335026e1e 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -9,6 +9,10 @@ insert_final_newline = true
[*.{cpp,hpp,c,h,mm}]
trim_trailing_whitespace = true
+[{*.gradle,AndroidManifest.xml}]
+indent_style = space
+indent_size = 4
+
[{*.{py,cs},SConstruct,SCsub}]
indent_style = space
indent_size = 4
diff --git a/.gitignore b/.gitignore
index 25a36c8e5f..cc5c822e69 100644
--- a/.gitignore
+++ b/.gitignore
@@ -19,7 +19,7 @@ local.properties
.gradletasknamecache
project.properties
platform/android/java/libs/*
-platform/android/java/assets
+platform/android/java/app/libs/*
# General c++ generated files
*.lib
@@ -346,3 +346,23 @@ compile_commands.json
# Cppcheck
*.cppcheck
+cppcheck-cppcheck-build-dir/
+
+# Gcov and Lcov code coverage
+*.gcno
+*.gcda
+*.gcov.html
+*.func.html
+*.func-sort-c.html
+*index-sort-f.html
+*index-sort-l.html
+*index.html
+MachineIndependent/
+godot.info
+amber.png
+emerald.png
+glass.png
+ruby.png
+snow.png
+updown.png
+gcov.css
diff --git a/SConstruct b/SConstruct
index f2c20ea91e..436dd48800 100644
--- a/SConstruct
+++ b/SConstruct
@@ -330,7 +330,7 @@ if selected_platform in platform_list:
else:
# MSVC doesn't have clear C standard support, /std only covers C++.
# We apply it to CCFLAGS (both C and C++ code) in case it impacts C features.
- env.Prepend(CCFLAGS=['/std:c++17', '/permissive-'])
+ env.Prepend(CCFLAGS=['/std:c++17'])
# Enforce our minimal compiler version requirements
cc_version = methods.get_compiler_version(env) or [-1, -1]
diff --git a/core/bind/core_bind.cpp b/core/bind/core_bind.cpp
index b798d732d6..bfe07d61c5 100644
--- a/core/bind/core_bind.cpp
+++ b/core/bind/core_bind.cpp
@@ -2344,10 +2344,10 @@ Error _Directory::change_dir(String p_dir) {
ERR_FAIL_COND_V_MSG(!d, ERR_UNCONFIGURED, "Directory must be opened before use.");
return d->change_dir(p_dir);
}
-String _Directory::get_current_dir() {
+String _Directory::get_current_dir(bool p_include_drive) {
ERR_FAIL_COND_V_MSG(!d, "", "Directory must be opened before use.");
- return d->get_current_dir();
+ return d->get_current_dir(p_include_drive);
}
Error _Directory::make_dir(String p_dir) {
@@ -2444,7 +2444,7 @@ void _Directory::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_drive", "idx"), &_Directory::get_drive);
ClassDB::bind_method(D_METHOD("get_current_drive"), &_Directory::get_current_drive);
ClassDB::bind_method(D_METHOD("change_dir", "todir"), &_Directory::change_dir);
- ClassDB::bind_method(D_METHOD("get_current_dir"), &_Directory::get_current_dir);
+ ClassDB::bind_method(D_METHOD("get_current_dir", "include_drive"), &_Directory::get_current_dir, DEFVAL(true));
ClassDB::bind_method(D_METHOD("make_dir", "path"), &_Directory::make_dir);
ClassDB::bind_method(D_METHOD("make_dir_recursive", "path"), &_Directory::make_dir_recursive);
ClassDB::bind_method(D_METHOD("file_exists", "path"), &_Directory::file_exists);
@@ -2576,30 +2576,26 @@ void _Marshalls::_bind_methods() {
////////////////
-Error _Semaphore::wait() {
+void _Semaphore::wait() {
- return semaphore->wait();
+ semaphore.wait();
}
-Error _Semaphore::post() {
+Error _Semaphore::try_wait() {
- return semaphore->post();
+ return semaphore.try_wait() ? OK : ERR_BUSY;
}
-void _Semaphore::_bind_methods() {
+void _Semaphore::post() {
- ClassDB::bind_method(D_METHOD("wait"), &_Semaphore::wait);
- ClassDB::bind_method(D_METHOD("post"), &_Semaphore::post);
+ semaphore.post();
}
-_Semaphore::_Semaphore() {
-
- semaphore = SemaphoreOld::create();
-}
-
-_Semaphore::~_Semaphore() {
+void _Semaphore::_bind_methods() {
- memdelete(semaphore);
+ ClassDB::bind_method(D_METHOD("wait"), &_Semaphore::wait);
+ ClassDB::bind_method(D_METHOD("try_wait"), &_Semaphore::try_wait);
+ ClassDB::bind_method(D_METHOD("post"), &_Semaphore::post);
}
///////////////
diff --git a/core/bind/core_bind.h b/core/bind/core_bind.h
index ae7c3d02fd..fc6419b7d8 100644
--- a/core/bind/core_bind.h
+++ b/core/bind/core_bind.h
@@ -572,7 +572,7 @@ public:
int get_current_drive();
Error change_dir(String p_dir); // Can be relative or absolute, return false on success.
- String get_current_dir(); // Return current dir location.
+ String get_current_dir(bool p_include_drive = true); // Return current dir location.
Error make_dir(String p_dir);
Error make_dir_recursive(String p_dir);
@@ -635,16 +635,14 @@ public:
class _Semaphore : public Reference {
GDCLASS(_Semaphore, Reference);
- SemaphoreOld *semaphore;
+ Semaphore semaphore;
static void _bind_methods();
public:
- Error wait();
- Error post();
-
- _Semaphore();
- ~_Semaphore();
+ void wait();
+ Error try_wait();
+ void post();
};
class _Thread : public Reference {
diff --git a/core/command_queue_mt.cpp b/core/command_queue_mt.cpp
index b88f773ed8..85e8a847a0 100644
--- a/core/command_queue_mt.cpp
+++ b/core/command_queue_mt.cpp
@@ -108,11 +108,10 @@ CommandQueueMT::CommandQueueMT(bool p_sync) {
for (int i = 0; i < SYNC_SEMAPHORES; i++) {
- sync_sems[i].sem = SemaphoreOld::create();
sync_sems[i].in_use = false;
}
if (p_sync)
- sync = SemaphoreOld::create();
+ sync = memnew(Semaphore);
else
sync = NULL;
}
@@ -121,9 +120,5 @@ CommandQueueMT::~CommandQueueMT() {
if (sync)
memdelete(sync);
- for (int i = 0; i < SYNC_SEMAPHORES; i++) {
-
- memdelete(sync_sems[i].sem);
- }
memfree(command_mem);
}
diff --git a/core/command_queue_mt.h b/core/command_queue_mt.h
index 7407557e7e..90231546ef 100644
--- a/core/command_queue_mt.h
+++ b/core/command_queue_mt.h
@@ -270,7 +270,7 @@
cmd->sync_sem = ss; \
unlock(); \
if (sync) sync->post(); \
- ss->sem->wait(); \
+ ss->sem.wait(); \
ss->in_use = false; \
}
@@ -287,7 +287,7 @@
cmd->sync_sem = ss; \
unlock(); \
if (sync) sync->post(); \
- ss->sem->wait(); \
+ ss->sem.wait(); \
ss->in_use = false; \
}
@@ -297,7 +297,7 @@ class CommandQueueMT {
struct SyncSemaphore {
- SemaphoreOld *sem;
+ Semaphore sem;
bool in_use;
};
@@ -313,7 +313,7 @@ class CommandQueueMT {
SyncSemaphore *sync_sem;
virtual void post() {
- sync_sem->sem->post();
+ sync_sem->sem.post();
}
};
@@ -342,7 +342,7 @@ class CommandQueueMT {
uint32_t dealloc_ptr;
SyncSemaphore sync_sems[SYNC_SEMAPHORES];
Mutex mutex;
- SemaphoreOld *sync;
+ Semaphore *sync;
template <class T>
T *allocate() {
diff --git a/core/error_macros.h b/core/error_macros.h
index 4a3ea28957..e4d7609e04 100644
--- a/core/error_macros.h
+++ b/core/error_macros.h
@@ -502,11 +502,11 @@ void _err_print_index_error(const char *p_function, const char *p_file, int p_li
*
* The current function returns `m_retval`.
*/
-#define ERR_FAIL_V(m_retval) \
- if (1) { \
- _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Method/Function Failed, returning: " __STR(m_value)); \
- return m_retval; \
- } else \
+#define ERR_FAIL_V(m_retval) \
+ if (1) { \
+ _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Method/Function Failed, returning: " __STR(m_retval)); \
+ return m_retval; \
+ } else \
((void)0)
/**
@@ -515,11 +515,11 @@ void _err_print_index_error(const char *p_function, const char *p_file, int p_li
*
* Prints `m_msg`, and the current function returns `m_retval`.
*/
-#define ERR_FAIL_V_MSG(m_retval, m_msg) \
- if (1) { \
- _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Method/Function Failed, returning: " __STR(m_value), DEBUG_STR(m_msg)); \
- return m_retval; \
- } else \
+#define ERR_FAIL_V_MSG(m_retval, m_msg) \
+ if (1) { \
+ _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Method/Function Failed, returning: " __STR(m_retval), DEBUG_STR(m_msg)); \
+ return m_retval; \
+ } else \
((void)0)
/**
diff --git a/core/io/config_file.cpp b/core/io/config_file.cpp
index b44ac16a87..531467ecd6 100644
--- a/core/io/config_file.cpp
+++ b/core/io/config_file.cpp
@@ -86,7 +86,8 @@ void ConfigFile::set_value(const String &p_section, const String &p_key, const V
Variant ConfigFile::get_value(const String &p_section, const String &p_key, Variant p_default) const {
if (!values.has(p_section) || !values[p_section].has(p_key)) {
- ERR_FAIL_COND_V_MSG(p_default.get_type() == Variant::NIL, Variant(), "Couldn't find the given section '" + p_section + "', key '" + p_key + "' and no default was given.");
+ ERR_FAIL_COND_V_MSG(p_default.get_type() == Variant::NIL, Variant(),
+ vformat("Couldn't find the given section \"%s\" and key \"%s\", and no default was given.", p_section, p_key));
return p_default;
}
@@ -112,7 +113,7 @@ void ConfigFile::get_sections(List<String> *r_sections) const {
}
void ConfigFile::get_section_keys(const String &p_section, List<String> *r_keys) const {
- ERR_FAIL_COND_MSG(!values.has(p_section), "Cannont get keys from nonexistent section '" + p_section + "'.");
+ ERR_FAIL_COND_MSG(!values.has(p_section), vformat("Cannot get keys from nonexistent section \"%s\".", p_section));
for (OrderedHashMap<String, Variant>::ConstElement E = values[p_section].front(); E; E = E.next()) {
r_keys->push_back(E.key());
@@ -121,12 +122,14 @@ void ConfigFile::get_section_keys(const String &p_section, List<String> *r_keys)
void ConfigFile::erase_section(const String &p_section) {
+ ERR_FAIL_COND_MSG(!values.has(p_section), vformat("Cannot erase nonexistent section \"%s\".", p_section));
values.erase(p_section);
}
void ConfigFile::erase_section_key(const String &p_section, const String &p_key) {
- ERR_FAIL_COND_MSG(!values.has(p_section), "Cannot erase key from nonexistent section '" + p_section + "'.");
+ ERR_FAIL_COND_MSG(!values.has(p_section), vformat("Cannot erase key \"%s\" from nonexistent section \"%s\".", p_key, p_section));
+ ERR_FAIL_COND_MSG(!values[p_section].has(p_key), vformat("Cannot erase nonexistent key \"%s\" from section \"%s\".", p_key, p_section));
values[p_section].erase(p_key);
}
@@ -291,7 +294,7 @@ Error ConfigFile::_parse(const String &p_path, VariantParser::Stream *p_stream)
if (err == ERR_FILE_EOF) {
return OK;
} else if (err != OK) {
- ERR_PRINT("ConfgFile - " + p_path + ":" + itos(lines) + " error: " + error_text + ".");
+ ERR_PRINT(vformat("ConfigFile parse error at %s:%d: %s.", p_path, lines, error_text));
return err;
}
@@ -324,11 +327,8 @@ void ConfigFile::_bind_methods() {
ClassDB::bind_method(D_METHOD("save", "path"), &ConfigFile::save);
ClassDB::bind_method(D_METHOD("load_encrypted", "path", "key"), &ConfigFile::load_encrypted);
- ClassDB::bind_method(D_METHOD("load_encrypted_pass", "path", "pass"), &ConfigFile::load_encrypted_pass);
+ ClassDB::bind_method(D_METHOD("load_encrypted_pass", "path", "password"), &ConfigFile::load_encrypted_pass);
ClassDB::bind_method(D_METHOD("save_encrypted", "path", "key"), &ConfigFile::save_encrypted);
- ClassDB::bind_method(D_METHOD("save_encrypted_pass", "path", "pass"), &ConfigFile::save_encrypted_pass);
-}
-
-ConfigFile::ConfigFile() {
+ ClassDB::bind_method(D_METHOD("save_encrypted_pass", "path", "password"), &ConfigFile::save_encrypted_pass);
}
diff --git a/core/io/config_file.h b/core/io/config_file.h
index 150fd24693..7efcb5a04c 100644
--- a/core/io/config_file.h
+++ b/core/io/config_file.h
@@ -74,8 +74,6 @@ public:
Error save_encrypted(const String &p_path, const Vector<uint8_t> &p_key);
Error save_encrypted_pass(const String &p_path, const String &p_pass);
-
- ConfigFile();
};
#endif // CONFIG_FILE_H
diff --git a/core/io/file_access_network.cpp b/core/io/file_access_network.cpp
index 7f1eb6fd90..370dd8f982 100644
--- a/core/io/file_access_network.cpp
+++ b/core/io/file_access_network.cpp
@@ -88,9 +88,7 @@ void FileAccessNetworkClient::_thread_func() {
while (!quit) {
DEBUG_PRINT("SEM WAIT - " + itos(sem->get()));
- Error err = sem->wait();
- if (err != OK)
- ERR_PRINT("sem->wait() failed");
+ sem.wait();
DEBUG_TIME("sem_unlock");
//DEBUG_PRINT("semwait returned "+itos(werr));
DEBUG_PRINT("MUTEX LOCK " + itos(lockcount));
@@ -141,7 +139,7 @@ void FileAccessNetworkClient::_thread_func() {
fa->_respond(len, Error(status));
}
- fa->sem->post();
+ fa->sem.post();
} break;
case FileAccessNetwork::RESPONSE_DATA: {
@@ -161,14 +159,14 @@ void FileAccessNetworkClient::_thread_func() {
int status = get_32();
fa->exists_modtime = status != 0;
- fa->sem->post();
+ fa->sem.post();
} break;
case FileAccessNetwork::RESPONSE_GET_MODTIME: {
uint64_t status = get_64();
fa->exists_modtime = status;
- fa->sem->post();
+ fa->sem.post();
} break;
}
@@ -230,7 +228,6 @@ FileAccessNetworkClient::FileAccessNetworkClient() {
singleton = this;
last_id = 0;
client.instance();
- sem = SemaphoreOld::create();
lockcount = 0;
}
@@ -238,12 +235,10 @@ FileAccessNetworkClient::~FileAccessNetworkClient() {
if (thread) {
quit = true;
- sem->post();
+ sem.post();
Thread::wait_to_finish(thread);
memdelete(thread);
}
-
- memdelete(sem);
}
void FileAccessNetwork::_set_block(int p_offset, const Vector<uint8_t> &p_block) {
@@ -264,7 +259,7 @@ void FileAccessNetwork::_set_block(int p_offset, const Vector<uint8_t> &p_block)
if (waiting_on_page == page) {
waiting_on_page = -1;
- page_sem->post();
+ page_sem.post();
}
}
@@ -306,9 +301,9 @@ Error FileAccessNetwork::_open(const String &p_path, int p_mode_flags) {
nc->unlock_mutex();
DEBUG_PRINT("OPEN POST");
DEBUG_TIME("open_post");
- nc->sem->post(); //awaiting answer
+ nc->sem.post(); //awaiting answer
DEBUG_PRINT("WAIT...");
- sem->wait();
+ sem.wait();
DEBUG_TIME("open_end");
DEBUG_PRINT("WAIT ENDED...");
@@ -393,7 +388,7 @@ void FileAccessNetwork::_queue_page(int p_page) const {
pages.write[p_page].queued = true;
}
DEBUG_PRINT("QUEUE PAGE POST");
- nc->sem->post();
+ nc->sem.post();
DEBUG_PRINT("queued " + itos(p_page));
}
}
@@ -426,7 +421,7 @@ int FileAccessNetwork::get_buffer(uint8_t *p_dst, int p_length) const {
}
buffer_mutex.unlock();
DEBUG_PRINT("wait");
- page_sem->wait();
+ page_sem.wait();
DEBUG_PRINT("done");
} else {
@@ -475,8 +470,8 @@ bool FileAccessNetwork::file_exists(const String &p_path) {
nc->client->put_data((const uint8_t *)cs.ptr(), cs.length());
nc->unlock_mutex();
DEBUG_PRINT("FILE EXISTS POST");
- nc->sem->post();
- sem->wait();
+ nc->sem.post();
+ sem.wait();
return exists_modtime != 0;
}
@@ -492,8 +487,8 @@ uint64_t FileAccessNetwork::_get_modified_time(const String &p_file) {
nc->client->put_data((const uint8_t *)cs.ptr(), cs.length());
nc->unlock_mutex();
DEBUG_PRINT("MODTIME POST");
- nc->sem->post();
- sem->wait();
+ nc->sem.post();
+ sem.wait();
return exists_modtime;
}
@@ -521,8 +516,6 @@ FileAccessNetwork::FileAccessNetwork() {
eof_flag = false;
opened = false;
pos = 0;
- sem = SemaphoreOld::create();
- page_sem = SemaphoreOld::create();
FileAccessNetworkClient *nc = FileAccessNetworkClient::singleton;
nc->lock_mutex();
id = nc->last_id++;
@@ -538,8 +531,6 @@ FileAccessNetwork::FileAccessNetwork() {
FileAccessNetwork::~FileAccessNetwork() {
close();
- memdelete(sem);
- memdelete(page_sem);
FileAccessNetworkClient *nc = FileAccessNetworkClient::singleton;
nc->lock_mutex();
diff --git a/core/io/file_access_network.h b/core/io/file_access_network.h
index 38d9b8e8a6..7f664b46f7 100644
--- a/core/io/file_access_network.h
+++ b/core/io/file_access_network.h
@@ -49,7 +49,7 @@ class FileAccessNetworkClient {
List<BlockRequest> block_requests;
- SemaphoreOld *sem;
+ Semaphore sem;
Thread *thread;
bool quit;
Mutex mutex;
@@ -85,8 +85,8 @@ public:
class FileAccessNetwork : public FileAccess {
- SemaphoreOld *sem;
- SemaphoreOld *page_sem;
+ Semaphore sem;
+ Semaphore page_sem;
Mutex buffer_mutex;
bool opened;
size_t total_size;
diff --git a/core/io/file_access_pack.cpp b/core/io/file_access_pack.cpp
index 83ce03418a..055ce816ad 100644
--- a/core/io/file_access_pack.cpp
+++ b/core/io/file_access_pack.cpp
@@ -454,7 +454,7 @@ Error DirAccessPack::change_dir(String p_dir) {
return OK;
}
-String DirAccessPack::get_current_dir() {
+String DirAccessPack::get_current_dir(bool p_include_drive) {
PackedData::PackedDir *pd = current;
String p = current->name;
diff --git a/core/io/file_access_pack.h b/core/io/file_access_pack.h
index b6ea9c158f..e1f35aabdd 100644
--- a/core/io/file_access_pack.h
+++ b/core/io/file_access_pack.h
@@ -216,7 +216,7 @@ public:
virtual String get_drive(int p_drive);
virtual Error change_dir(String p_dir);
- virtual String get_current_dir();
+ virtual String get_current_dir(bool p_include_drive = true);
virtual bool file_exists(String p_file);
virtual bool dir_exists(String p_dir);
diff --git a/core/io/ip.cpp b/core/io/ip.cpp
index af534e4bb7..2143b84d15 100644
--- a/core/io/ip.cpp
+++ b/core/io/ip.cpp
@@ -71,7 +71,7 @@ struct _IP_ResolverPrivate {
}
Mutex mutex;
- SemaphoreOld *sem;
+ Semaphore sem;
Thread *thread;
//Semaphore* semaphore;
@@ -98,7 +98,7 @@ struct _IP_ResolverPrivate {
while (!ipr->thread_abort) {
- ipr->sem->wait();
+ ipr->sem.wait();
MutexLock lock(ipr->mutex);
ipr->resolve_queues();
@@ -148,7 +148,7 @@ IP::ResolverID IP::resolve_hostname_queue_item(const String &p_hostname, IP::Typ
resolver->queue[id].response = IP_Address();
resolver->queue[id].status = IP::RESOLVER_STATUS_WAITING;
if (resolver->thread)
- resolver->sem->post();
+ resolver->sem.post();
else
resolver->resolve_queues();
}
@@ -300,23 +300,13 @@ IP::IP() {
singleton = this;
resolver = memnew(_IP_ResolverPrivate);
- resolver->sem = NULL;
#ifndef NO_THREADS
- resolver->sem = SemaphoreOld::create();
- if (resolver->sem) {
- resolver->thread_abort = false;
+ resolver->thread_abort = false;
- resolver->thread = Thread::create(_IP_ResolverPrivate::_thread_function, resolver);
-
- if (!resolver->thread)
- memdelete(resolver->sem); //wtf
- } else {
- resolver->thread = NULL;
- }
+ resolver->thread = Thread::create(_IP_ResolverPrivate::_thread_function, resolver);
#else
- resolver->sem = NULL;
resolver->thread = NULL;
#endif
}
@@ -326,10 +316,9 @@ IP::~IP() {
#ifndef NO_THREADS
if (resolver->thread) {
resolver->thread_abort = true;
- resolver->sem->post();
+ resolver->sem.post();
Thread::wait_to_finish(resolver->thread);
memdelete(resolver->thread);
- memdelete(resolver->sem);
}
#endif
diff --git a/core/io/resource_format_binary.cpp b/core/io/resource_format_binary.cpp
index 54b75cc29d..efd452191a 100644
--- a/core/io/resource_format_binary.cpp
+++ b/core/io/resource_format_binary.cpp
@@ -1008,7 +1008,9 @@ String ResourceLoaderBinary::recognize(FileAccess *p_f) {
ResourceLoaderBinary::ResourceLoaderBinary() :
translation_remapped(false),
+ ver_format(0),
f(NULL),
+ importmd_ofs(0),
error(OK) {
progress = nullptr;
diff --git a/core/io/resource_loader.cpp b/core/io/resource_loader.cpp
index 504dbe2d63..5dca8b3b89 100644
--- a/core/io/resource_loader.cpp
+++ b/core/io/resource_loader.cpp
@@ -538,6 +538,7 @@ RES ResourceLoader::load(const String &p_path, const String &p_type_hint, bool p
if (r_error) {
*r_error = err;
}
+ thread_load_mutex->unlock();
return RES();
}
thread_load_mutex->unlock();
diff --git a/core/io/resource_loader.h b/core/io/resource_loader.h
index 3b7a27f551..ea89917a5f 100644
--- a/core/io/resource_loader.h
+++ b/core/io/resource_loader.h
@@ -116,7 +116,7 @@ private:
String type_hint;
float progress = 0.0;
ThreadLoadStatus status = THREAD_LOAD_IN_PROGRESS;
- Error error;
+ Error error = OK;
RES resource;
bool xl_remapped = false;
bool use_sub_threads = false;
diff --git a/core/math/rect2.h b/core/math/rect2.h
index e4ea615c22..3b9660e2f0 100644
--- a/core/math/rect2.h
+++ b/core/math/rect2.h
@@ -47,28 +47,26 @@ struct Rect2 {
real_t get_area() const { return size.width * size.height; }
- inline bool intersects(const Rect2 &p_rect) const {
- if (position.x >= (p_rect.position.x + p_rect.size.width))
- return false;
- if ((position.x + size.width) <= p_rect.position.x)
- return false;
- if (position.y >= (p_rect.position.y + p_rect.size.height))
- return false;
- if ((position.y + size.height) <= p_rect.position.y)
- return false;
-
- return true;
- }
-
- inline bool intersects_touch(const Rect2 &p_rect) const {
- if (position.x > (p_rect.position.x + p_rect.size.width))
- return false;
- if ((position.x + size.width) < p_rect.position.x)
- return false;
- if (position.y > (p_rect.position.y + p_rect.size.height))
- return false;
- if ((position.y + size.height) < p_rect.position.y)
- return false;
+ inline bool intersects(const Rect2 &p_rect, const bool p_include_borders = false) const {
+ if (p_include_borders) {
+ if (position.x > (p_rect.position.x + p_rect.size.width))
+ return false;
+ if ((position.x + size.width) < p_rect.position.x)
+ return false;
+ if (position.y > (p_rect.position.y + p_rect.size.height))
+ return false;
+ if ((position.y + size.height) < p_rect.position.y)
+ return false;
+ } else {
+ if (position.x >= (p_rect.position.x + p_rect.size.width))
+ return false;
+ if ((position.x + size.width) <= p_rect.position.x)
+ return false;
+ if (position.y >= (p_rect.position.y + p_rect.size.height))
+ return false;
+ if ((position.y + size.height) <= p_rect.position.y)
+ return false;
+ }
return true;
}
diff --git a/core/message_queue.cpp b/core/message_queue.cpp
index 235003627e..37207483fe 100644
--- a/core/message_queue.cpp
+++ b/core/message_queue.cpp
@@ -268,7 +268,10 @@ void MessageQueue::flush() {
//using reverse locking strategy
_THREAD_SAFE_LOCK_
- ERR_FAIL_COND(flushing); //already flushing, you did something odd
+ if (flushing) {
+ _THREAD_SAFE_UNLOCK_
+ ERR_FAIL_COND(flushing); //already flushing, you did something odd
+ }
flushing = true;
while (read_pos < buffer_end) {
diff --git a/core/os/dir_access.cpp b/core/os/dir_access.cpp
index f65fc00077..642c86be2f 100644
--- a/core/os/dir_access.cpp
+++ b/core/os/dir_access.cpp
@@ -66,6 +66,11 @@ int DirAccess::get_current_drive() {
return 0;
}
+bool DirAccess::drives_are_shortcuts() {
+
+ return false;
+}
+
static Error _erase_recursive(DirAccess *da) {
List<String> dirs;
diff --git a/core/os/dir_access.h b/core/os/dir_access.h
index 55a6d53f72..aac6c67f0a 100644
--- a/core/os/dir_access.h
+++ b/core/os/dir_access.h
@@ -76,9 +76,10 @@ public:
virtual int get_drive_count() = 0;
virtual String get_drive(int p_drive) = 0;
virtual int get_current_drive();
+ virtual bool drives_are_shortcuts();
virtual Error change_dir(String p_dir) = 0; ///< can be relative or absolute, return false on success
- virtual String get_current_dir() = 0; ///< return current dir location
+ virtual String get_current_dir(bool p_include_drive = true) = 0; ///< return current dir location
virtual Error make_dir(String p_dir) = 0;
virtual Error make_dir_recursive(String p_dir);
virtual Error erase_contents_recursive(); //super dangerous, use with care!
diff --git a/core/os/mutex.h b/core/os/mutex.h
index 6cf8ee7c40..8d7b378d60 100644
--- a/core/os/mutex.h
+++ b/core/os/mutex.h
@@ -34,7 +34,7 @@
#include "core/error_list.h"
#include "core/typedefs.h"
-#if !(defined NO_THREADS)
+#if !defined(NO_THREADS)
#include <mutex>
diff --git a/core/os/semaphore.cpp b/core/os/semaphore.cpp
index 2c20f234d0..93f1e2dff4 100644
--- a/core/os/semaphore.cpp
+++ b/core/os/semaphore.cpp
@@ -29,17 +29,3 @@
/*************************************************************************/
#include "semaphore.h"
-
-#include "core/error_macros.h"
-
-SemaphoreOld *(*SemaphoreOld::create_func)() = 0;
-
-SemaphoreOld *SemaphoreOld::create() {
-
- ERR_FAIL_COND_V(!create_func, 0);
-
- return create_func();
-}
-
-SemaphoreOld::~SemaphoreOld() {
-}
diff --git a/core/os/semaphore.h b/core/os/semaphore.h
index f16a15a6db..6f194d4887 100644
--- a/core/os/semaphore.h
+++ b/core/os/semaphore.h
@@ -34,30 +34,32 @@
#include "core/error_list.h"
#include "core/typedefs.h"
+#if !defined(NO_THREADS)
+
#include <condition_variable>
#include <mutex>
class Semaphore {
private:
- std::mutex mutex_;
- std::condition_variable condition_;
- unsigned long count_ = 0; // Initialized as locked.
+ mutable std::mutex mutex_;
+ mutable std::condition_variable condition_;
+ mutable unsigned long count_ = 0; // Initialized as locked.
public:
- _ALWAYS_INLINE_ void post() {
+ _ALWAYS_INLINE_ void post() const {
std::lock_guard<decltype(mutex_)> lock(mutex_);
++count_;
condition_.notify_one();
}
- _ALWAYS_INLINE_ void wait() {
+ _ALWAYS_INLINE_ void wait() const {
std::unique_lock<decltype(mutex_)> lock(mutex_);
while (!count_) // Handle spurious wake-ups.
condition_.wait(lock);
--count_;
}
- _ALWAYS_INLINE_ bool try_wait() {
+ _ALWAYS_INLINE_ bool try_wait() const {
std::lock_guard<decltype(mutex_)> lock(mutex_);
if (count_) {
--count_;
@@ -67,18 +69,15 @@ public:
}
};
-class SemaphoreOld {
-protected:
- static SemaphoreOld *(*create_func)();
+#else
+class Semaphore {
public:
- virtual Error wait() = 0; ///< wait until semaphore has positive value, then decrement and pass
- virtual Error post() = 0; ///< unlock the semaphore, incrementing the value
- virtual int get() const = 0; ///< get semaphore value
-
- static SemaphoreOld *create(); ///< Create a mutex
-
- virtual ~SemaphoreOld();
+ _ALWAYS_INLINE_ void post() const {}
+ _ALWAYS_INLINE_ void wait() const {}
+ _ALWAYS_INLINE_ bool try_wait() const { return true; }
};
#endif
+
+#endif
diff --git a/core/os/thread_dummy.cpp b/core/os/thread_dummy.cpp
index 097c90c1fb..9dcddcae11 100644
--- a/core/os/thread_dummy.cpp
+++ b/core/os/thread_dummy.cpp
@@ -40,14 +40,6 @@ void ThreadDummy::make_default() {
Thread::create_func = &ThreadDummy::create;
};
-SemaphoreOld *SemaphoreDummy::create() {
- return memnew(SemaphoreDummy);
-};
-
-void SemaphoreDummy::make_default() {
- SemaphoreOld::create_func = &SemaphoreDummy::create;
-};
-
RWLock *RWLockDummy::create() {
return memnew(RWLockDummy);
};
diff --git a/core/os/thread_dummy.h b/core/os/thread_dummy.h
index c0976ec299..da8188f983 100644
--- a/core/os/thread_dummy.h
+++ b/core/os/thread_dummy.h
@@ -45,18 +45,6 @@ public:
static void make_default();
};
-class SemaphoreDummy : public SemaphoreOld {
-
- static SemaphoreOld *create();
-
-public:
- virtual Error wait() { return OK; };
- virtual Error post() { return OK; };
- virtual int get() const { return 0; }; ///< get semaphore value
-
- static void make_default();
-};
-
class RWLockDummy : public RWLock {
static RWLock *create();
diff --git a/core/project_settings.cpp b/core/project_settings.cpp
index cf6b0471ec..3a21610331 100644
--- a/core/project_settings.cpp
+++ b/core/project_settings.cpp
@@ -1215,9 +1215,6 @@ ProjectSettings::ProjectSettings() {
Compression::gzip_level = GLOBAL_DEF("compression/formats/gzip/compression_level", Z_DEFAULT_COMPRESSION);
custom_prop_info["compression/formats/gzip/compression_level"] = PropertyInfo(Variant::INT, "compression/formats/gzip/compression_level", PROPERTY_HINT_RANGE, "-1,9,1");
- // Would ideally be defined in an Android-specific file, but then it doesn't appear in the docs
- GLOBAL_DEF("android/modules", "");
-
using_datapack = false;
}
diff --git a/core/register_core_types.cpp b/core/register_core_types.cpp
index 07a252ad31..b0ba8ed194 100644
--- a/core/register_core_types.cpp
+++ b/core/register_core_types.cpp
@@ -100,7 +100,7 @@ extern void unregister_variant_methods();
void register_core_types() {
//consistency check
- ERR_FAIL_COND(sizeof(Callable) > 16);
+ static_assert(sizeof(Callable) <= 16);
ObjectDB::setup();
ResourceCache::setup();
diff --git a/core/rid_owner.h b/core/rid_owner.h
index bd01eba17d..5c8c48a4cb 100644
--- a/core/rid_owner.h
+++ b/core/rid_owner.h
@@ -298,7 +298,11 @@ public:
if (description) {
print_error("ERROR: " + itos(alloc_count) + " RID allocations of type '" + description + "' were leaked at exit.");
} else {
+#ifdef NO_SAFE_CAST
+ print_error("ERROR: " + itos(alloc_count) + " RID allocations of type 'unknown' were leaked at exit.");
+#else
print_error("ERROR: " + itos(alloc_count) + " RID allocations of type '" + typeid(T).name() + "' were leaked at exit.");
+#endif
}
for (size_t i = 0; i < max_alloc; i++) {
diff --git a/core/variant_call.cpp b/core/variant_call.cpp
index 9cc08b54e6..99cfc7ed3c 100644
--- a/core/variant_call.cpp
+++ b/core/variant_call.cpp
@@ -391,7 +391,7 @@ struct _VariantCall {
VCALL_LOCALMEM0R(Rect2, has_no_area);
VCALL_LOCALMEM1R(Rect2, has_point);
VCALL_LOCALMEM1R(Rect2, is_equal_approx);
- VCALL_LOCALMEM1R(Rect2, intersects);
+ VCALL_LOCALMEM2R(Rect2, intersects);
VCALL_LOCALMEM1R(Rect2, encloses);
VCALL_LOCALMEM1R(Rect2, clip);
VCALL_LOCALMEM1R(Rect2, merge);
@@ -1834,7 +1834,7 @@ void register_variant_methods() {
ADDFUNC0R(RECT2, BOOL, Rect2, has_no_area, varray());
ADDFUNC1R(RECT2, BOOL, Rect2, has_point, VECTOR2, "point", varray());
ADDFUNC1R(RECT2, BOOL, Rect2, is_equal_approx, RECT2, "rect", varray());
- ADDFUNC1R(RECT2, BOOL, Rect2, intersects, RECT2, "b", varray());
+ ADDFUNC2R(RECT2, BOOL, Rect2, intersects, RECT2, "b", BOOL, "include_borders", varray(false));
ADDFUNC1R(RECT2, BOOL, Rect2, encloses, RECT2, "b", varray());
ADDFUNC1R(RECT2, RECT2, Rect2, clip, RECT2, "b", varray());
ADDFUNC1R(RECT2, RECT2, Rect2, merge, RECT2, "b", varray());
diff --git a/core/variant_parser.cpp b/core/variant_parser.cpp
index 12fd9976bd..d2ee0b71c9 100644
--- a/core/variant_parser.cpp
+++ b/core/variant_parser.cpp
@@ -532,6 +532,10 @@ Error VariantParser::parse_value(Token &token, Variant &value, Stream *p_stream,
value = false;
else if (id == "null" || id == "nil")
value = Variant();
+ else if (id == "inf")
+ value = Math_INF;
+ else if (id == "nan")
+ value = Math_NAN;
else if (id == "Vector2") {
Vector<float> args;
@@ -1499,8 +1503,10 @@ Error VariantWriter::write(const Variant &p_variant, StoreStringFunc p_store_str
case Variant::FLOAT: {
String s = rtosfix(p_variant.operator real_t());
- if (s.find(".") == -1 && s.find("e") == -1)
- s += ".0";
+ if (s != "inf" && s != "nan") {
+ if (s.find(".") == -1 && s.find("e") == -1)
+ s += ".0";
+ }
p_store_string_func(p_store_string_ud, s);
} break;
case Variant::STRING: {
diff --git a/core/variant_parser.h b/core/variant_parser.h
index ad0a4d6682..d50842145c 100644
--- a/core/variant_parser.h
+++ b/core/variant_parser.h
@@ -77,7 +77,7 @@ public:
struct ResourceParser {
- void *userdata;
+ void *userdata = nullptr;
ParseResourceFunc func;
ParseResourceFunc ext_func;
ParseResourceFunc sub_func;
diff --git a/doc/classes/ARVRPositionalTracker.xml b/doc/classes/ARVRPositionalTracker.xml
index 9225717978..640b721d37 100644
--- a/doc/classes/ARVRPositionalTracker.xml
+++ b/doc/classes/ARVRPositionalTracker.xml
@@ -54,6 +54,13 @@
Returns the world-space controller position.
</description>
</method>
+ <method name="get_tracker_id" qualifiers="const">
+ <return type="int">
+ </return>
+ <description>
+ Returns the internal tracker ID. This uniquely identifies the tracker per tracker type and matches the ID you need to specify for nodes such as the [ARVRController] and [ARVRAnchor] nodes.
+ </description>
+ </method>
<method name="get_tracks_orientation" qualifiers="const">
<return type="bool">
</return>
diff --git a/doc/classes/ConcavePolygonShape.xml b/doc/classes/ConcavePolygonShape.xml
index 21f2f681b9..47f2276c63 100644
--- a/doc/classes/ConcavePolygonShape.xml
+++ b/doc/classes/ConcavePolygonShape.xml
@@ -5,6 +5,7 @@
</brief_description>
<description>
Concave polygon shape resource, which can be set into a [PhysicsBody] or area. This shape is created by feeding a list of triangles.
+ Note: when used for collision, [ConcavePolygonShape] is intended to work with static [PhysicsBody] nodes like [StaticBody] and will not work with [KinematicBody] or [RigidBody] with a mode other than Static.
</description>
<tutorials>
</tutorials>
diff --git a/doc/classes/ConfigFile.xml b/doc/classes/ConfigFile.xml
index 00d98130f3..a16326d55a 100644
--- a/doc/classes/ConfigFile.xml
+++ b/doc/classes/ConfigFile.xml
@@ -37,7 +37,7 @@
<argument index="0" name="section" type="String">
</argument>
<description>
- Deletes the specified section along with all the key-value pairs inside.
+ Deletes the specified section along with all the key-value pairs inside. Raises an error if the section does not exist.
</description>
</method>
<method name="erase_section_key">
@@ -48,6 +48,7 @@
<argument index="1" name="key" type="String">
</argument>
<description>
+ Deletes the specified key in a section. Raises an error if either the section or the key do not exist.
</description>
</method>
<method name="get_section_keys" qualifiers="const">
@@ -56,7 +57,7 @@
<argument index="0" name="section" type="String">
</argument>
<description>
- Returns an array of all defined key identifiers in the specified section.
+ Returns an array of all defined key identifiers in the specified section. Raises an error and returns an empty array if the section does not exist.
</description>
</method>
<method name="get_sections" qualifiers="const">
@@ -76,7 +77,7 @@
<argument index="2" name="default" type="Variant" default="null">
</argument>
<description>
- Returns the current value for the specified section and key. If the section and/or the key do not exist, the method returns the value of the optional [code]default[/code] argument, or [code]null[/code] if it is omitted.
+ Returns the current value for the specified section and key. If either the section or the key do not exist, the method returns the fallback [code]default[/code] value. If [code]default[/code] is not specified or set to [code]null[/code], an error is also raised.
</description>
</method>
<method name="has_section" qualifiers="const">
@@ -105,7 +106,7 @@
<argument index="0" name="path" type="String">
</argument>
<description>
- Loads the config file specified as a parameter. The file's contents are parsed and loaded in the ConfigFile object which the method was called on.
+ Loads the config file specified as a parameter. The file's contents are parsed and loaded in the [ConfigFile] object which the method was called on.
Returns one of the [enum Error] code constants ([code]OK[/code] on success).
</description>
</method>
@@ -117,6 +118,8 @@
<argument index="1" name="key" type="PackedByteArray">
</argument>
<description>
+ Loads the encrypted config file specified as a parameter, using the provided [code]key[/code] to decrypt it. The file's contents are parsed and loaded in the [ConfigFile] object which the method was called on.
+ Returns one of the [enum Error] code constants ([code]OK[/code] on success).
</description>
</method>
<method name="load_encrypted_pass">
@@ -124,9 +127,11 @@
</return>
<argument index="0" name="path" type="String">
</argument>
- <argument index="1" name="pass" type="String">
+ <argument index="1" name="password" type="String">
</argument>
<description>
+ Loads the encrypted config file specified as a parameter, using the provided [code]password[/code] to decrypt it. The file's contents are parsed and loaded in the [ConfigFile] object which the method was called on.
+ Returns one of the [enum Error] code constants ([code]OK[/code] on success).
</description>
</method>
<method name="parse">
@@ -145,7 +150,7 @@
<argument index="0" name="path" type="String">
</argument>
<description>
- Saves the contents of the ConfigFile object to the file specified as a parameter. The output file uses an INI-style structure.
+ Saves the contents of the [ConfigFile] object to the file specified as a parameter. The output file uses an INI-style structure.
Returns one of the [enum Error] code constants ([code]OK[/code] on success).
</description>
</method>
@@ -157,6 +162,8 @@
<argument index="1" name="key" type="PackedByteArray">
</argument>
<description>
+ Saves the contents of the [ConfigFile] object to the AES-256 encrypted file specified as a parameter, using the provided [code]key[/code] to encrypt it. The output file uses an INI-style structure.
+ Returns one of the [enum Error] code constants ([code]OK[/code] on success).
</description>
</method>
<method name="save_encrypted_pass">
@@ -164,9 +171,11 @@
</return>
<argument index="0" name="path" type="String">
</argument>
- <argument index="1" name="pass" type="String">
+ <argument index="1" name="password" type="String">
</argument>
<description>
+ Saves the contents of the [ConfigFile] object to the AES-256 encrypted file specified as a parameter, using the provided [code]password[/code] to encrypt it. The output file uses an INI-style structure.
+ Returns one of the [enum Error] code constants ([code]OK[/code] on success).
</description>
</method>
<method name="set_value">
@@ -179,7 +188,7 @@
<argument index="2" name="value" type="Variant">
</argument>
<description>
- Assigns a value to the specified key of the specified section. If the section and/or the key do not exist, they are created. Passing a [code]null[/code] value deletes the specified key if it exists, and deletes the section if it ends up empty once the key has been removed.
+ Assigns a value to the specified key of the specified section. If either the section or the key do not exist, they are created. Passing a [code]null[/code] value deletes the specified key if it exists, and deletes the section if it ends up empty once the key has been removed.
</description>
</method>
</methods>
diff --git a/doc/classes/Directory.xml b/doc/classes/Directory.xml
index ed4257a809..cb59a69876 100644
--- a/doc/classes/Directory.xml
+++ b/doc/classes/Directory.xml
@@ -77,8 +77,11 @@
<method name="get_current_dir">
<return type="String">
</return>
+ <argument index="0" name="include_drive" type="bool" default="true">
+ </argument>
<description>
Returns the absolute path to the currently opened directory (e.g. [code]res://folder[/code] or [code]C:\tmp\folder[/code]).
+ On Windows, if [code]include_drive[/code] is [code]false[/code], the leading drive specificator is omitted from the returned value (e.g. [code]\tmp\folder[/code]).
</description>
</method>
<method name="get_current_drive">
diff --git a/doc/classes/EditorPlugin.xml b/doc/classes/EditorPlugin.xml
index d17fd0a661..587fd51f1e 100644
--- a/doc/classes/EditorPlugin.xml
+++ b/doc/classes/EditorPlugin.xml
@@ -267,15 +267,27 @@
</description>
</method>
<method name="get_plugin_icon" qualifiers="virtual">
- <return type="Object">
+ <return type="Texture2D">
</return>
<description>
+ Override this method in your plugin to return a [Texture2D] in order to give it an icon.
+ For main screen plugins, this appears at the top of the screen, to the right of the "2D", "3D", "Script", and "AssetLib" buttons.
+ Ideally, the plugin icon should be white with a transparent background and 16x16 pixels in size.
+ [codeblock]
+ func get_plugin_icon():
+ # You can use a custom icon:
+ return preload("res://addons/my_plugin/my_plugin_icon.svg")
+ # Or use a built-in icon:
+ return get_editor_interface().get_base_control().get_icon("Node", "EditorIcons")
+ [/codeblock]
</description>
</method>
<method name="get_plugin_name" qualifiers="virtual">
<return type="String">
</return>
<description>
+ Override this method in your plugin to provide the name of the plugin when displayed in the Godot editor.
+ For main screen plugins, this appears at the top of the screen, to the right of the "2D", "3D", "Script", and "AssetLib" buttons.
</description>
</method>
<method name="get_script_create_dialog">
diff --git a/doc/classes/Node.xml b/doc/classes/Node.xml
index eb15bc2ad9..717130728d 100644
--- a/doc/classes/Node.xml
+++ b/doc/classes/Node.xml
@@ -328,7 +328,7 @@
<return type="float">
</return>
<description>
- Returns the time elapsed since the last physics-bound frame (see [method _physics_process]). This is always a constant value in physics processing unless the frames per second is changed via [member Engine.target_fps].
+ Returns the time elapsed since the last physics-bound frame (see [method _physics_process]). This is always a constant value in physics processing unless the frames per second is changed via [member Engine.iterations_per_second].
</description>
</method>
<method name="get_position_in_parent" qualifiers="const">
@@ -747,7 +747,7 @@
<argument index="0" name="enable" type="bool">
</argument>
<description>
- Enables or disables physics (i.e. fixed framerate) processing. When a node is being processed, it will receive a [constant NOTIFICATION_PHYSICS_PROCESS] at a fixed (usually 60 FPS, see [member Engine.target_fps] to change) interval (and the [method _physics_process] callback will be called if exists). Enabled automatically if [method _physics_process] is overridden. Any calls to this before [method _ready] will be ignored.
+ Enables or disables physics (i.e. fixed framerate) processing. When a node is being processed, it will receive a [constant NOTIFICATION_PHYSICS_PROCESS] at a fixed (usually 60 FPS, see [member Engine.iterations_per_second] to change) interval (and the [method _physics_process] callback will be called if exists). Enabled automatically if [method _physics_process] is overridden. Any calls to this before [method _ready] will be ignored.
</description>
</method>
<method name="set_physics_process_internal">
diff --git a/doc/classes/ProjectSettings.xml b/doc/classes/ProjectSettings.xml
index a2c6ed34e0..8347283785 100644
--- a/doc/classes/ProjectSettings.xml
+++ b/doc/classes/ProjectSettings.xml
@@ -179,9 +179,6 @@
</method>
</methods>
<members>
- <member name="android/modules" type="String" setter="" getter="" default="&quot;&quot;">
- Comma-separated list of custom Android modules (which must have been built in the Android export templates) using their Java package path, e.g. [code]org/godotengine/org/GodotPaymentV3,org/godotengine/godot/MyCustomSingleton"[/code].
- </member>
<member name="application/boot_splash/bg_color" type="Color" setter="" getter="" default="Color( 0.14, 0.14, 0.14, 1 )">
Background color for the boot splash.
</member>
diff --git a/doc/classes/Rect2.xml b/doc/classes/Rect2.xml
index 26abfb538b..dbf461cdf1 100644
--- a/doc/classes/Rect2.xml
+++ b/doc/classes/Rect2.xml
@@ -143,8 +143,11 @@
</return>
<argument index="0" name="b" type="Rect2">
</argument>
+ <argument index="1" name="include_borders" type="bool" default="false">
+ </argument>
<description>
- Returns [code]true[/code] if the [Rect2] overlaps with another.
+ Returns [code]true[/code] if the [Rect2] overlaps with [code]b[/code] (i.e. they have at least one point in common).
+ If [code]include_borders[/code] is [code]true[/code], they will also be considered overlapping if their borders touch, even without intersection.
</description>
</method>
<method name="is_equal_approx">
diff --git a/doc/classes/Semaphore.xml b/doc/classes/Semaphore.xml
index eca98fb10e..19719eea93 100644
--- a/doc/classes/Semaphore.xml
+++ b/doc/classes/Semaphore.xml
@@ -11,17 +11,24 @@
</tutorials>
<methods>
<method name="post">
- <return type="int" enum="Error">
+ <return type="void">
</return>
<description>
- Lowers the [Semaphore], allowing one more thread in. Returns [constant OK] on success, [constant ERR_BUSY] otherwise.
+ Lowers the [Semaphore], allowing one more thread in.
</description>
</method>
<method name="wait">
+ <return type="void">
+ </return>
+ <description>
+ Waits for the [Semaphore], if its value is zero, blocks until non-zero.
+ </description>
+ </method>
+ <method name="try_wait">
<return type="int" enum="Error">
</return>
<description>
- Tries to wait for the [Semaphore], if its value is zero, blocks until non-zero. Returns [constant OK] on success, [constant ERR_BUSY] otherwise.
+ Like [method wait], but won't block, so if the value is zero, fails immediately and returns [constant ERR_BUSY]. If non-zero, it returns [constant OK] to report success.
</description>
</method>
</methods>
diff --git a/doc/classes/SpringArm.xml b/doc/classes/SpringArm.xml
index f426cfc352..780ed5077d 100644
--- a/doc/classes/SpringArm.xml
+++ b/doc/classes/SpringArm.xml
@@ -18,14 +18,14 @@
<argument index="0" name="RID" type="RID">
</argument>
<description>
- Adds the object with the given [RID] to the list of objects excluded from the collision check.
+ Adds the [PhysicsBody] object with the given [RID] to the list of [PhysicsBody] objects excluded from the collision check.
</description>
</method>
<method name="clear_excluded_objects">
<return type="void">
</return>
<description>
- Clears the list of objects excluded from the collision check.
+ Clears the list of [PhysicsBody] objects excluded from the collision check.
</description>
</method>
<method name="get_hit_length">
@@ -41,7 +41,7 @@
<argument index="0" name="RID" type="RID">
</argument>
<description>
- Removes the given [RID] from the list of objects excluded from the collision check.
+ Removes the given [RID] from the list of [PhysicsBody] objects excluded from the collision check.
</description>
</method>
</methods>
diff --git a/doc/classes/Tween.xml b/doc/classes/Tween.xml
index 342acaae96..371b027534 100644
--- a/doc/classes/Tween.xml
+++ b/doc/classes/Tween.xml
@@ -21,7 +21,7 @@
</tutorials>
<methods>
<method name="follow_method">
- <return type="bool">
+ <return type="void">
</return>
<argument index="0" name="object" type="Object">
</argument>
@@ -47,7 +47,7 @@
</description>
</method>
<method name="follow_property">
- <return type="bool">
+ <return type="void">
</return>
<argument index="0" name="object" type="Object">
</argument>
@@ -80,7 +80,7 @@
</description>
</method>
<method name="interpolate_callback">
- <return type="bool">
+ <return type="void">
</return>
<argument index="0" name="object" type="Object">
</argument>
@@ -103,7 +103,7 @@
</description>
</method>
<method name="interpolate_deferred_callback">
- <return type="bool">
+ <return type="void">
</return>
<argument index="0" name="object" type="Object">
</argument>
@@ -126,7 +126,7 @@
</description>
</method>
<method name="interpolate_method">
- <return type="bool">
+ <return type="void">
</return>
<argument index="0" name="object" type="Object">
</argument>
@@ -150,7 +150,7 @@
</description>
</method>
<method name="interpolate_property">
- <return type="bool">
+ <return type="void">
</return>
<argument index="0" name="object" type="Object">
</argument>
@@ -182,7 +182,7 @@
</description>
</method>
<method name="remove">
- <return type="bool">
+ <return type="void">
</return>
<argument index="0" name="object" type="Object">
</argument>
@@ -193,14 +193,14 @@
</description>
</method>
<method name="remove_all">
- <return type="bool">
+ <return type="void">
</return>
<description>
Stops animation and removes all tweens.
</description>
</method>
<method name="reset">
- <return type="bool">
+ <return type="void">
</return>
<argument index="0" name="object" type="Object">
</argument>
@@ -211,14 +211,14 @@
</description>
</method>
<method name="reset_all">
- <return type="bool">
+ <return type="void">
</return>
<description>
Resets all tweens to their initial values (the ones given, not those before the tween).
</description>
</method>
<method name="resume">
- <return type="bool">
+ <return type="void">
</return>
<argument index="0" name="object" type="Object">
</argument>
@@ -229,14 +229,14 @@
</description>
</method>
<method name="resume_all">
- <return type="bool">
+ <return type="void">
</return>
<description>
Continues animating all stopped tweens.
</description>
</method>
<method name="seek">
- <return type="bool">
+ <return type="void">
</return>
<argument index="0" name="time" type="float">
</argument>
@@ -254,14 +254,14 @@
</description>
</method>
<method name="start">
- <return type="bool">
+ <return type="void">
</return>
<description>
Starts the tween. You can define animations both before and after this.
</description>
</method>
<method name="stop">
- <return type="bool">
+ <return type="void">
</return>
<argument index="0" name="object" type="Object">
</argument>
@@ -272,14 +272,14 @@
</description>
</method>
<method name="stop_all">
- <return type="bool">
+ <return type="void">
</return>
<description>
Stops animating all tweens.
</description>
</method>
<method name="targeting_method">
- <return type="bool">
+ <return type="void">
</return>
<argument index="0" name="object" type="Object">
</argument>
@@ -305,7 +305,7 @@
</description>
</method>
<method name="targeting_property">
- <return type="bool">
+ <return type="void">
</return>
<argument index="0" name="object" type="Object">
</argument>
diff --git a/drivers/unix/dir_access_unix.cpp b/drivers/unix/dir_access_unix.cpp
index 02cb4fa956..715bc56003 100644
--- a/drivers/unix/dir_access_unix.cpp
+++ b/drivers/unix/dir_access_unix.cpp
@@ -269,6 +269,11 @@ String DirAccessUnix::get_drive(int p_drive) {
return list[p_drive];
}
+bool DirAccessUnix::drives_are_shortcuts() {
+
+ return true;
+}
+
Error DirAccessUnix::make_dir(String p_dir) {
GLOBAL_LOCK_FUNCTION
@@ -337,7 +342,7 @@ Error DirAccessUnix::change_dir(String p_dir) {
return OK;
}
-String DirAccessUnix::get_current_dir() {
+String DirAccessUnix::get_current_dir(bool p_include_drive) {
String base = _get_root_path();
if (base != "") {
diff --git a/drivers/unix/dir_access_unix.h b/drivers/unix/dir_access_unix.h
index 32ddc76638..b403d8e356 100644
--- a/drivers/unix/dir_access_unix.h
+++ b/drivers/unix/dir_access_unix.h
@@ -63,9 +63,10 @@ public:
virtual int get_drive_count();
virtual String get_drive(int p_drive);
+ virtual bool drives_are_shortcuts();
virtual Error change_dir(String p_dir); ///< can be relative or absolute, return false on success
- virtual String get_current_dir(); ///< return current dir location
+ virtual String get_current_dir(bool p_include_drive = true); ///< return current dir location
virtual Error make_dir(String p_dir);
virtual bool file_exists(String p_file);
diff --git a/drivers/unix/os_unix.cpp b/drivers/unix/os_unix.cpp
index 1d94b9618d..d21b095037 100644
--- a/drivers/unix/os_unix.cpp
+++ b/drivers/unix/os_unix.cpp
@@ -38,7 +38,6 @@
#include "drivers/unix/file_access_unix.h"
#include "drivers/unix/net_socket_posix.h"
#include "drivers/unix/rw_lock_posix.h"
-#include "drivers/unix/semaphore_posix.h"
#include "drivers/unix/thread_posix.h"
#include "servers/visual_server.h"
@@ -121,13 +120,9 @@ void OS_Unix::initialize_core() {
#ifdef NO_THREADS
ThreadDummy::make_default();
- SemaphoreDummy::make_default();
RWLockDummy::make_default();
#else
ThreadPosix::make_default();
-#if !defined(OSX_ENABLED) && !defined(IPHONE_ENABLED)
- SemaphorePosix::make_default();
-#endif
RWLockPosix::make_default();
#endif
FileAccess::make_default<FileAccessUnix>(FileAccess::ACCESS_RESOURCES);
@@ -307,7 +302,7 @@ Error OS_Unix::execute(const String &p_path, const List<String> &p_arguments, bo
if (p_pipe_mutex) {
p_pipe_mutex->lock();
}
- (*r_pipe) += buf;
+ (*r_pipe) += String::utf8(buf);
if (p_pipe_mutex) {
p_pipe_mutex->unlock();
}
diff --git a/drivers/vulkan/rendering_device_vulkan.cpp b/drivers/vulkan/rendering_device_vulkan.cpp
index 2bf8a16091..3b0d1f6ac3 100644
--- a/drivers/vulkan/rendering_device_vulkan.cpp
+++ b/drivers/vulkan/rendering_device_vulkan.cpp
@@ -4182,7 +4182,10 @@ RenderingDeviceVulkan::DescriptorPool *RenderingDeviceVulkan::_descriptor_pool_a
descriptor_pool_create_info.poolSizeCount = sizes.size();
descriptor_pool_create_info.pPoolSizes = sizes.ptr();
VkResult res = vkCreateDescriptorPool(device, &descriptor_pool_create_info, NULL, &pool->pool);
- ERR_FAIL_COND_V(res, NULL);
+ if (res) {
+ memdelete(pool);
+ ERR_FAIL_COND_V(res, NULL);
+ }
descriptor_pools[p_key].insert(pool);
}
@@ -6862,7 +6865,7 @@ void RenderingDeviceVulkan::initialize(VulkanContext *p_context) {
max_descriptors_per_pool = GLOBAL_DEF("rendering/vulkan/descriptor_pools/max_descriptors_per_pool", 64);
//check to make sure DescriptorPoolKey is good
- ERR_FAIL_COND(sizeof(uint64_t) * 3 < UNIFORM_TYPE_MAX * sizeof(uint16_t));
+ static_assert(sizeof(uint64_t) * 3 >= UNIFORM_TYPE_MAX * sizeof(uint16_t));
draw_list = NULL;
draw_list_count = 0;
diff --git a/drivers/vulkan/vulkan_context.cpp b/drivers/vulkan/vulkan_context.cpp
index 7068f707e6..038cae7f96 100644
--- a/drivers/vulkan/vulkan_context.cpp
+++ b/drivers/vulkan/vulkan_context.cpp
@@ -44,7 +44,8 @@
#define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0]))
#define APP_SHORT_NAME "GodotEngine"
-VKAPI_ATTR VkBool32 VKAPI_CALL VulkanContext::_debug_messenger_callback(VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity,
+VKAPI_ATTR VkBool32 VKAPI_CALL VulkanContext::_debug_messenger_callback(
+ VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity,
VkDebugUtilsMessageTypeFlagsEXT messageType,
const VkDebugUtilsMessengerCallbackDataEXT *pCallbackData,
void *pUserData) {
@@ -67,24 +68,6 @@ VKAPI_ATTR VkBool32 VKAPI_CALL VulkanContext::_debug_messenger_callback(VkDebugU
return VK_FALSE;
}
- String severity_string;
- switch (messageSeverity) {
- case VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT:
- severity_string = "VERBOSE : ";
- break;
- case VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT:
- severity_string = "INFO : ";
- break;
- case VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT:
- severity_string = "WARNING : ";
- break;
- case VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT:
- severity_string = "ERROR : ";
- break;
- case VK_DEBUG_UTILS_MESSAGE_SEVERITY_FLAG_BITS_MAX_ENUM_EXT:
- break;
- }
-
String type_string;
switch (messageType) {
case (VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT):
@@ -133,16 +116,31 @@ VKAPI_ATTR VkBool32 VKAPI_CALL VulkanContext::_debug_messenger_callback(VkDebugU
}
}
- String error_message(severity_string + type_string +
+ String error_message(type_string +
" - Message Id Number: " + String::num_int64(pCallbackData->messageIdNumber) +
" | Message Id Name: " + pCallbackData->pMessageIdName +
"\n\t" + pCallbackData->pMessage +
objects_string + labels_string);
- ERR_PRINT(error_message);
-
- CRASH_COND_MSG(Engine::get_singleton()->is_abort_on_gpu_errors_enabled(),
- "Crashing, because abort on GPU errors is enabled.");
+ // Convert VK severity to our own log macros.
+ switch (messageSeverity) {
+ case VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT:
+ print_verbose(error_message);
+ break;
+ case VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT:
+ print_line(error_message);
+ break;
+ case VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT:
+ WARN_PRINT(error_message);
+ break;
+ case VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT:
+ ERR_PRINT(error_message);
+ CRASH_COND_MSG(Engine::get_singleton()->is_abort_on_gpu_errors_enabled(),
+ "Crashing, because abort on GPU errors is enabled.");
+ break;
+ case VK_DEBUG_UTILS_MESSAGE_SEVERITY_FLAG_BITS_MAX_ENUM_EXT:
+ break; // Shouldn't happen, only handling to make compilers happy.
+ }
return VK_FALSE;
}
@@ -358,8 +356,39 @@ Error VulkanContext::_create_physical_device() {
free(physical_devices);
ERR_FAIL_V(ERR_CANT_CREATE);
}
- /* for now, just grab the first physical device */
- gpu = physical_devices[0];
+
+ /*Find the first discrete GPU with the most VRAM.*/
+ {
+ print_line("Selecting primary GPU.");
+ VkPhysicalDeviceProperties device_properties;
+ VkPhysicalDeviceMemoryProperties memory_properties;
+ gpu = physical_devices[0];
+ uint32_t largest_vram_size = 0;
+ VkPhysicalDeviceType gpu_type = VK_PHYSICAL_DEVICE_TYPE_OTHER;
+ for (uint32_t i = 0; i < gpu_count; i++) {
+ vkGetPhysicalDeviceProperties(physical_devices[i], &device_properties);
+
+ /*Skip virtual and CPU devices for now.*/
+ if (device_properties.deviceType > VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU) {
+ continue;
+ }
+
+ vkGetPhysicalDeviceMemoryProperties(physical_devices[i], &memory_properties);
+
+ /*Total all heaps in case of 3GB+1GB configurations and similar.*/
+ uint32_t memory_size = 0;
+ for (uint32_t j = 0; j < memory_properties.memoryHeapCount; j++) {
+ memory_size += memory_properties.memoryHeaps[j].size;
+ }
+
+ if ((device_properties.deviceType >= gpu_type) || (device_properties.deviceType == gpu_type && memory_size > largest_vram_size)) {
+ gpu = physical_devices[i];
+ gpu_type = device_properties.deviceType;
+ largest_vram_size = memory_size;
+ print_line(device_properties.deviceName);
+ }
+ }
+ }
free(physical_devices);
/* Look for device extensions */
diff --git a/drivers/windows/dir_access_windows.cpp b/drivers/windows/dir_access_windows.cpp
index 8e0eac6986..cf09f79832 100644
--- a/drivers/windows/dir_access_windows.cpp
+++ b/drivers/windows/dir_access_windows.cpp
@@ -189,7 +189,7 @@ Error DirAccessWindows::make_dir(String p_dir) {
return ERR_CANT_CREATE;
}
-String DirAccessWindows::get_current_dir() {
+String DirAccessWindows::get_current_dir(bool p_include_drive) {
String base = _get_root_path();
if (base != "") {
@@ -203,7 +203,11 @@ String DirAccessWindows::get_current_dir() {
} else {
}
- return current_dir;
+ if (p_include_drive) {
+ return current_dir;
+ } else {
+ return current_dir.right(current_dir.find(":") + 1);
+ }
}
bool DirAccessWindows::file_exists(String p_file) {
diff --git a/drivers/windows/dir_access_windows.h b/drivers/windows/dir_access_windows.h
index 43951c0be9..f59e4d7030 100644
--- a/drivers/windows/dir_access_windows.h
+++ b/drivers/windows/dir_access_windows.h
@@ -69,7 +69,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(); ///< return current dir location
+ virtual String get_current_dir(bool p_include_drive = true); ///< return current dir location
virtual bool file_exists(String p_file);
virtual bool dir_exists(String p_dir);
diff --git a/editor/debugger/editor_debugger_inspector.cpp b/editor/debugger/editor_debugger_inspector.cpp
index 6ada212323..1506d64b63 100644
--- a/editor/debugger/editor_debugger_inspector.cpp
+++ b/editor/debugger/editor_debugger_inspector.cpp
@@ -91,6 +91,7 @@ EditorDebuggerInspector::EditorDebuggerInspector() {
}
EditorDebuggerInspector::~EditorDebuggerInspector() {
+ clear_cache();
memdelete(variables);
}
diff --git a/editor/debugger/script_editor_debugger.cpp b/editor/debugger/script_editor_debugger.cpp
index 40c79b08e6..169ff61e71 100644
--- a/editor/debugger/script_editor_debugger.cpp
+++ b/editor/debugger/script_editor_debugger.cpp
@@ -1423,7 +1423,7 @@ void ScriptEditorDebugger::_clear_errors_list() {
error_tree->clear();
error_count = 0;
warning_count = 0;
- _notification(NOTIFICATION_PROCESS);
+ update_tabs();
}
// Right click on specific file(s) or folder(s).
@@ -1834,7 +1834,5 @@ ScriptEditorDebugger::ScriptEditorDebugger(EditorNode *p_editor) {
ScriptEditorDebugger::~ScriptEditorDebugger() {
ppeer->set_stream_peer(Ref<StreamPeer>());
-
- inspector->clear_cache();
memdelete(scene_tree);
}
diff --git a/editor/editor_file_dialog.cpp b/editor/editor_file_dialog.cpp
index 6262680454..250fa6b3e0 100644
--- a/editor/editor_file_dialog.cpp
+++ b/editor/editor_file_dialog.cpp
@@ -199,7 +199,10 @@ Vector<String> EditorFileDialog::get_selected_files() const {
void EditorFileDialog::update_dir() {
- dir->set_text(dir_access->get_current_dir());
+ if (drives->is_visible()) {
+ drives->select(dir_access->get_current_drive());
+ }
+ dir->set_text(dir_access->get_current_dir(false));
// Disable "Open" button only when selecting file(s) mode.
get_ok()->set_disabled(_is_open_should_be_disabled());
@@ -691,7 +694,7 @@ void EditorFileDialog::update_file_name() {
String base_name = file_str.get_basename();
Vector<String> filter_substr = filter_str.split(";");
if (filter_substr.size() >= 2) {
- file_str = base_name + "." + filter_substr[1].strip_edges().to_lower();
+ file_str = base_name + "." + filter_substr[0].strip_edges().lstrip("*.").to_lower();
} else {
file_str = base_name + "." + filter_str.get_extension().strip_edges().to_lower();
}
@@ -946,7 +949,7 @@ void EditorFileDialog::add_filter(const String &p_filter) {
String EditorFileDialog::get_current_dir() const {
- return dir->get_text();
+ return dir_access->get_current_dir();
}
String EditorFileDialog::get_current_file() const {
@@ -954,7 +957,7 @@ String EditorFileDialog::get_current_file() const {
}
String EditorFileDialog::get_current_path() const {
- return dir->get_text().plus_file(file->get_text());
+ return dir_access->get_current_dir().plus_file(file->get_text());
}
void EditorFileDialog::set_current_dir(const String &p_dir) {
@@ -1149,6 +1152,12 @@ void EditorFileDialog::_update_drives() {
drives->hide();
} else {
drives->clear();
+ Node *dp = drives->get_parent();
+ if (dp) {
+ dp->remove_child(drives);
+ }
+ dp = dir_access->drives_are_shortcuts() ? shortcuts_container : drives_container;
+ dp->add_child(drives);
drives->show();
for (int i = 0; i < dir_access->get_drive_count(); i++) {
@@ -1383,6 +1392,7 @@ void EditorFileDialog::_bind_methods() {
ClassDB::bind_method(D_METHOD("is_showing_hidden_files"), &EditorFileDialog::is_showing_hidden_files);
ClassDB::bind_method(D_METHOD("_update_file_name"), &EditorFileDialog::update_file_name);
ClassDB::bind_method(D_METHOD("_update_dir"), &EditorFileDialog::update_dir);
+ ClassDB::bind_method(D_METHOD("_update_file_list"), &EditorFileDialog::update_file_list);
ClassDB::bind_method(D_METHOD("_thumbnail_done"), &EditorFileDialog::_thumbnail_done);
ClassDB::bind_method(D_METHOD("set_display_mode", "mode"), &EditorFileDialog::set_display_mode);
ClassDB::bind_method(D_METHOD("get_display_mode"), &EditorFileDialog::get_display_mode);
@@ -1517,6 +1527,9 @@ EditorFileDialog::EditorFileDialog() {
pathhb->add_child(memnew(Label(TTR("Path:"))));
+ drives_container = memnew(HBoxContainer);
+ pathhb->add_child(drives_container);
+
dir = memnew(LineEdit);
pathhb->add_child(dir);
dir->set_h_size_flags(SIZE_EXPAND_FILL);
@@ -1560,8 +1573,10 @@ EditorFileDialog::EditorFileDialog() {
mode_list->set_tooltip(TTR("View items as a list."));
pathhb->add_child(mode_list);
+ shortcuts_container = memnew(HBoxContainer);
+ pathhb->add_child(shortcuts_container);
+
drives = memnew(OptionButton);
- pathhb->add_child(drives);
drives->connect("item_selected", callable_mp(this, &EditorFileDialog::_select_drive));
makedir = memnew(Button);
diff --git a/editor/editor_file_dialog.h b/editor/editor_file_dialog.h
index 8b48604b31..998ac33761 100644
--- a/editor/editor_file_dialog.h
+++ b/editor/editor_file_dialog.h
@@ -100,6 +100,8 @@ private:
ToolButton *dir_next;
ToolButton *dir_up;
+ HBoxContainer *drives_container;
+ HBoxContainer *shortcuts_container;
OptionButton *drives;
ItemList *item_list;
PopupMenu *item_menu;
diff --git a/editor/editor_folding.cpp b/editor/editor_folding.cpp
index 8308cd760b..507a77e641 100644
--- a/editor/editor_folding.cpp
+++ b/editor/editor_folding.cpp
@@ -133,6 +133,8 @@ void EditorFolding::_fill_folds(const Node *p_root, const Node *p_node, Array &p
}
void EditorFolding::save_scene_folding(const Node *p_scene, const String &p_path) {
+ ERR_FAIL_NULL(p_scene);
+
FileAccessRef file_check = FileAccess::create(FileAccess::ACCESS_RESOURCES);
if (!file_check->file_exists(p_path)) //This can happen when creating scene from FilesystemDock. It has path, but no file.
return;
diff --git a/editor/editor_inspector.cpp b/editor/editor_inspector.cpp
index 3f99048ba6..ed5a411d8b 100644
--- a/editor/editor_inspector.cpp
+++ b/editor/editor_inspector.cpp
@@ -29,6 +29,7 @@
/*************************************************************************/
#include "editor_inspector.h"
+
#include "array_property_edit.h"
#include "dictionary_property_edit.h"
#include "editor_feature_profile.h"
@@ -2052,16 +2053,16 @@ void EditorInspector::_edit_set(const String &p_name, const Variant &p_value, bo
}
}
-void EditorInspector::_property_changed(const String &p_path, const Variant &p_value, const String &p_name, bool changing) {
+void EditorInspector::_property_changed(const String &p_path, const Variant &p_value, const String &p_name, bool p_changing) {
// The "changing" variable must be true for properties that trigger events as typing occurs,
- // like "text_changed" signal. eg: Text property of Label, Button, RichTextLabel, etc.
- if (changing)
+ // like "text_changed" signal. E.g. text property of Label, Button, RichTextLabel, etc.
+ if (p_changing)
this->changing++;
_edit_set(p_path, p_value, false, p_name);
- if (changing)
+ if (p_changing)
this->changing--;
if (restart_request_props.has(p_path)) {
diff --git a/editor/editor_inspector.h b/editor/editor_inspector.h
index 7ad55a13ad..7a1542d30f 100644
--- a/editor/editor_inspector.h
+++ b/editor/editor_inspector.h
@@ -302,7 +302,7 @@ class EditorInspector : public ScrollContainer {
void _edit_set(const String &p_name, const Variant &p_value, bool p_refresh_all, const String &p_changed_field);
- void _property_changed(const String &p_path, const Variant &p_value, const String &p_name = "", bool changing = false);
+ void _property_changed(const String &p_path, const Variant &p_value, const String &p_name = "", bool p_changing = false);
void _property_changed_update_all(const String &p_path, const Variant &p_value, const String &p_name = "", bool p_changing = false);
void _multiple_properties_changed(Vector<String> p_paths, Array p_values);
void _property_keyed(const String &p_path, bool p_advance);
diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp
index 6f8027ba64..43e640b40e 100644
--- a/editor/editor_node.cpp
+++ b/editor/editor_node.cpp
@@ -3279,7 +3279,7 @@ void EditorNode::_clear_undo_history() {
void EditorNode::set_current_scene(int p_idx) {
//Save the folding in case the scene gets reloaded.
- if (editor_data.get_scene_path(p_idx) != "")
+ if (editor_data.get_scene_path(p_idx) != "" && editor_data.get_edited_scene_root(p_idx))
editor_folding.save_scene_folding(editor_data.get_edited_scene_root(p_idx), editor_data.get_scene_path(p_idx));
if (editor_data.check_and_update_scene(p_idx)) {
diff --git a/editor/editor_plugin.cpp b/editor/editor_plugin.cpp
index 2135e70de3..10ecdb19c0 100644
--- a/editor/editor_plugin.cpp
+++ b/editor/editor_plugin.cpp
@@ -862,7 +862,7 @@ void EditorPlugin::_bind_methods() {
ClassDB::add_virtual_method(get_class_static(), MethodInfo("forward_canvas_force_draw_over_viewport", PropertyInfo(Variant::OBJECT, "overlay", PROPERTY_HINT_RESOURCE_TYPE, "Control")));
ClassDB::add_virtual_method(get_class_static(), MethodInfo(Variant::BOOL, "forward_spatial_gui_input", PropertyInfo(Variant::OBJECT, "camera", PROPERTY_HINT_RESOURCE_TYPE, "Camera"), PropertyInfo(Variant::OBJECT, "event", PROPERTY_HINT_RESOURCE_TYPE, "InputEvent")));
ClassDB::add_virtual_method(get_class_static(), MethodInfo(Variant::STRING, "get_plugin_name"));
- ClassDB::add_virtual_method(get_class_static(), MethodInfo(Variant::OBJECT, "get_plugin_icon"));
+ ClassDB::add_virtual_method(get_class_static(), MethodInfo(PropertyInfo(Variant::OBJECT, "icon", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"), "get_plugin_icon"));
ClassDB::add_virtual_method(get_class_static(), MethodInfo(Variant::BOOL, "has_main_screen"));
ClassDB::add_virtual_method(get_class_static(), MethodInfo("make_visible", PropertyInfo(Variant::BOOL, "visible")));
ClassDB::add_virtual_method(get_class_static(), MethodInfo("edit", PropertyInfo(Variant::OBJECT, "object")));
diff --git a/editor/editor_properties_array_dict.cpp b/editor/editor_properties_array_dict.cpp
index 4ae6ced35b..c2a6aeb582 100644
--- a/editor/editor_properties_array_dict.cpp
+++ b/editor/editor_properties_array_dict.cpp
@@ -165,10 +165,10 @@ EditorPropertyDictionaryObject::EditorPropertyDictionaryObject() {
///////////////////// ARRAY ///////////////////////////
-void EditorPropertyArray::_property_changed(const String &p_prop, Variant p_value, const String &p_name, bool changing) {
+void EditorPropertyArray::_property_changed(const String &p_property, Variant p_value, const String &p_name, bool p_changing) {
- if (p_prop.begins_with("indices")) {
- int idx = p_prop.get_slice("/", 1).to_int();
+ if (p_property.begins_with("indices")) {
+ int idx = p_property.get_slice("/", 1).to_int();
Variant array = object->get_array();
array.set(idx, p_value);
emit_changed(get_edited_property(), array, "", true);
@@ -213,7 +213,7 @@ void EditorPropertyArray::_change_type_menu(int p_index) {
update_property();
}
-void EditorPropertyArray::_object_id_selected(const String &p_property, ObjectID p_id) {
+void EditorPropertyArray::_object_id_selected(const StringName &p_property, ObjectID p_id) {
emit_signal("object_id_selected", p_property, p_id);
}
@@ -528,16 +528,16 @@ EditorPropertyArray::EditorPropertyArray() {
///////////////////// DICTIONARY ///////////////////////////
-void EditorPropertyDictionary::_property_changed(const String &p_prop, Variant p_value, const String &p_name, bool changing) {
+void EditorPropertyDictionary::_property_changed(const String &p_property, Variant p_value, const String &p_name, bool p_changing) {
- if (p_prop == "new_item_key") {
+ if (p_property == "new_item_key") {
object->set_new_item_key(p_value);
- } else if (p_prop == "new_item_value") {
+ } else if (p_property == "new_item_value") {
object->set_new_item_value(p_value);
- } else if (p_prop.begins_with("indices")) {
- int idx = p_prop.get_slice("/", 1).to_int();
+ } else if (p_property.begins_with("indices")) {
+ int idx = p_property.get_slice("/", 1).to_int();
Dictionary dict = object->get_dict();
Variant key = dict.get_key_at_index(idx);
dict[key] = p_value;
@@ -969,7 +969,7 @@ void EditorPropertyDictionary::update_property() {
}
}
-void EditorPropertyDictionary::_object_id_selected(const String &p_property, ObjectID p_id) {
+void EditorPropertyDictionary::_object_id_selected(const StringName &p_property, ObjectID p_id) {
emit_signal("object_id_selected", p_property, p_id);
}
diff --git a/editor/editor_properties_array_dict.h b/editor/editor_properties_array_dict.h
index d4f8abf583..51a4be1b3a 100644
--- a/editor/editor_properties_array_dict.h
+++ b/editor/editor_properties_array_dict.h
@@ -100,11 +100,11 @@ class EditorPropertyArray : public EditorProperty {
void _page_changed(double p_page);
void _length_changed(double p_page);
void _edit_pressed();
- void _property_changed(const String &p_prop, Variant p_value, const String &p_name = String(), bool changing = false);
+ void _property_changed(const String &p_property, Variant p_value, const String &p_name = "", bool p_changing = false);
void _change_type(Object *p_button, int p_index);
void _change_type_menu(int p_index);
- void _object_id_selected(const String &p_property, ObjectID p_id);
+ void _object_id_selected(const StringName &p_property, ObjectID p_id);
void _remove_pressed(int p_index);
protected:
@@ -135,12 +135,12 @@ class EditorPropertyDictionary : public EditorProperty {
void _page_changed(double p_page);
void _edit_pressed();
- void _property_changed(const String &p_prop, Variant p_value, const String &p_name = String(), bool changing = false);
+ void _property_changed(const String &p_property, Variant p_value, const String &p_name = "", bool p_changing = false);
void _change_type(Object *p_button, int p_index);
void _change_type_menu(int p_index);
void _add_key_value();
- void _object_id_selected(const String &p_property, ObjectID p_id);
+ void _object_id_selected(const StringName &p_property, ObjectID p_id);
protected:
static void _bind_methods();
diff --git a/editor/editor_resource_preview.cpp b/editor/editor_resource_preview.cpp
index 40482dc367..3c401a6fc7 100644
--- a/editor/editor_resource_preview.cpp
+++ b/editor/editor_resource_preview.cpp
@@ -217,7 +217,7 @@ void EditorResourcePreview::_thread() {
exited = false;
while (!exit) {
- preview_sem->wait();
+ preview_sem.wait();
preview_mutex.lock();
if (queue.size()) {
@@ -379,7 +379,7 @@ void EditorResourcePreview::queue_edited_resource_preview(const Ref<Resource> &p
queue.push_back(item);
}
- preview_sem->post();
+ preview_sem.post();
}
void EditorResourcePreview::queue_resource_preview(const String &p_path, Object *p_receiver, const StringName &p_receiver_func, const Variant &p_userdata) {
@@ -403,7 +403,7 @@ void EditorResourcePreview::queue_resource_preview(const String &p_path, Object
queue.push_back(item);
}
- preview_sem->post();
+ preview_sem.post();
}
void EditorResourcePreview::add_preview_generator(const Ref<EditorResourcePreviewGenerator> &p_generator) {
@@ -462,7 +462,7 @@ void EditorResourcePreview::start() {
void EditorResourcePreview::stop() {
if (thread) {
exit = true;
- preview_sem->post();
+ preview_sem.post();
while (!exited) {
OS::get_singleton()->delay_usec(10000);
VisualServer::get_singleton()->sync(); //sync pending stuff, as thread may be blocked on visual server
@@ -476,7 +476,6 @@ void EditorResourcePreview::stop() {
EditorResourcePreview::EditorResourcePreview() {
thread = NULL;
singleton = this;
- preview_sem = SemaphoreOld::create();
order = 0;
exit = false;
exited = false;
@@ -485,5 +484,4 @@ EditorResourcePreview::EditorResourcePreview() {
EditorResourcePreview::~EditorResourcePreview() {
stop();
- memdelete(preview_sem);
}
diff --git a/editor/editor_resource_preview.h b/editor/editor_resource_preview.h
index ae347c0469..0e1684963c 100644
--- a/editor/editor_resource_preview.h
+++ b/editor/editor_resource_preview.h
@@ -71,7 +71,7 @@ class EditorResourcePreview : public Node {
List<QueueItem> queue;
Mutex preview_mutex;
- SemaphoreOld *preview_sem;
+ Semaphore preview_sem;
Thread *thread;
volatile bool exit;
volatile bool exited;
diff --git a/editor/editor_settings.h b/editor/editor_settings.h
index 4caa494d59..d4dd19ee10 100644
--- a/editor/editor_settings.h
+++ b/editor/editor_settings.h
@@ -117,7 +117,7 @@ private:
void _get_property_list(List<PropertyInfo> *p_list) const;
void _add_property_info_bind(const Dictionary &p_info);
- void _load_defaults(Ref<ConfigFile> p_extra_config = NULL);
+ void _load_defaults(Ref<ConfigFile> p_extra_config = Ref<ConfigFile>());
void _load_default_text_editor_theme();
bool _save_text_editor_theme(String p_file);
bool _is_default_text_editor_theme(String p_theme_name);
diff --git a/editor/editor_visual_profiler.cpp b/editor/editor_visual_profiler.cpp
index 1999fccddf..e9638148e2 100644
--- a/editor/editor_visual_profiler.cpp
+++ b/editor/editor_visual_profiler.cpp
@@ -824,7 +824,7 @@ EditorVisualProfiler::EditorVisualProfiler() {
frame_delay->set_wait_time(0.1);
frame_delay->set_one_shot(true);
add_child(frame_delay);
- frame_delay->connect("timeout", callable_mp(this, &EditorVisualProfiler::_update_frame));
+ frame_delay->connect("timeout", callable_mp(this, &EditorVisualProfiler::_update_frame), make_binds(false));
plot_delay = memnew(Timer);
plot_delay->set_wait_time(0.1);
diff --git a/editor/export_template_manager.cpp b/editor/export_template_manager.cpp
index bbf741948b..9328a5e04d 100644
--- a/editor/export_template_manager.cpp
+++ b/editor/export_template_manager.cpp
@@ -547,9 +547,7 @@ void ExportTemplateManager::_notification(int p_what) {
bool ExportTemplateManager::can_install_android_template() {
const String templates_dir = EditorSettings::get_singleton()->get_templates_dir().plus_file(VERSION_FULL_CONFIG);
- return FileAccess::exists(templates_dir.plus_file("android_source.zip")) &&
- FileAccess::exists(templates_dir.plus_file("android_release.apk")) &&
- FileAccess::exists(templates_dir.plus_file("android_debug.apk"));
+ return FileAccess::exists(templates_dir.plus_file("android_source.zip"));
}
Error ExportTemplateManager::install_android_template() {
@@ -563,13 +561,6 @@ Error ExportTemplateManager::install_android_template() {
// Make res://android dir (if it does not exist).
da->make_dir("android");
{
- // Add an empty .gdignore file to avoid scan.
- FileAccessRef f = FileAccess::open("res://android/.gdignore", FileAccess::WRITE);
- ERR_FAIL_COND_V(!f, ERR_CANT_CREATE);
- f->store_line("");
- f->close();
- }
- {
// Add version, to ensure building won't work if template and Godot version don't match.
FileAccessRef f = FileAccess::open("res://android/.build_version", FileAccess::WRITE);
ERR_FAIL_COND_V(!f, ERR_CANT_CREATE);
@@ -577,8 +568,19 @@ Error ExportTemplateManager::install_android_template() {
f->close();
}
- Error err = da->make_dir_recursive("android/build");
+ // Create the android plugins directory.
+ Error err = da->make_dir_recursive("android/plugins");
+ ERR_FAIL_COND_V(err != OK, err);
+
+ err = da->make_dir_recursive("android/build");
ERR_FAIL_COND_V(err != OK, err);
+ {
+ // Add an empty .gdignore file to avoid scan.
+ FileAccessRef f = FileAccess::open("res://android/build/.gdignore", FileAccess::WRITE);
+ ERR_FAIL_COND_V(!f, ERR_CANT_CREATE);
+ f->store_line("");
+ f->close();
+ }
// Uncompress source template.
diff --git a/editor/filesystem_dock.cpp b/editor/filesystem_dock.cpp
index cc62e93268..6c69f46941 100644
--- a/editor/filesystem_dock.cpp
+++ b/editor/filesystem_dock.cpp
@@ -323,7 +323,7 @@ void FileSystemDock::_notification(int p_what) {
file_list_popup->connect("id_pressed", callable_mp(this, &FileSystemDock::_file_list_rmb_option));
tree_popup->connect("id_pressed", callable_mp(this, &FileSystemDock::_tree_rmb_option));
- current_path->connect("text_entered", callable_mp(this, &FileSystemDock::_navigate_to_path));
+ current_path->connect("text_entered", callable_mp(this, &FileSystemDock::_navigate_to_path), make_binds(false));
always_show_folders = bool(EditorSettings::get_singleton()->get("docks/filesystem/always_show_folders"));
@@ -1406,8 +1406,8 @@ bool FileSystemDock::_check_existing() {
return true;
}
-void FileSystemDock::_move_operation_confirm(const String &p_to_path, bool overwrite) {
- if (!overwrite) {
+void FileSystemDock::_move_operation_confirm(const String &p_to_path, bool p_overwrite) {
+ if (!p_overwrite) {
to_move_path = p_to_path;
bool can_move = _check_existing();
if (!can_move) {
@@ -2620,7 +2620,7 @@ FileSystemDock::FileSystemDock(EditorNode *p_editor) {
move_dialog = memnew(EditorDirDialog);
move_dialog->get_ok()->set_text(TTR("Move"));
add_child(move_dialog);
- move_dialog->connect("dir_selected", callable_mp(this, &FileSystemDock::_move_operation_confirm));
+ move_dialog->connect("dir_selected", callable_mp(this, &FileSystemDock::_move_operation_confirm), make_binds(false));
rename_dialog = memnew(ConfirmationDialog);
VBoxContainer *rename_dialog_vb = memnew(VBoxContainer);
diff --git a/editor/filesystem_dock.h b/editor/filesystem_dock.h
index 1969f85e72..00f8cd9d50 100644
--- a/editor/filesystem_dock.h
+++ b/editor/filesystem_dock.h
@@ -222,7 +222,7 @@ private:
void _duplicate_operation_confirm();
void _move_with_overwrite();
bool _check_existing();
- void _move_operation_confirm(const String &p_to_path, bool overwrite = false);
+ void _move_operation_confirm(const String &p_to_path, bool p_overwrite = false);
void _tree_rmb_option(int p_option);
void _file_list_rmb_option(int p_option);
diff --git a/editor/groups_editor.cpp b/editor/groups_editor.cpp
index 650aaa9043..b4c9a01f2a 100644
--- a/editor/groups_editor.cpp
+++ b/editor/groups_editor.cpp
@@ -659,6 +659,7 @@ void GroupsEditor::_show_group_dialog() {
}
void GroupsEditor::_bind_methods() {
+ ClassDB::bind_method("update_tree", &GroupsEditor::update_tree);
}
GroupsEditor::GroupsEditor() {
diff --git a/editor/inspector_dock.h b/editor/inspector_dock.h
index 016aee12cf..61b3239169 100644
--- a/editor/inspector_dock.h
+++ b/editor/inspector_dock.h
@@ -105,7 +105,7 @@ class InspectorDock : public VBoxContainer {
void _warning_pressed();
void _resource_created();
- void _resource_selected(const RES &p_res, const String &p_property = "");
+ void _resource_selected(const RES &p_res, const String &p_property);
void _edit_forward();
void _edit_back();
void _menu_collapseall();
diff --git a/editor/plugins/asset_library_editor_plugin.cpp b/editor/plugins/asset_library_editor_plugin.cpp
index ea5da1d9ac..80b7e6ffc8 100644
--- a/editor/plugins/asset_library_editor_plugin.cpp
+++ b/editor/plugins/asset_library_editor_plugin.cpp
@@ -1360,7 +1360,7 @@ EditorAssetLibrary::EditorAssetLibrary(bool p_templates_only) {
filter->set_h_size_flags(SIZE_EXPAND_FILL);
filter->connect("text_entered", callable_mp(this, &EditorAssetLibrary::_search_text_entered));
search = memnew(Button(TTR("Search")));
- search->connect("pressed", callable_mp(this, &EditorAssetLibrary::_search));
+ search->connect("pressed", callable_mp(this, &EditorAssetLibrary::_search), make_binds(0));
search_hb->add_child(search);
if (!p_templates_only)
diff --git a/editor/plugins/gi_probe_editor_plugin.cpp b/editor/plugins/gi_probe_editor_plugin.cpp
index ddcbb11f7c..c077c23e8a 100644
--- a/editor/plugins/gi_probe_editor_plugin.cpp
+++ b/editor/plugins/gi_probe_editor_plugin.cpp
@@ -70,22 +70,33 @@ void GIProbeEditorPlugin::_notification(int p_what) {
return;
}
- String text;
-
- Vector3i size = gi_probe->get_estimated_cell_size();
- text = itos(size.x) + ", " + itos(size.y) + ", " + itos(size.z);
+ const Vector3i size = gi_probe->get_estimated_cell_size();
+ String text = vformat(String::utf8("%d × %d × %d"), size.x, size.y, size.z);
int data_size = 4;
if (GLOBAL_GET("rendering/quality/gi_probes/anisotropic")) {
data_size += 4;
}
- text += " - VRAM Size: " + String::num(size.x * size.y * size.z * data_size / (1024.0 * 1024.0), 2) + " Mb.";
+ const double size_mb = size.x * size.y * size.z * data_size / (1024.0 * 1024.0);
+ text += " - " + vformat(TTR("VRAM Size: %s MB"), String::num(size_mb, 2));
if (bake_info->get_text() == text) {
return;
}
- bake_info->add_color_override("font_color", bake_info->get_color("success_color", "Editor"));
+ // Color the label depending on the estimated performance level.
+ Color color;
+ if (size_mb <= 16.0 + CMP_EPSILON) {
+ // Fast.
+ color = bake_info->get_color("success_color", "Editor");
+ } else if (size_mb <= 64.0 + CMP_EPSILON) {
+ // Medium.
+ color = bake_info->get_color("warning_color", "Editor");
+ } else {
+ // Slow.
+ color = bake_info->get_color("error_color", "Editor");
+ }
+ bake_info->add_color_override("font_color", color);
bake_info->set_text(text);
}
}
diff --git a/editor/plugins/sprite_frames_editor_plugin.cpp b/editor/plugins/sprite_frames_editor_plugin.cpp
index 288aeb5c4a..5d615c7553 100644
--- a/editor/plugins/sprite_frames_editor_plugin.cpp
+++ b/editor/plugins/sprite_frames_editor_plugin.cpp
@@ -990,7 +990,7 @@ SpriteFramesEditor::SpriteFramesEditor() {
empty2->connect("pressed", callable_mp(this, &SpriteFramesEditor::_empty2_pressed));
move_up->connect("pressed", callable_mp(this, &SpriteFramesEditor::_up_pressed));
move_down->connect("pressed", callable_mp(this, &SpriteFramesEditor::_down_pressed));
- file->connect("files_selected", callable_mp(this, &SpriteFramesEditor::_file_load_request));
+ file->connect("files_selected", callable_mp(this, &SpriteFramesEditor::_file_load_request), make_binds(-1));
loading_scene = false;
sel = -1;
diff --git a/editor/plugins/visual_shader_editor_plugin.cpp b/editor/plugins/visual_shader_editor_plugin.cpp
index e7b7c05901..9100e28352 100644
--- a/editor/plugins/visual_shader_editor_plugin.cpp
+++ b/editor/plugins/visual_shader_editor_plugin.cpp
@@ -2978,7 +2978,7 @@ class VisualShaderNodePluginDefaultEditor : public VBoxContainer {
Ref<Resource> parent_resource;
public:
- void _property_changed(const String &prop, const Variant &p_value, const String &p_field, bool p_changing = false) {
+ void _property_changed(const String &p_property, const Variant &p_value, const String &p_field = "", bool p_changing = false) {
if (p_changing)
return;
@@ -2986,13 +2986,13 @@ public:
UndoRedo *undo_redo = EditorNode::get_singleton()->get_undo_redo();
updating = true;
- undo_redo->create_action(TTR("Edit Visual Property") + ": " + prop, UndoRedo::MERGE_ENDS);
- undo_redo->add_do_property(node.ptr(), prop, p_value);
- undo_redo->add_undo_property(node.ptr(), prop, node->get(prop));
+ undo_redo->create_action(TTR("Edit Visual Property") + ": " + p_property, UndoRedo::MERGE_ENDS);
+ undo_redo->add_do_property(node.ptr(), p_property, p_value);
+ undo_redo->add_undo_property(node.ptr(), p_property, node->get(p_property));
if (p_value.get_type() == Variant::OBJECT) {
- RES prev_res = node->get(prop);
+ RES prev_res = node->get(p_property);
RES curr_res = p_value;
if (curr_res.is_null()) {
diff --git a/editor/project_settings_editor.h b/editor/project_settings_editor.h
index 9054991dfd..52e7612acd 100644
--- a/editor/project_settings_editor.h
+++ b/editor/project_settings_editor.h
@@ -119,7 +119,7 @@ class ProjectSettingsEditor : public AcceptDialog {
void _item_del();
void _update_actions();
void _save();
- void _add_item(int p_item, Ref<InputEvent> p_exiting_event = NULL);
+ void _add_item(int p_item, Ref<InputEvent> p_exiting_event = Ref<InputEvent>());
void _edit_item(Ref<InputEvent> p_exiting_event);
void _action_check(String p_action);
diff --git a/editor/scene_tree_dock.cpp b/editor/scene_tree_dock.cpp
index b6741ccaec..c5ebf40482 100644
--- a/editor/scene_tree_dock.cpp
+++ b/editor/scene_tree_dock.cpp
@@ -1127,7 +1127,7 @@ void SceneTreeDock::_notification(int p_what) {
} break;
case NOTIFICATION_ENTER_TREE: {
- clear_inherit_confirm->connect("confirmed", callable_mp(this, &SceneTreeDock::_tool_selected), varray(TOOL_SCENE_CLEAR_INHERITANCE_CONFIRM));
+ clear_inherit_confirm->connect("confirmed", callable_mp(this, &SceneTreeDock::_tool_selected), make_binds(TOOL_SCENE_CLEAR_INHERITANCE_CONFIRM, false));
} break;
case NOTIFICATION_EXIT_TREE: {
@@ -2739,7 +2739,7 @@ void SceneTreeDock::_update_create_root_dialog() {
void SceneTreeDock::_favorite_root_selected(const String &p_class) {
selected_favorite_root = p_class;
- _tool_selected(TOOL_CREATE_FAVORITE, false);
+ _tool_selected(TOOL_CREATE_FAVORITE);
}
void SceneTreeDock::_feature_profile_changed() {
diff --git a/editor/spatial_editor_gizmos.cpp b/editor/spatial_editor_gizmos.cpp
index c155430eae..d6e443ec14 100644
--- a/editor/spatial_editor_gizmos.cpp
+++ b/editor/spatial_editor_gizmos.cpp
@@ -2724,10 +2724,11 @@ GIProbeGizmoPlugin::GIProbeGizmoPlugin() {
create_material("gi_probe_material", gizmo_color);
- gizmo_color.a = 0.5;
+ // This gizmo draws a lot of lines. Use a low opacity to make it not too intrusive.
+ gizmo_color.a = 0.1;
create_material("gi_probe_internal_material", gizmo_color);
- gizmo_color.a = 0.1;
+ gizmo_color.a = 0.05;
create_material("gi_probe_solid_material", gizmo_color);
create_icon_material("gi_probe_icon", SpatialEditor::get_singleton()->get_icon("GizmoGIProbe", "EditorIcons"));
diff --git a/main/tests/test_string.cpp b/main/tests/test_string.cpp
index eef3d9b84c..84731746fa 100644
--- a/main/tests/test_string.cpp
+++ b/main/tests/test_string.cpp
@@ -1172,7 +1172,7 @@ MainLoop *test() {
/** A character length != wchar_t may be forced, so the tests won't work */
- ERR_FAIL_COND_V(sizeof(CharType) != sizeof(wchar_t), NULL);
+ static_assert(sizeof(CharType) == sizeof(wchar_t));
int count = 0;
int passed = 0;
diff --git a/methods.py b/methods.py
index 3f03e6bbd2..28c6d0c097 100644
--- a/methods.py
+++ b/methods.py
@@ -1,5 +1,4 @@
import os
-import os.path
import re
import glob
import subprocess
@@ -564,7 +563,11 @@ def get_compiler_version(env):
if not env.msvc:
# Not using -dumpversion as some GCC distros only return major, and
# Clang used to return hardcoded 4.2.1: # https://reviews.llvm.org/D56803
- version = decode_utf8(subprocess.check_output([env['CXX'], '--version']).strip())
+ try:
+ version = decode_utf8(subprocess.check_output([env.subst(env['CXX']), '--version']).strip())
+ except (subprocess.CalledProcessError, OSError):
+ print("Couldn't parse CXX environment variable to infer compiler version.")
+ return None
else: # TODO: Implement for MSVC
return None
match = re.search('[0-9]+\.[0-9.]+', version)
diff --git a/modules/mono/build_scripts/mono_configure.py b/modules/mono/build_scripts/mono_configure.py
index 033c467da9..9a6198f13a 100644
--- a/modules/mono/build_scripts/mono_configure.py
+++ b/modules/mono/build_scripts/mono_configure.py
@@ -163,7 +163,7 @@ def configure(env, env_mono):
copy_file(mono_bin_path, '#bin', mono_dll_name + '.dll')
else:
- is_apple = (sys.platform == 'darwin' or "osxcross" in env)
+ is_apple = env['platform'] in ['osx', 'iphone']
sharedlib_ext = '.dylib' if is_apple else '.so'
diff --git a/modules/mono/editor/GodotTools/GodotTools/Ides/Rider/RiderPathLocator.cs b/modules/mono/editor/GodotTools/GodotTools/Ides/Rider/RiderPathLocator.cs
index 5965e0fbcf..77740f0e53 100644
--- a/modules/mono/editor/GodotTools/GodotTools/Ides/Rider/RiderPathLocator.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/Ides/Rider/RiderPathLocator.cs
@@ -218,20 +218,29 @@ namespace GodotTools.Ides.Rider
private static void CollectPathsFromRegistry(string registryKey, List<string> installPaths)
{
+ using (var key = Registry.CurrentUser.OpenSubKey(registryKey))
+ {
+ CollectPathsFromRegistry(installPaths, key);
+ }
using (var key = Registry.LocalMachine.OpenSubKey(registryKey))
{
- if (key == null) return;
- foreach (var subkeyName in key.GetSubKeyNames().Where(a => a.Contains("Rider")))
+ CollectPathsFromRegistry(installPaths, key);
+ }
+ }
+
+ private static void CollectPathsFromRegistry(List<string> installPaths, RegistryKey key)
+ {
+ if (key == null) return;
+ foreach (var subkeyName in key.GetSubKeyNames().Where(a => a.Contains("Rider")))
+ {
+ using (var subkey = key.OpenSubKey(subkeyName))
{
- using (var subkey = key.OpenSubKey(subkeyName))
- {
- var folderObject = subkey?.GetValue("InstallLocation");
- if (folderObject == null) continue;
- var folder = folderObject.ToString();
- var possiblePath = Path.Combine(folder, @"bin\rider64.exe");
- if (File.Exists(possiblePath))
- installPaths.Add(possiblePath);
- }
+ var folderObject = subkey?.GetValue("InstallLocation");
+ if (folderObject == null) continue;
+ var folder = folderObject.ToString();
+ var possiblePath = Path.Combine(folder, @"bin\rider64.exe");
+ if (File.Exists(possiblePath))
+ installPaths.Add(possiblePath);
}
}
}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/GD.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/GD.cs
index 19962d418a..2a9c2d73b1 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/GD.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/GD.cs
@@ -83,7 +83,7 @@ namespace Godot
public static void Print(params object[] what)
{
- godot_icall_GD_print(Array.ConvertAll(what, x => x.ToString()));
+ godot_icall_GD_print(Array.ConvertAll(what, x => x?.ToString()));
}
public static void PrintStack()
diff --git a/modules/stb_vorbis/audio_stream_ogg_vorbis.cpp b/modules/stb_vorbis/audio_stream_ogg_vorbis.cpp
index 054ed088df..42f341cef7 100644
--- a/modules/stb_vorbis/audio_stream_ogg_vorbis.cpp
+++ b/modules/stb_vorbis/audio_stream_ogg_vorbis.cpp
@@ -57,7 +57,8 @@ void AudioStreamPlaybackOGGVorbis::_mix_internal(AudioFrame *p_buffer, int p_fra
if (todo) {
//end of file!
- if (vorbis_stream->loop && mixed > 0) {
+ bool is_not_empty = mixed > 0 || stb_vorbis_stream_length_in_samples(ogg_stream) > 0;
+ if (vorbis_stream->loop && is_not_empty) {
//loop
seek(vorbis_stream->loop_offset);
loops++;
diff --git a/platform/android/SCsub b/platform/android/SCsub
index fd2a774c71..46f0703a65 100644
--- a/platform/android/SCsub
+++ b/platform/android/SCsub
@@ -18,6 +18,10 @@ android_files = [
'java_class_wrapper.cpp',
'java_godot_wrapper.cpp',
'java_godot_io_wrapper.cpp',
+ 'jni_utils.cpp',
+ 'android_keys_utils.cpp',
+ 'vulkan/vk_renderer_jni.cpp',
+ 'plugin/godot_plugin_jni.cpp'
]
env_android = env.Clone()
diff --git a/drivers/windows/semaphore_windows.h b/platform/android/android_keys_utils.cpp
index 159e8b3b96..88874ba2c7 100644
--- a/drivers/windows/semaphore_windows.h
+++ b/platform/android/android_keys_utils.cpp
@@ -1,5 +1,5 @@
/*************************************************************************/
-/* semaphore_windows.h */
+/* android_keys_utils.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
@@ -28,31 +28,16 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#ifndef SEMAPHORE_WINDOWS_H
-#define SEMAPHORE_WINDOWS_H
+#include "android_keys_utils.h"
-#include "core/os/semaphore.h"
+unsigned int android_get_keysym(unsigned int p_code) {
+ for (int i = 0; _ak_to_keycode[i].keysym != KEY_UNKNOWN; i++) {
-#ifdef WINDOWS_ENABLED
+ if (_ak_to_keycode[i].keycode == p_code) {
-#include <windows.h>
+ return _ak_to_keycode[i].keysym;
+ }
+ }
-class SemaphoreWindows : public SemaphoreOld {
-
- mutable HANDLE semaphore;
-
- static SemaphoreOld *create_semaphore_windows();
-
-public:
- virtual Error wait();
- virtual Error post();
- virtual int get() const;
-
- static void make_default();
- SemaphoreWindows();
-
- ~SemaphoreWindows();
-};
-
-#endif
-#endif
+ return KEY_UNKNOWN;
+}
diff --git a/platform/android/android_keys_utils.h b/platform/android/android_keys_utils.h
new file mode 100644
index 0000000000..f076688ac8
--- /dev/null
+++ b/platform/android/android_keys_utils.h
@@ -0,0 +1,280 @@
+/*************************************************************************/
+/* android_keys_utils.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2020 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. */
+/*************************************************************************/
+
+#ifndef ANDROID_KEYS_UTILS_H
+#define ANDROID_KEYS_UTILS_H
+
+#include <core/os/keyboard.h>
+
+/*
+ * Android Key codes.
+ */
+enum {
+ AKEYCODE_UNKNOWN = 0,
+ AKEYCODE_SOFT_LEFT = 1,
+ AKEYCODE_SOFT_RIGHT = 2,
+ AKEYCODE_HOME = 3,
+ AKEYCODE_BACK = 4,
+ AKEYCODE_CALL = 5,
+ AKEYCODE_ENDCALL = 6,
+ AKEYCODE_0 = 7,
+ AKEYCODE_1 = 8,
+ AKEYCODE_2 = 9,
+ AKEYCODE_3 = 10,
+ AKEYCODE_4 = 11,
+ AKEYCODE_5 = 12,
+ AKEYCODE_6 = 13,
+ AKEYCODE_7 = 14,
+ AKEYCODE_8 = 15,
+ AKEYCODE_9 = 16,
+ AKEYCODE_STAR = 17,
+ AKEYCODE_POUND = 18,
+ AKEYCODE_DPAD_UP = 19,
+ AKEYCODE_DPAD_DOWN = 20,
+ AKEYCODE_DPAD_LEFT = 21,
+ AKEYCODE_DPAD_RIGHT = 22,
+ AKEYCODE_DPAD_CENTER = 23,
+ AKEYCODE_VOLUME_UP = 24,
+ AKEYCODE_VOLUME_DOWN = 25,
+ AKEYCODE_POWER = 26,
+ AKEYCODE_CAMERA = 27,
+ AKEYCODE_CLEAR = 28,
+ AKEYCODE_A = 29,
+ AKEYCODE_B = 30,
+ AKEYCODE_C = 31,
+ AKEYCODE_D = 32,
+ AKEYCODE_E = 33,
+ AKEYCODE_F = 34,
+ AKEYCODE_G = 35,
+ AKEYCODE_H = 36,
+ AKEYCODE_I = 37,
+ AKEYCODE_J = 38,
+ AKEYCODE_K = 39,
+ AKEYCODE_L = 40,
+ AKEYCODE_M = 41,
+ AKEYCODE_N = 42,
+ AKEYCODE_O = 43,
+ AKEYCODE_P = 44,
+ AKEYCODE_Q = 45,
+ AKEYCODE_R = 46,
+ AKEYCODE_S = 47,
+ AKEYCODE_T = 48,
+ AKEYCODE_U = 49,
+ AKEYCODE_V = 50,
+ AKEYCODE_W = 51,
+ AKEYCODE_X = 52,
+ AKEYCODE_Y = 53,
+ AKEYCODE_Z = 54,
+ AKEYCODE_COMMA = 55,
+ AKEYCODE_PERIOD = 56,
+ AKEYCODE_ALT_LEFT = 57,
+ AKEYCODE_ALT_RIGHT = 58,
+ AKEYCODE_SHIFT_LEFT = 59,
+ AKEYCODE_SHIFT_RIGHT = 60,
+ AKEYCODE_TAB = 61,
+ AKEYCODE_SPACE = 62,
+ AKEYCODE_SYM = 63,
+ AKEYCODE_EXPLORER = 64,
+ AKEYCODE_ENVELOPE = 65,
+ AKEYCODE_ENTER = 66,
+ AKEYCODE_DEL = 67,
+ AKEYCODE_GRAVE = 68,
+ AKEYCODE_MINUS = 69,
+ AKEYCODE_EQUALS = 70,
+ AKEYCODE_LEFT_BRACKET = 71,
+ AKEYCODE_RIGHT_BRACKET = 72,
+ AKEYCODE_BACKSLASH = 73,
+ AKEYCODE_SEMICOLON = 74,
+ AKEYCODE_APOSTROPHE = 75,
+ AKEYCODE_SLASH = 76,
+ AKEYCODE_AT = 77,
+ AKEYCODE_NUM = 78,
+ AKEYCODE_HEADSETHOOK = 79,
+ AKEYCODE_FOCUS = 80, // *Camera* focus
+ AKEYCODE_PLUS = 81,
+ AKEYCODE_MENU = 82,
+ AKEYCODE_NOTIFICATION = 83,
+ AKEYCODE_SEARCH = 84,
+ AKEYCODE_MEDIA_PLAY_PAUSE = 85,
+ AKEYCODE_MEDIA_STOP = 86,
+ AKEYCODE_MEDIA_NEXT = 87,
+ AKEYCODE_MEDIA_PREVIOUS = 88,
+ AKEYCODE_MEDIA_REWIND = 89,
+ AKEYCODE_MEDIA_FAST_FORWARD = 90,
+ AKEYCODE_MUTE = 91,
+ AKEYCODE_PAGE_UP = 92,
+ AKEYCODE_PAGE_DOWN = 93,
+ AKEYCODE_PICTSYMBOLS = 94,
+ AKEYCODE_SWITCH_CHARSET = 95,
+ AKEYCODE_BUTTON_A = 96,
+ AKEYCODE_BUTTON_B = 97,
+ AKEYCODE_BUTTON_C = 98,
+ AKEYCODE_BUTTON_X = 99,
+ AKEYCODE_BUTTON_Y = 100,
+ AKEYCODE_BUTTON_Z = 101,
+ AKEYCODE_BUTTON_L1 = 102,
+ AKEYCODE_BUTTON_R1 = 103,
+ AKEYCODE_BUTTON_L2 = 104,
+ AKEYCODE_BUTTON_R2 = 105,
+ AKEYCODE_BUTTON_THUMBL = 106,
+ AKEYCODE_BUTTON_THUMBR = 107,
+ AKEYCODE_BUTTON_START = 108,
+ AKEYCODE_BUTTON_SELECT = 109,
+ AKEYCODE_BUTTON_MODE = 110,
+
+ // NOTE: If you add a new keycode here you must also add it to several other files.
+ // Refer to frameworks/base/core/java/android/view/KeyEvent.java for the full list.
+};
+
+struct _WinTranslatePair {
+
+ unsigned int keysym;
+ unsigned int keycode;
+};
+
+static _WinTranslatePair _ak_to_keycode[] = {
+ { KEY_TAB, AKEYCODE_TAB },
+ { KEY_ENTER, AKEYCODE_ENTER },
+ { KEY_SHIFT, AKEYCODE_SHIFT_LEFT },
+ { KEY_SHIFT, AKEYCODE_SHIFT_RIGHT },
+ { KEY_ALT, AKEYCODE_ALT_LEFT },
+ { KEY_ALT, AKEYCODE_ALT_RIGHT },
+ { KEY_MENU, AKEYCODE_MENU },
+ { KEY_PAUSE, AKEYCODE_MEDIA_PLAY_PAUSE },
+ { KEY_ESCAPE, AKEYCODE_BACK },
+ { KEY_SPACE, AKEYCODE_SPACE },
+ { KEY_PAGEUP, AKEYCODE_PAGE_UP },
+ { KEY_PAGEDOWN, AKEYCODE_PAGE_DOWN },
+ { KEY_HOME, AKEYCODE_HOME }, //(0x24)
+ { KEY_LEFT, AKEYCODE_DPAD_LEFT },
+ { KEY_UP, AKEYCODE_DPAD_UP },
+ { KEY_RIGHT, AKEYCODE_DPAD_RIGHT },
+ { KEY_DOWN, AKEYCODE_DPAD_DOWN },
+ { KEY_PERIODCENTERED, AKEYCODE_DPAD_CENTER },
+ { KEY_BACKSPACE, AKEYCODE_DEL },
+ { KEY_0, AKEYCODE_0 }, ////0 key
+ { KEY_1, AKEYCODE_1 }, ////1 key
+ { KEY_2, AKEYCODE_2 }, ////2 key
+ { KEY_3, AKEYCODE_3 }, ////3 key
+ { KEY_4, AKEYCODE_4 }, ////4 key
+ { KEY_5, AKEYCODE_5 }, ////5 key
+ { KEY_6, AKEYCODE_6 }, ////6 key
+ { KEY_7, AKEYCODE_7 }, ////7 key
+ { KEY_8, AKEYCODE_8 }, ////8 key
+ { KEY_9, AKEYCODE_9 }, ////9 key
+ { KEY_A, AKEYCODE_A }, ////A key
+ { KEY_B, AKEYCODE_B }, ////B key
+ { KEY_C, AKEYCODE_C }, ////C key
+ { KEY_D, AKEYCODE_D }, ////D key
+ { KEY_E, AKEYCODE_E }, ////E key
+ { KEY_F, AKEYCODE_F }, ////F key
+ { KEY_G, AKEYCODE_G }, ////G key
+ { KEY_H, AKEYCODE_H }, ////H key
+ { KEY_I, AKEYCODE_I }, ////I key
+ { KEY_J, AKEYCODE_J }, ////J key
+ { KEY_K, AKEYCODE_K }, ////K key
+ { KEY_L, AKEYCODE_L }, ////L key
+ { KEY_M, AKEYCODE_M }, ////M key
+ { KEY_N, AKEYCODE_N }, ////N key
+ { KEY_O, AKEYCODE_O }, ////O key
+ { KEY_P, AKEYCODE_P }, ////P key
+ { KEY_Q, AKEYCODE_Q }, ////Q key
+ { KEY_R, AKEYCODE_R }, ////R key
+ { KEY_S, AKEYCODE_S }, ////S key
+ { KEY_T, AKEYCODE_T }, ////T key
+ { KEY_U, AKEYCODE_U }, ////U key
+ { KEY_V, AKEYCODE_V }, ////V key
+ { KEY_W, AKEYCODE_W }, ////W key
+ { KEY_X, AKEYCODE_X }, ////X key
+ { KEY_Y, AKEYCODE_Y }, ////Y key
+ { KEY_Z, AKEYCODE_Z }, ////Z key
+ { KEY_HOMEPAGE, AKEYCODE_EXPLORER },
+ { KEY_LAUNCH0, AKEYCODE_BUTTON_A },
+ { KEY_LAUNCH1, AKEYCODE_BUTTON_B },
+ { KEY_LAUNCH2, AKEYCODE_BUTTON_C },
+ { KEY_LAUNCH3, AKEYCODE_BUTTON_X },
+ { KEY_LAUNCH4, AKEYCODE_BUTTON_Y },
+ { KEY_LAUNCH5, AKEYCODE_BUTTON_Z },
+ { KEY_LAUNCH6, AKEYCODE_BUTTON_L1 },
+ { KEY_LAUNCH7, AKEYCODE_BUTTON_R1 },
+ { KEY_LAUNCH8, AKEYCODE_BUTTON_L2 },
+ { KEY_LAUNCH9, AKEYCODE_BUTTON_R2 },
+ { KEY_LAUNCHA, AKEYCODE_BUTTON_THUMBL },
+ { KEY_LAUNCHB, AKEYCODE_BUTTON_THUMBR },
+ { KEY_LAUNCHC, AKEYCODE_BUTTON_START },
+ { KEY_LAUNCHD, AKEYCODE_BUTTON_SELECT },
+ { KEY_LAUNCHE, AKEYCODE_BUTTON_MODE },
+ { KEY_VOLUMEMUTE, AKEYCODE_MUTE },
+ { KEY_VOLUMEDOWN, AKEYCODE_VOLUME_DOWN },
+ { KEY_VOLUMEUP, AKEYCODE_VOLUME_UP },
+ { KEY_BACK, AKEYCODE_MEDIA_REWIND },
+ { KEY_FORWARD, AKEYCODE_MEDIA_FAST_FORWARD },
+ { KEY_MEDIANEXT, AKEYCODE_MEDIA_NEXT },
+ { KEY_MEDIAPREVIOUS, AKEYCODE_MEDIA_PREVIOUS },
+ { KEY_MEDIASTOP, AKEYCODE_MEDIA_STOP },
+ { KEY_PLUS, AKEYCODE_PLUS },
+ { KEY_EQUAL, AKEYCODE_EQUALS }, // the '+' key
+ { KEY_COMMA, AKEYCODE_COMMA }, // the ',' key
+ { KEY_MINUS, AKEYCODE_MINUS }, // the '-' key
+ { KEY_SLASH, AKEYCODE_SLASH }, // the '/?' key
+ { KEY_BACKSLASH, AKEYCODE_BACKSLASH },
+ { KEY_BRACKETLEFT, AKEYCODE_LEFT_BRACKET },
+ { KEY_BRACKETRIGHT, AKEYCODE_RIGHT_BRACKET },
+ { KEY_UNKNOWN, 0 }
+};
+/*
+TODO: map these android key:
+ AKEYCODE_SOFT_LEFT = 1,
+ AKEYCODE_SOFT_RIGHT = 2,
+ AKEYCODE_CALL = 5,
+ AKEYCODE_ENDCALL = 6,
+ AKEYCODE_STAR = 17,
+ AKEYCODE_POUND = 18,
+ AKEYCODE_POWER = 26,
+ AKEYCODE_CAMERA = 27,
+ AKEYCODE_CLEAR = 28,
+ AKEYCODE_SYM = 63,
+ AKEYCODE_ENVELOPE = 65,
+ AKEYCODE_GRAVE = 68,
+ AKEYCODE_SEMICOLON = 74,
+ AKEYCODE_APOSTROPHE = 75,
+ AKEYCODE_AT = 77,
+ AKEYCODE_NUM = 78,
+ AKEYCODE_HEADSETHOOK = 79,
+ AKEYCODE_FOCUS = 80, // *Camera* focus
+ AKEYCODE_NOTIFICATION = 83,
+ AKEYCODE_SEARCH = 84,
+ AKEYCODE_PICTSYMBOLS = 94,
+ AKEYCODE_SWITCH_CHARSET = 95,
+*/
+
+unsigned int android_get_keysym(unsigned int p_code);
+
+#endif // ANDROID_KEYS_UTILS_H
diff --git a/platform/android/audio_driver_opensl.cpp b/platform/android/audio_driver_opensl.cpp
index 307bc3a169..222120f81f 100644
--- a/platform/android/audio_driver_opensl.cpp
+++ b/platform/android/audio_driver_opensl.cpp
@@ -328,13 +328,13 @@ AudioDriver::SpeakerMode AudioDriverOpenSL::get_speaker_mode() const {
void AudioDriverOpenSL::lock() {
- if (active && mutex)
+ if (active)
mutex.lock();
}
void AudioDriverOpenSL::unlock() {
- if (active && mutex)
+ if (active)
mutex.unlock();
}
diff --git a/platform/android/detect.py b/platform/android/detect.py
index 8b62360888..8f74ae0ef0 100644
--- a/platform/android/detect.py
+++ b/platform/android/detect.py
@@ -205,7 +205,6 @@ def configure(env):
env.Append(CPPFLAGS=["-isystem", env["ANDROID_NDK_ROOT"] + "/sources/cxx-stl/llvm-libc++/include"])
env.Append(CPPFLAGS=["-isystem", env["ANDROID_NDK_ROOT"] + "/sources/cxx-stl/llvm-libc++abi/include"])
- env.Append(CXXFLAGS=["-std=gnu++14"])
# Disable exceptions and rtti on non-tools (template) builds
if env['tools']:
diff --git a/platform/android/dir_access_jandroid.cpp b/platform/android/dir_access_jandroid.cpp
index f52b511522..ebcac884db 100644
--- a/platform/android/dir_access_jandroid.cpp
+++ b/platform/android/dir_access_jandroid.cpp
@@ -144,7 +144,7 @@ Error DirAccessJAndroid::change_dir(String p_dir) {
return OK;
}
-String DirAccessJAndroid::get_current_dir() {
+String DirAccessJAndroid::get_current_dir(bool p_include_drive) {
return "res://" + current_dir;
}
diff --git a/platform/android/dir_access_jandroid.h b/platform/android/dir_access_jandroid.h
index caeb4b58b9..8dab3e50ce 100644
--- a/platform/android/dir_access_jandroid.h
+++ b/platform/android/dir_access_jandroid.h
@@ -65,7 +65,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(); ///< return current dir location
+ virtual String get_current_dir(bool p_include_drive = true); ///< return current dir location
virtual bool file_exists(String p_file);
virtual bool dir_exists(String p_dir);
diff --git a/platform/android/export/export.cpp b/platform/android/export/export.cpp
index c618177475..e7d4bc6c51 100644
--- a/platform/android/export/export.cpp
+++ b/platform/android/export/export.cpp
@@ -688,6 +688,8 @@ class EditorExportPlatformAndroid : public EditorExportPlatform {
int xr_mode_index = p_preset->get("xr_features/xr_mode");
+ String plugins = p_preset->get("custom_template/plugins");
+
Vector<String> perms;
const char **aperms = android_perms;
@@ -851,6 +853,11 @@ class EditorExportPlatformAndroid : public EditorExportPlatform {
}
}
+ if (tname == "meta-data" && attrname == "value" && value == "custom_template_plugins_value") {
+ // Update the meta-data 'android:value' attribute with the list of enabled plugins.
+ string_table.write[attr_value] = plugins;
+ }
+
iofs += 20;
}
@@ -1363,6 +1370,7 @@ public:
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "custom_template/debug", PROPERTY_HINT_GLOBAL_FILE, "*.apk"), ""));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "custom_template/release", PROPERTY_HINT_GLOBAL_FILE, "*.apk"), ""));
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "custom_template/use_custom_build"), false));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "custom_template/plugins", PROPERTY_HINT_PLACEHOLDER_TEXT, "Plugin1,Plugin2,..."), ""));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "command_line/extra_args"), ""));
r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "version/code", PROPERTY_HINT_RANGE, "1,4096,1,or_greater"), 1));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "version/name"), "1.0"));
@@ -1743,260 +1751,6 @@ public:
return list;
}
- void _update_custom_build_project() {
-
- DirAccessRef da = DirAccess::open("res://android");
-
- ERR_FAIL_COND_MSG(!da, "Cannot open directory 'res://android'.");
- Map<String, List<String> > directory_paths;
- Map<String, List<String> > manifest_sections;
- Map<String, List<String> > gradle_sections;
- da->list_dir_begin();
- String d = da->get_next();
- while (d != String()) {
-
- if (!d.begins_with(".") && d != "build" && da->current_is_dir()) { //a dir and not the build dir
- //add directories found
- DirAccessRef ds = DirAccess::open(String("res://android").plus_file(d));
- if (ds) {
- ds->list_dir_begin();
- String sd = ds->get_next();
- while (sd != String()) {
-
- if (!sd.begins_with(".") && ds->current_is_dir()) {
- String key = sd.to_upper();
- if (!directory_paths.has(key)) {
- directory_paths[key] = List<String>();
- }
- String path = ProjectSettings::get_singleton()->get_resource_path().plus_file("android").plus_file(d).plus_file(sd);
- directory_paths[key].push_back(path);
- print_line("Add: " + sd + ":" + path);
- }
-
- sd = ds->get_next();
- }
- ds->list_dir_end();
- }
- //parse manifest
- {
- FileAccessRef f = FileAccess::open(String("res://android").plus_file(d).plus_file("AndroidManifest.conf"), FileAccess::READ);
- if (f) {
-
- String section;
- while (!f->eof_reached()) {
- String l = f->get_line();
- String k = l.strip_edges();
- if (k.begins_with("[")) {
- section = k.substr(1, k.length() - 2).strip_edges().to_upper();
- print_line("Section: " + section);
- } else if (k != String()) {
- if (!manifest_sections.has(section)) {
- manifest_sections[section] = List<String>();
- }
- manifest_sections[section].push_back(l);
- }
- }
-
- f->close();
- }
- }
- //parse gradle
- {
- FileAccessRef f = FileAccess::open(String("res://android").plus_file(d).plus_file("gradle.conf"), FileAccess::READ);
- if (f) {
-
- String section;
- while (!f->eof_reached()) {
- String l = f->get_line().strip_edges();
- String k = l.strip_edges();
- if (k.begins_with("[")) {
- section = k.substr(1, k.length() - 2).strip_edges().to_upper();
- print_line("Section: " + section);
- } else if (k != String()) {
- if (!gradle_sections.has(section)) {
- gradle_sections[section] = List<String>();
- }
- gradle_sections[section].push_back(l);
- }
- }
- }
- }
- }
- d = da->get_next();
- }
- da->list_dir_end();
-
- { //fix gradle build
-
- String new_file;
- {
- FileAccessRef f = FileAccess::open("res://android/build/build.gradle", FileAccess::READ);
- if (f) {
-
- while (!f->eof_reached()) {
- String l = f->get_line();
-
- if (l.begins_with("//CHUNK_")) {
- String text = l.replace_first("//CHUNK_", "");
- int begin_pos = text.find("_BEGIN");
- if (begin_pos != -1) {
- text = text.substr(0, begin_pos);
- text = text.to_upper(); //just in case
-
- String end_marker = "//CHUNK_" + text + "_END";
- size_t pos = f->get_position();
- bool found = false;
- while (!f->eof_reached()) {
- l = f->get_line();
- if (l.begins_with(end_marker)) {
- found = true;
- break;
- }
- }
-
- new_file += "//CHUNK_" + text + "_BEGIN\n";
-
- if (!found) {
- ERR_PRINT("No end marker found in build.gradle for chunk: " + text);
- f->seek(pos);
- } else {
-
- //add chunk lines
- if (gradle_sections.has(text)) {
- for (List<String>::Element *E = gradle_sections[text].front(); E; E = E->next()) {
- new_file += E->get() + "\n";
- }
- }
- new_file += end_marker + "\n";
- }
- } else {
- new_file += l + "\n"; //pass line by
- }
- } else if (l.begins_with("//DIR_")) {
- String text = l.replace_first("//DIR_", "");
- int begin_pos = text.find("_BEGIN");
- if (begin_pos != -1) {
- text = text.substr(0, begin_pos);
- text = text.to_upper(); //just in case
-
- String end_marker = "//DIR_" + text + "_END";
- size_t pos = f->get_position();
- bool found = false;
- while (!f->eof_reached()) {
- l = f->get_line();
- if (l.begins_with(end_marker)) {
- found = true;
- break;
- }
- }
-
- new_file += "//DIR_" + text + "_BEGIN\n";
-
- if (!found) {
- ERR_PRINT("No end marker found in build.gradle for dir: " + text);
- f->seek(pos);
- } else {
- //add chunk lines
- if (directory_paths.has(text)) {
- for (List<String>::Element *E = directory_paths[text].front(); E; E = E->next()) {
- new_file += ",'" + E->get().replace("'", "\'") + "'";
- new_file += "\n";
- }
- }
- new_file += end_marker + "\n";
- }
- } else {
- new_file += l + "\n"; //pass line by
- }
-
- } else {
- new_file += l + "\n";
- }
- }
- }
- }
-
- FileAccessRef f = FileAccess::open("res://android/build/build.gradle", FileAccess::WRITE);
- f->store_string(new_file);
- f->close();
- }
-
- { //fix manifest
-
- String new_file;
- {
- FileAccessRef f = FileAccess::open("res://android/build/AndroidManifest.xml", FileAccess::READ);
- if (f) {
-
- while (!f->eof_reached()) {
- String l = f->get_line();
-
- if (l.begins_with("<!--CHUNK_")) {
- String text = l.replace_first("<!--CHUNK_", "");
- int begin_pos = text.find("_BEGIN-->");
- if (begin_pos != -1) {
- text = text.substr(0, begin_pos);
- text = text.to_upper(); //just in case
-
- String end_marker = "<!--CHUNK_" + text + "_END-->";
- size_t pos = f->get_position();
- bool found = false;
- while (!f->eof_reached()) {
- l = f->get_line();
- if (l.begins_with(end_marker)) {
- found = true;
- break;
- }
- }
-
- new_file += "<!--CHUNK_" + text + "_BEGIN-->\n";
-
- if (!found) {
- ERR_PRINT("No end marker found in AndroidManifest.xml for chunk: " + text);
- f->seek(pos);
- } else {
- //add chunk lines
- if (manifest_sections.has(text)) {
- for (List<String>::Element *E = manifest_sections[text].front(); E; E = E->next()) {
- new_file += E->get() + "\n";
- }
- }
- new_file += end_marker + "\n";
- }
- } else {
- new_file += l + "\n"; //pass line by
- }
-
- } else if (l.strip_edges().begins_with("<application")) {
- String last_tag = "android:icon=\"@mipmap/icon\"";
- int last_tag_pos = l.find(last_tag);
- if (last_tag_pos == -1) {
- ERR_PRINT("Not adding application attributes as the expected tag was not found in '<application': " + last_tag);
- new_file += l + "\n";
- } else {
- String base = l.substr(0, last_tag_pos + last_tag.length());
- if (manifest_sections.has("application_attribs")) {
- for (List<String>::Element *E = manifest_sections["application_attribs"].front(); E; E = E->next()) {
- String to_add = E->get().strip_edges();
- base += " " + to_add + " ";
- }
- }
- base += ">\n";
- new_file += base;
- }
- } else {
- new_file += l + "\n";
- }
- }
- }
- }
-
- FileAccessRef f = FileAccess::open("res://android/build/AndroidManifest.xml", FileAccess::WRITE);
- f->store_string(new_file);
- f->close();
- }
- }
-
virtual Error export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags = 0) {
ExportNotifier notifier(*this, p_preset, p_debug, p_path, p_flags);
@@ -2025,8 +1779,6 @@ public:
ERR_FAIL_COND_V_MSG(sdk_path == "", ERR_UNCONFIGURED, "Android SDK path must be configured in Editor Settings at 'export/android/custom_build_sdk_path'.");
- _update_custom_build_project();
-
OS::get_singleton()->set_environment("ANDROID_HOME", sdk_path); //set and overwrite if required
String build_command;
@@ -2037,14 +1789,18 @@ public:
#endif
String build_path = ProjectSettings::get_singleton()->get_resource_path().plus_file("android/build");
+ String plugins_dir = ProjectSettings::get_singleton()->get_resource_path().plus_file("android/plugins");
build_command = build_path.plus_file(build_command);
String package_name = get_package_name(p_preset->get("package/unique_name"));
+ String plugins = p_preset->get("custom_template/plugins");
List<String> cmdline;
cmdline.push_back("build");
cmdline.push_back("-Pexport_package_name=" + package_name); // argument to specify the package name.
+ cmdline.push_back("-Pcustom_template_plugins_dir=" + plugins_dir); // argument to specify the plugins directory.
+ cmdline.push_back("-Pcustom_template_plugins=" + plugins); // argument to specify the list of plugins to enable.
cmdline.push_back("-p"); // argument to specify the start directory.
cmdline.push_back(build_path); // start directory.
/*{ used for debug
diff --git a/platform/android/java/app/AndroidManifest.xml b/platform/android/java/app/AndroidManifest.xml
index 4d2eb1ef65..cc480d1c84 100644
--- a/platform/android/java/app/AndroidManifest.xml
+++ b/platform/android/java/app/AndroidManifest.xml
@@ -6,9 +6,6 @@
android:versionName="1.0"
android:installLocation="auto" >
- <!-- Adding custom text to the manifest is fine, but do it outside the custom USER and APPLICATION BEGIN/END comments, -->
- <!-- as that gets rewritten. -->
-
<supports-screens
android:smallScreens="true"
android:normalScreens="true"
@@ -19,14 +16,11 @@
android:glEsVersion="0x00020000"
android:required="true" />
-<!-- Custom user permissions XML added by add-ons. It's recommended to add them from the export preset, though. -->
-<!--CHUNK_USER_PERMISSIONS_BEGIN-->
-<!--CHUNK_USER_PERMISSIONS_END-->
-
- <!-- Any tag in this line after android:icon will be erased when doing custom builds. -->
- <!-- If you want to add tags manually, do before it. -->
- <!-- WARNING: This should stay on a single line until the parsing code is improved. See GH-32414. -->
- <application android:label="@string/godot_project_name_string" android:allowBackup="false" tools:ignore="GoogleAppIndexingWarning" android:icon="@mipmap/icon" >
+ <application
+ android:label="@string/godot_project_name_string"
+ android:allowBackup="false"
+ tools:ignore="GoogleAppIndexingWarning"
+ android:icon="@mipmap/icon" >
<!-- The following metadata values are replaced when Godot exports, modifying them here has no effect. -->
<!-- Do these changes in the export preset. Adding new ones is fine. -->
@@ -36,6 +30,11 @@
android:name="xr_mode_metadata_name"
android:value="xr_mode_metadata_value" />
+ <!-- Metadata populated at export time and used by Godot to figure out which plugins must be enabled. -->
+ <meta-data
+ android:name="custom_template_plugins"
+ android:value="custom_template_plugins_value"/>
+
<activity
android:name=".GodotApp"
android:label="@string/godot_project_name_string"
@@ -52,10 +51,6 @@
</intent-filter>
</activity>
-<!-- Custom application XML added by add-ons. -->
-<!--CHUNK_APPLICATION_BEGIN-->
-<!--CHUNK_APPLICATION_END-->
-
</application>
</manifest>
diff --git a/platform/android/java/app/build.gradle b/platform/android/java/app/build.gradle
index 2e4f2ffab0..5e37f538e9 100644
--- a/platform/android/java/app/build.gradle
+++ b/platform/android/java/app/build.gradle
@@ -1,7 +1,4 @@
// Gradle build config for Godot Engine's Android port.
-//
-// Do not remove/modify comments ending with BEGIN/END, they are used to inject
-// addon-specific configuration.
apply from: 'config.gradle'
buildscript {
@@ -10,13 +7,10 @@ buildscript {
repositories {
google()
jcenter()
-//CHUNK_BUILDSCRIPT_REPOSITORIES_BEGIN
-//CHUNK_BUILDSCRIPT_REPOSITORIES_END
}
dependencies {
classpath libraries.androidGradlePlugin
-//CHUNK_BUILDSCRIPT_DEPENDENCIES_BEGIN
-//CHUNK_BUILDSCRIPT_DEPENDENCIES_END
+ classpath libraries.kotlinGradlePlugin
}
}
@@ -27,24 +21,35 @@ allprojects {
mavenCentral()
google()
jcenter()
-//CHUNK_ALLPROJECTS_REPOSITORIES_BEGIN
-//CHUNK_ALLPROJECTS_REPOSITORIES_END
}
}
dependencies {
implementation libraries.supportCoreUtils
+ implementation libraries.kotlinStdLib
+ implementation libraries.v4Support
if (rootProject.findProject(":lib")) {
implementation project(":lib")
+ } else if (rootProject.findProject(":godot:lib")) {
+ implementation project(":godot:lib")
} else {
// 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'])
releaseImplementation fileTree(dir: 'libs/release', include: ['*.jar', '*.aar'])
}
-//CHUNK_DEPENDENCIES_BEGIN
-//CHUNK_DEPENDENCIES_END
+
+ // Godot prebuilt plugins
+ implementation fileTree(dir: 'libs/plugins', include: ["GodotPayment*.aar"])
+
+ // Godot user plugins dependencies
+ String pluginsDir = getGodotPluginsDirectory()
+ String[] pluginsBinaries = getGodotPluginsBinaries()
+ if (pluginsDir != null && !pluginsDir.isEmpty() &&
+ pluginsBinaries != null && pluginsBinaries.size() > 0) {
+ implementation fileTree(dir: pluginsDir, include: pluginsBinaries)
+ }
}
android {
@@ -56,8 +61,6 @@ android {
applicationId getExportPackageName()
minSdkVersion versions.minSdk
targetSdkVersion versions.targetSdk
-//CHUNK_ANDROID_DEFAULTCONFIG_BEGIN
-//CHUNK_ANDROID_DEFAULTCONFIG_END
}
lintOptions {
@@ -79,37 +82,13 @@ android {
sourceSets {
main {
manifest.srcFile 'AndroidManifest.xml'
- java.srcDirs = [
- 'src'
-//DIR_SRC_BEGIN
-//DIR_SRC_END
- ]
- res.srcDirs = [
- 'res'
-//DIR_RES_BEGIN
-//DIR_RES_END
- ]
- aidl.srcDirs = [
- 'aidl'
-//DIR_AIDL_BEGIN
-//DIR_AIDL_END
- ]
- assets.srcDirs = [
- 'assets'
-//DIR_ASSETS_BEGIN
-//DIR_ASSETS_END
- ]
+ java.srcDirs = ['src']
+ res.srcDirs = ['res']
+ aidl.srcDirs = ['aidl']
+ assets.srcDirs = ['assets']
}
- debug.jniLibs.srcDirs = [
- 'libs/debug'
-//DIR_JNI_DEBUG_BEGIN
-//DIR_JNI_DEBUG_END
- ]
- release.jniLibs.srcDirs = [
- 'libs/release'
-//DIR_JNI_RELEASE_BEGIN
-//DIR_JNI_RELEASE_END
- ]
+ debug.jniLibs.srcDirs = ['libs/debug']
+ release.jniLibs.srcDirs = ['libs/release']
}
applicationVariants.all { variant ->
@@ -118,6 +97,3 @@ android {
}
}
}
-
-//CHUNK_GLOBAL_BEGIN
-//CHUNK_GLOBAL_END
diff --git a/platform/android/java/app/config.gradle b/platform/android/java/app/config.gradle
index 5550d3099d..3fb26326c0 100644
--- a/platform/android/java/app/config.gradle
+++ b/platform/android/java/app/config.gradle
@@ -1,24 +1,66 @@
ext.versions = [
- androidGradlePlugin : '3.4.2',
- compileSdk : 28,
- minSdk : 18,
- targetSdk : 28,
- buildTools : '28.0.3',
- supportCoreUtils : '28.0.0'
+ androidGradlePlugin: '3.6.0',
+ compileSdk : 29,
+ minSdk : 18,
+ targetSdk : 29,
+ buildTools : '29.0.1',
+ supportCoreUtils : '28.0.0',
+ kotlinVersion : '1.3.61',
+ v4Support : '28.0.0'
]
ext.libraries = [
- androidGradlePlugin : "com.android.tools.build:gradle:$versions.androidGradlePlugin",
- supportCoreUtils : "com.android.support:support-core-utils:$versions.supportCoreUtils"
+ androidGradlePlugin: "com.android.tools.build:gradle:$versions.androidGradlePlugin",
+ supportCoreUtils : "com.android.support:support-core-utils:$versions.supportCoreUtils",
+ kotlinGradlePlugin : "org.jetbrains.kotlin:kotlin-gradle-plugin:$versions.kotlinVersion",
+ kotlinStdLib : "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$versions.kotlinVersion",
+ v4Support : "com.android.support:support-v4:$versions.v4Support"
]
ext.getExportPackageName = { ->
- // Retrieve the app id from the project property set by the Godot build command.
- String appId = project.hasProperty("export_package_name") ? project.property("export_package_name") : ""
- // Check if the app id is valid, otherwise use the default.
- if (appId == null || appId.isEmpty()) {
- appId = "com.godot.game"
- }
- return appId
+ // Retrieve the app id from the project property set by the Godot build command.
+ String appId = project.hasProperty("export_package_name") ? project.property("export_package_name") : ""
+ // Check if the app id is valid, otherwise use the default.
+ if (appId == null || appId.isEmpty()) {
+ appId = "com.godot.game"
+ }
+ return appId
+}
+
+/**
+ * Parse the project properties for the 'custom_template_plugins' property and return
+ * their binaries for inclusion in the build dependencies.
+ *
+ * The listed plugins must have their binaries in the project plugins directory.
+ */
+ext.getGodotPluginsBinaries = { ->
+ String[] binDeps = []
+
+ // Retrieve the list of enabled plugins.
+ if (project.hasProperty("custom_template_plugins")) {
+ String pluginsList = project.property("custom_template_plugins")
+ if (pluginsList != null && !pluginsList.trim().isEmpty()) {
+ for (String plugin : pluginsList.split(",")) {
+ binDeps += plugin + "*.aar"
+ }
+ }
+ }
+
+ return binDeps
+}
+
+/**
+ * Parse the project properties for the 'custom_template_plugins_dir' property and return
+ * its value.
+ *
+ * The returned value is the directory containing user plugins.
+ */
+ext.getGodotPluginsDirectory = { ->
+ // The plugins directory is provided by the 'custom_template_plugins_dir' property.
+ String pluginsDir = project.hasProperty("custom_template_plugins_dir")
+ ? project.property("custom_template_plugins_dir")
+ : ""
+
+ return pluginsDir
}
diff --git a/platform/android/java/build.gradle b/platform/android/java/build.gradle
index 2052017888..976a5bda99 100644
--- a/platform/android/java/build.gradle
+++ b/platform/android/java/build.gradle
@@ -9,6 +9,7 @@ buildscript {
}
dependencies {
classpath libraries.androidGradlePlugin
+ classpath libraries.kotlinGradlePlugin
}
}
@@ -24,7 +25,7 @@ ext {
sconsExt = org.gradle.internal.os.OperatingSystem.current().isWindows() ? ".bat" : ""
supportedAbis = ["armv7", "arm64v8", "x86", "x86_64"]
- supportedTargets = ['release':"release", 'debug':"release_debug"]
+ supportedTargets = ['release': "release", 'debug': "release_debug"]
// Used by gradle to specify which architecture to build for by default when running `./gradlew build`.
// This command is usually used by Android Studio.
@@ -64,10 +65,10 @@ task copyReleaseBinaryToBin(type: Copy) {
}
/**
- * Copy the Godot android library archive debug file into the app debug libs directory.
+ * Copy the Godot android library archive debug file into the app module debug libs directory.
* Depends on the library build task to ensure the AAR file is generated prior to copying.
*/
-task copyDebugAAR(type: Copy) {
+task copyDebugAARToAppModule(type: Copy) {
dependsOn ':lib:assembleDebug'
from('lib/build/outputs/aar')
into('app/libs/debug')
@@ -75,16 +76,45 @@ task copyDebugAAR(type: Copy) {
}
/**
- * Copy the Godot android library archive release file into the app release libs directory.
+ * Copy the Godot android library archive debug file into the root bin directory.
* Depends on the library build task to ensure the AAR file is generated prior to copying.
*/
-task copyReleaseAAR(type: Copy) {
+task copyDebugAARToBin(type: Copy) {
+ dependsOn ':lib:assembleDebug'
+ from('lib/build/outputs/aar')
+ into(binDir)
+ include('godot-lib.debug.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'
from('lib/build/outputs/aar')
into('app/libs/release')
include('godot-lib.release.aar')
}
+task copyGodotPaymentPluginToAppModule(type: Copy) {
+ dependsOn ':plugins:godotpayment:assembleRelease'
+ from('plugins/godotpayment/build/outputs/aar')
+ into('app/libs/plugins')
+ include('GodotPayment.release.aar')
+}
+
+/**
+ * Copy the Godot android library archive release file into the root bin directory.
+ * Depends on the library build task to ensure the AAR file is generated prior to copying.
+ */
+task copyReleaseAARToBin(type: Copy) {
+ dependsOn ':lib:assembleRelease'
+ from('lib/build/outputs/aar')
+ into(binDir)
+ include('godot-lib.release.aar')
+}
+
/**
* 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'.
@@ -95,7 +125,7 @@ task zipCustomBuild(type: Zip) {
doFirst {
logger.lifecycle("Generating Godot custom build template")
}
- from(fileTree(dir: 'app', excludes: ['**/build/**', '**/.gradle/**', '**/*.iml']), fileTree(dir: '.', includes: ['gradle.properties','gradlew', 'gradlew.bat', 'gradle/**']))
+ from(fileTree(dir: 'app', excludes: ['**/build/**', '**/.gradle/**', '**/*.iml']), fileTree(dir: '.', includes: ['gradle.properties', 'gradlew', 'gradlew.bat', 'gradle/**']))
include '**/*'
archiveName 'android_source.zip'
destinationDir(file(binDir))
@@ -110,19 +140,24 @@ task generateGodotTemplates(type: GradleBuild) {
startParameter.excludedTaskNames += ":lib:" + getSconsTaskName(buildType)
}
- tasks = []
+ tasks = ["copyGodotPaymentPluginToAppModule"]
// Only build the apks and aar files for which we have native shared libraries.
for (String target : supportedTargets.keySet()) {
File targetLibs = new File("lib/libs/" + target)
- if (targetLibs != null && targetLibs.isDirectory()) {
- File[] targetLibsContents = targetLibs.listFiles()
- if (targetLibsContents != null && targetLibsContents.length > 0) {
- // Copy the generated aar library files to the custom build directory.
- tasks += "copy" + target.capitalize() + "AAR"
- // Copy the prebuilt binary templates to the bin directory.
- tasks += "copy" + target.capitalize() + "BinaryToBin"
- }
+ if (targetLibs != null
+ && targetLibs.isDirectory()
+ && targetLibs.listFiles() != null
+ && targetLibs.listFiles().length > 0) {
+ String capitalizedTarget = target.capitalize()
+ // Copy the generated aar library files to the custom build directory.
+ tasks += "copy" + capitalizedTarget + "AARToAppModule"
+ // Copy the generated aar library files to the bin directory.
+ tasks += "copy" + capitalizedTarget + "AARToBin"
+ // Copy the prebuilt binary templates to the bin directory.
+ tasks += "copy" + capitalizedTarget + "BinaryToBin"
+ } else {
+ logger.lifecycle("No native shared libs for target $target. Skipping build.")
}
}
@@ -133,20 +168,28 @@ task generateGodotTemplates(type: GradleBuild) {
* Clean the generated artifacts.
*/
task cleanGodotTemplates(type: Delete) {
- // Delete the generated native libs
- delete("lib/libs")
+ // Delete the generated native libs
+ delete("lib/libs")
+
+ // Delete the library generated AAR files
+ delete("lib/build/outputs/aar")
+
+ // Delete the godotpayment libs directory contents
+ delete("plugins/godotpayment/libs")
- // Delete the library generated AAR files
- delete("lib/build/outputs/aar")
+ // Delete the generated godotpayment aar
+ delete("plugins/godotpayment/build/outputs/aar")
- // Delete the app libs directory contents
- delete("app/libs")
+ // Delete the app libs directory contents
+ delete("app/libs")
- // Delete the generated binary apks
- delete("app/build/outputs/apk")
+ // Delete the generated binary apks
+ delete("app/build/outputs/apk")
- // Delete the Godot templates in the Godot bin directory
- delete("$binDir/android_debug.apk")
- delete("$binDir/android_release.apk")
- delete("$binDir/android_source.zip")
+ // Delete the Godot templates in the Godot bin directory
+ delete("$binDir/android_debug.apk")
+ delete("$binDir/android_release.apk")
+ delete("$binDir/android_source.zip")
+ delete("$binDir/godot-lib.debug.aar")
+ delete("$binDir/godot-lib.release.aar")
}
diff --git a/platform/android/java/gradle/wrapper/gradle-wrapper.properties b/platform/android/java/gradle/wrapper/gradle-wrapper.properties
index bf50265715..f56b0f6a5e 100644
--- a/platform/android/java/gradle/wrapper/gradle-wrapper.properties
+++ b/platform/android/java/gradle/wrapper/gradle-wrapper.properties
@@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-5.1.1-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.4-all.zip
diff --git a/platform/android/java/lib/build.gradle b/platform/android/java/lib/build.gradle
index eb97484b9c..ca8aaf8af0 100644
--- a/platform/android/java/lib/build.gradle
+++ b/platform/android/java/lib/build.gradle
@@ -2,6 +2,8 @@ apply plugin: 'com.android.library'
dependencies {
implementation libraries.supportCoreUtils
+ implementation libraries.kotlinStdLib
+ implementation libraries.v4Support
}
def pathToRootDir = "../../../../"
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 ac1032dab6..7db4aa6597 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/Godot.java
+++ b/platform/android/java/lib/src/org/godotengine/godot/Godot.java
@@ -61,8 +61,11 @@ import android.os.Messenger;
import android.os.VibrationEffect;
import android.os.Vibrator;
import android.provider.Settings.Secure;
+import android.support.annotation.CallSuper;
import android.support.annotation.Keep;
+import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
+import android.support.v4.app.FragmentActivity;
import android.view.Display;
import android.view.KeyEvent;
import android.view.MotionEvent;
@@ -87,22 +90,20 @@ import com.google.android.vending.expansion.downloader.IStub;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
-import java.lang.reflect.Method;
import java.security.MessageDigest;
-import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Locale;
-import javax.microedition.khronos.opengles.GL10;
import org.godotengine.godot.input.GodotEditText;
import org.godotengine.godot.payments.PaymentsManager;
+import org.godotengine.godot.plugin.GodotPlugin;
+import org.godotengine.godot.plugin.GodotPluginRegistry;
import org.godotengine.godot.utils.GodotNetUtils;
import org.godotengine.godot.utils.PermissionsUtil;
import org.godotengine.godot.xr.XRMode;
-public abstract class Godot extends Activity implements SensorEventListener, IDownloaderClient {
+public abstract class Godot extends FragmentActivity implements SensorEventListener, IDownloaderClient {
- static final int MAX_SINGLETONS = 64;
private IStub mDownloaderClientStub;
private TextView mStatusText;
private TextView mProgressFraction;
@@ -126,8 +127,7 @@ public abstract class Godot extends Activity implements SensorEventListener, IDo
private boolean activityResumed;
private int mState;
- // Used to dispatch events to the main thread.
- private final Handler mainThreadHandler = new Handler(Looper.getMainLooper());
+ private GodotPluginRegistry pluginRegistry;
static private Intent mCurrentIntent;
@@ -154,83 +154,6 @@ public abstract class Godot extends Activity implements SensorEventListener, IDo
mPauseButton.setText(stringResourceID);
}
- static public class SingletonBase {
-
- protected void registerClass(String p_name, String[] p_methods) {
-
- GodotLib.singleton(p_name, this);
-
- Class clazz = getClass();
- Method[] methods = clazz.getDeclaredMethods();
- for (Method method : methods) {
- boolean found = false;
-
- for (String s : p_methods) {
- if (s.equals(method.getName())) {
- found = true;
- break;
- }
- }
- if (!found)
- continue;
-
- List<String> ptr = new ArrayList<String>();
-
- Class[] paramTypes = method.getParameterTypes();
- for (Class c : paramTypes) {
- ptr.add(c.getName());
- }
-
- String[] pt = new String[ptr.size()];
- ptr.toArray(pt);
-
- GodotLib.method(p_name, method.getName(), method.getReturnType().getName(), pt);
- }
-
- Godot.singletons[Godot.singleton_count++] = this;
- }
-
- /**
- * Invoked once during the Godot Android initialization process after creation of the
- * {@link GodotView} view.
- * <p>
- * This method should be overridden by descendants of this class that would like to add
- * their view/layout to the Godot view hierarchy.
- *
- * @return the view to be included; null if no views should be included.
- */
- @Nullable
- protected View onMainCreateView(Activity activity) {
- return null;
- }
-
- protected void onMainActivityResult(int requestCode, int resultCode, Intent data) {
- }
-
- protected void onMainRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
- }
-
- protected void onMainPause() {}
- protected void onMainResume() {}
- protected void onMainDestroy() {}
- protected boolean onMainBackPressed() { return false; }
-
- protected void onGLDrawFrame(GL10 gl) {}
- protected void onGLSurfaceChanged(GL10 gl, int width, int height) {} // singletons will always miss first onGLSurfaceChanged call
- //protected void onGLSurfaceCreated(GL10 gl, EGLConfig config) {} // singletons won't be ready until first GodotLib.step()
-
- public void registerMethods() {}
- }
-
- /*
- protected List<SingletonBase> singletons = new ArrayList<SingletonBase>();
- protected void instanceSingleton(SingletonBase s) {
-
- s.registerMethods();
- singletons.add(s);
- }
- */
-
private String[] command_line;
private boolean use_apk_expansion;
@@ -246,9 +169,6 @@ public abstract class Godot extends Activity implements SensorEventListener, IDo
public static GodotIO io;
public static GodotNetUtils netUtils;
- static SingletonBase[] singletons = new SingletonBase[MAX_SINGLETONS];
- static int singleton_count = 0;
-
public interface ResultCallback {
public void callback(int requestCode, int resultCode, Intent data);
}
@@ -265,16 +185,15 @@ public abstract class Godot extends Activity implements SensorEventListener, IDo
result_callback = null;
};
- for (int i = 0; i < singleton_count; i++) {
-
- singletons[i].onMainActivityResult(requestCode, resultCode, data);
+ for (GodotPlugin plugin : pluginRegistry.getAllPlugins()) {
+ plugin.onMainActivityResult(requestCode, resultCode, data);
}
};
@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
- for (int i = 0; i < singleton_count; i++) {
- singletons[i].onMainRequestPermissionsResult(requestCode, permissions, grantResults);
+ for (GodotPlugin plugin : pluginRegistry.getAllPlugins()) {
+ plugin.onMainRequestPermissionsResult(requestCode, permissions, grantResults);
}
for (int i = 0; i < permissions.length; i++) {
@@ -283,6 +202,16 @@ public abstract class Godot extends Activity implements SensorEventListener, IDo
};
/**
+ * Invoked on the GL thread when the Godot main loop has started.
+ */
+ @CallSuper
+ protected void onGLGodotMainLoopStarted() {
+ for (GodotPlugin plugin : pluginRegistry.getAllPlugins()) {
+ plugin.onGLGodotMainLoopStarted();
+ }
+ }
+
+ /**
* Used by the native code (java_godot_lib_jni.cpp) to complete initialization of the GLSurfaceView view and renderer.
*/
@Keep
@@ -302,14 +231,13 @@ public abstract class Godot extends Activity implements SensorEventListener, IDo
edittext.setView(mView);
io.setEdit(edittext);
- final Godot godot = this;
mView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
Point fullSize = new Point();
- godot.getWindowManager().getDefaultDisplay().getSize(fullSize);
+ getWindowManager().getDefaultDisplay().getSize(fullSize);
Rect gameSize = new Rect();
- godot.mView.getWindowVisibleDisplayFrame(gameSize);
+ mView.getWindowVisibleDisplayFrame(gameSize);
final int keyboardHeight = fullSize.y - gameSize.bottom;
GodotLib.setVirtualKeyboardHeight(keyboardHeight);
@@ -321,23 +249,23 @@ public abstract class Godot extends Activity implements SensorEventListener, IDo
@Override
public void run() {
GodotLib.setup(current_command_line);
- setKeepScreenOn("True".equals(GodotLib.getGlobal("display/window/energy_saving/keep_screen_on")));
- // The Godot Android plugins are setup on completion of GodotLib.setup
- mainThreadHandler.post(new Runnable() {
- @Override
- public void run() {
- // Include the non-null views returned in the Godot view hierarchy.
- for (int i = 0; i < singleton_count; i++) {
- View view = singletons[i].onMainCreateView(Godot.this);
- if (view != null) {
- layout.addView(view);
- }
- }
- }
- });
+ // Must occur after GodotLib.setup has completed.
+ for (GodotPlugin plugin : pluginRegistry.getAllPlugins()) {
+ plugin.onGLRegisterPluginWithGodotNative();
+ }
+
+ setKeepScreenOn("True".equals(GodotLib.getGlobal("display/window/energy_saving/keep_screen_on")));
}
});
+
+ // Include the returned non-null views in the Godot view hierarchy.
+ for (GodotPlugin plugin : pluginRegistry.getAllPlugins()) {
+ View pluginView = plugin.onMainCreateView(this);
+ if (pluginView != null) {
+ layout.addView(pluginView);
+ }
+ }
}
public void setKeepScreenOn(final boolean p_enabled) {
@@ -535,6 +463,7 @@ public abstract class Godot extends Activity implements SensorEventListener, IDo
Window window = getWindow();
window.addFlags(WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON);
mClipboard = (ClipboardManager)getSystemService(Context.CLIPBOARD_SERVICE);
+ pluginRegistry = GodotPluginRegistry.initializePluginRegistry(this);
//check for apk expansion API
if (true) {
@@ -675,8 +604,8 @@ public abstract class Godot extends Activity implements SensorEventListener, IDo
protected void onDestroy() {
if (mPaymentsManager != null) mPaymentsManager.destroy();
- for (int i = 0; i < singleton_count; i++) {
- singletons[i].onMainDestroy();
+ for (GodotPlugin plugin : pluginRegistry.getAllPlugins()) {
+ plugin.onMainDestroy();
}
GodotLib.ondestroy(this);
@@ -703,8 +632,8 @@ public abstract class Godot extends Activity implements SensorEventListener, IDo
mSensorManager.unregisterListener(this);
- for (int i = 0; i < singleton_count; i++) {
- singletons[i].onMainPause();
+ for (GodotPlugin plugin : pluginRegistry.getAllPlugins()) {
+ plugin.onMainPause();
}
}
@@ -755,9 +684,8 @@ public abstract class Godot extends Activity implements SensorEventListener, IDo
View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY);
}
- for (int i = 0; i < singleton_count; i++) {
-
- singletons[i].onMainResume();
+ for (GodotPlugin plugin : pluginRegistry.getAllPlugins()) {
+ plugin.onMainResume();
}
}
@@ -850,8 +778,8 @@ public abstract class Godot extends Activity implements SensorEventListener, IDo
public void onBackPressed() {
boolean shouldQuit = true;
- for (int i = 0; i < singleton_count; i++) {
- if (singletons[i].onMainBackPressed()) {
+ for (GodotPlugin plugin : pluginRegistry.getAllPlugins()) {
+ if (plugin.onMainBackPressed()) {
shouldQuit = false;
}
}
@@ -866,6 +794,17 @@ public abstract class Godot extends Activity implements SensorEventListener, IDo
}
}
+ /**
+ * Queue a runnable to be run on the GL thread.
+ * <p>
+ * This must be called after the GL thread has started.
+ */
+ public final void runOnGLThread(@NonNull Runnable action) {
+ if (mView != null) {
+ mView.queueEvent(action);
+ }
+ }
+
private void forceQuit() {
System.exit(0);
}
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 9ee29d439c..89a65aea24 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/GodotLib.java
+++ b/platform/android/java/lib/src/org/godotengine/godot/GodotLib.java
@@ -176,22 +176,6 @@ public class GodotLib {
public static native void audio();
/**
- * Used to setup a {@link org.godotengine.godot.Godot.SingletonBase} instance.
- * @param p_name Name of the instance.
- * @param p_object Reference to the singleton instance.
- */
- public static native void singleton(String p_name, Object p_object);
-
- /**
- * Used to complete registration of the {@link org.godotengine.godot.Godot.SingletonBase} instance's methods.
- * @param p_sname Name of the instance
- * @param p_name Name of the method to register
- * @param p_ret Return type of the registered method
- * @param p_params Method parameters types
- */
- public static native void method(String p_sname, String p_name, String p_ret, String[] p_params);
-
- /**
* Used to access Godot global properties.
* @param p_key Property key
* @return String value of the property
diff --git a/platform/android/java/lib/src/org/godotengine/godot/GodotRenderer.java b/platform/android/java/lib/src/org/godotengine/godot/GodotRenderer.java
index 26fa033f12..ee9a2aee4f 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/GodotRenderer.java
+++ b/platform/android/java/lib/src/org/godotengine/godot/GodotRenderer.java
@@ -30,9 +30,12 @@
package org.godotengine.godot;
+import android.content.Context;
import android.opengl.GLSurfaceView;
import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;
+import org.godotengine.godot.plugin.GodotPlugin;
+import org.godotengine.godot.plugin.GodotPluginRegistry;
import org.godotengine.godot.utils.GLUtils;
/**
@@ -40,8 +43,13 @@ import org.godotengine.godot.utils.GLUtils;
*/
class GodotRenderer implements GLSurfaceView.Renderer {
+ private final GodotPluginRegistry pluginRegistry;
private boolean activityJustResumed = false;
+ GodotRenderer() {
+ this.pluginRegistry = GodotPluginRegistry.getPluginRegistry();
+ }
+
public void onDrawFrame(GL10 gl) {
if (activityJustResumed) {
GodotLib.onRendererResumed();
@@ -49,21 +57,23 @@ class GodotRenderer implements GLSurfaceView.Renderer {
}
GodotLib.step();
- for (int i = 0; i < Godot.singleton_count; i++) {
- Godot.singletons[i].onGLDrawFrame(gl);
+ for (GodotPlugin plugin : pluginRegistry.getAllPlugins()) {
+ plugin.onGLDrawFrame(gl);
}
}
public void onSurfaceChanged(GL10 gl, int width, int height) {
-
GodotLib.resize(width, height);
- for (int i = 0; i < Godot.singleton_count; i++) {
- Godot.singletons[i].onGLSurfaceChanged(gl, width, height);
+ for (GodotPlugin plugin : pluginRegistry.getAllPlugins()) {
+ plugin.onGLSurfaceChanged(gl, width, height);
}
}
public void onSurfaceCreated(GL10 gl, EGLConfig config) {
GodotLib.newcontext(GLUtils.use_32);
+ for (GodotPlugin plugin : pluginRegistry.getAllPlugins()) {
+ plugin.onGLSurfaceCreated(gl, config);
+ }
}
void onActivityResumed() {
diff --git a/platform/iphone/semaphore_iphone.cpp b/platform/android/java/lib/src/org/godotengine/godot/payments/GodotPaymentInterface.java
index 0461f58c40..6ac7338b30 100644
--- a/platform/iphone/semaphore_iphone.cpp
+++ b/platform/android/java/lib/src/org/godotengine/godot/payments/GodotPaymentInterface.java
@@ -1,12 +1,12 @@
/*************************************************************************/
-/* semaphore_iphone.cpp */
+/* GodotPaymentInterface.java */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2019 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 */
@@ -28,85 +28,70 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#include "semaphore_iphone.h"
+package org.godotengine.godot.payments;
-#include <fcntl.h>
-#include <unistd.h>
+public interface GodotPaymentInterface {
+ void purchase(String sku, String transactionId);
-void cgsem_init(cgsem_t *);
-void cgsem_post(cgsem_t *);
-void cgsem_wait(cgsem_t *);
-void cgsem_destroy(cgsem_t *);
+ void consumeUnconsumedPurchases();
-void cgsem_init(cgsem_t *cgsem) {
- int flags, fd, i;
+ String getSignature();
- pipe(cgsem->pipefd);
+ void callbackSuccess(String ticket, String signature, String sku);
- /* Make the pipes FD_CLOEXEC to allow them to close should we call
- * execv on restart. */
- for (i = 0; i < 2; i++) {
- fd = cgsem->pipefd[i];
- flags = fcntl(fd, F_GETFD, 0);
- flags |= FD_CLOEXEC;
- fcntl(fd, F_SETFD, flags);
- }
-}
+ void callbackSuccessProductMassConsumed(String ticket, String signature, String sku);
-void cgsem_post(cgsem_t *cgsem) {
- const char buf = 1;
+ void callbackSuccessNoUnconsumedPurchases();
- write(cgsem->pipefd[1], &buf, 1);
-}
+ void callbackFailConsume(String message);
-void cgsem_wait(cgsem_t *cgsem) {
- char buf;
+ void callbackFail(String message);
- read(cgsem->pipefd[0], &buf, 1);
-}
+ void callbackCancel();
-void cgsem_destroy(cgsem_t *cgsem) {
- close(cgsem->pipefd[1]);
- close(cgsem->pipefd[0]);
-}
+ void callbackAlreadyOwned(String sku);
-#include "core/os/memory.h"
+ int getPurchaseCallbackId();
-#include <errno.h>
+ void setPurchaseCallbackId(int purchaseCallbackId);
-Error SemaphoreIphone::wait() {
+ String getPurchaseValidationUrlPrefix();
- cgsem_wait(&sem);
- return OK;
-}
+ void setPurchaseValidationUrlPrefix(String url);
-Error SemaphoreIphone::post() {
+ String getAccessToken();
- cgsem_post(&sem);
+ void setAccessToken(String accessToken);
- return OK;
-}
-int SemaphoreIphone::get() const {
+ void setTransactionId(String transactionId);
- return 0;
-}
+ String getTransactionId();
-SemaphoreOld *SemaphoreIphone::create_semaphore_iphone() {
+ // request purchased items are not consumed
+ void requestPurchased();
- return memnew(SemaphoreIphone);
-}
+ // callback for requestPurchased()
+ void callbackPurchased(String receipt, String signature, String sku);
-void SemaphoreIphone::make_default() {
+ void callbackDisconnected();
- create_func = create_semaphore_iphone;
-}
+ void callbackConnected();
-SemaphoreIphone::SemaphoreIphone() {
+ // true if connected, false otherwise
+ boolean isConnected();
- cgsem_init(&sem);
-}
+ // consume item automatically after purchase. default is true.
+ void setAutoConsume(boolean autoConsume);
+
+ // consume a specific item
+ void consume(String sku);
+
+ // query in app item detail info
+ void querySkuDetails(String[] list);
+
+ void addSkuDetail(String itemJson);
-SemaphoreIphone::~SemaphoreIphone() {
+ void completeSkuDetail();
- cgsem_destroy(&sem);
+ void errorSkuDetail(String errorMessage);
}
diff --git a/platform/android/java/lib/src/org/godotengine/godot/payments/PaymentsManager.java b/platform/android/java/lib/src/org/godotengine/godot/payments/PaymentsManager.java
index 90b958266b..9bf6650f84 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/payments/PaymentsManager.java
+++ b/platform/android/java/lib/src/org/godotengine/godot/payments/PaymentsManager.java
@@ -43,7 +43,6 @@ import android.util.Log;
import com.android.vending.billing.IInAppBillingService;
import java.util.ArrayList;
import java.util.Arrays;
-import org.godotengine.godot.GodotPaymentV3;
import org.json.JSONException;
import org.json.JSONObject;
@@ -90,9 +89,9 @@ public class PaymentsManager {
public void onServiceDisconnected(ComponentName name) {
mService = null;
- // At this stage, godotPaymentV3 might not have been initialized yet.
- if (godotPaymentV3 != null) {
- godotPaymentV3.callbackDisconnected();
+ // At this stage, godotPayment might not have been initialized yet.
+ if (godotPayment != null) {
+ godotPayment.callbackDisconnected();
}
}
@@ -100,9 +99,9 @@ public class PaymentsManager {
public void onServiceConnected(ComponentName name, IBinder service) {
mService = IInAppBillingService.Stub.asInterface(service);
- // At this stage, godotPaymentV3 might not have been initialized yet.
- if (godotPaymentV3 != null) {
- godotPaymentV3.callbackConnected();
+ // At this stage, godotPayment might not have been initialized yet.
+ if (godotPayment != null) {
+ godotPayment.callbackConnected();
}
}
};
@@ -111,17 +110,17 @@ public class PaymentsManager {
new PurchaseTask(mService, activity) {
@Override
protected void error(String message) {
- godotPaymentV3.callbackFail(message);
+ godotPayment.callbackFail(message);
}
@Override
protected void canceled() {
- godotPaymentV3.callbackCancel();
+ godotPayment.callbackCancel();
}
@Override
protected void alreadyOwned() {
- godotPaymentV3.callbackAlreadyOwned(sku);
+ godotPayment.callbackAlreadyOwned(sku);
}
}
.purchase(sku, transactionId);
@@ -135,19 +134,19 @@ public class PaymentsManager {
new ReleaseAllConsumablesTask(mService, activity) {
@Override
protected void success(String sku, String receipt, String signature, String token) {
- godotPaymentV3.callbackSuccessProductMassConsumed(receipt, signature, sku);
+ godotPayment.callbackSuccessProductMassConsumed(receipt, signature, sku);
}
@Override
protected void error(String message) {
Log.d("godot", "consumeUnconsumedPurchases :" + message);
- godotPaymentV3.callbackFailConsume(message);
+ godotPayment.callbackFailConsume(message);
}
@Override
protected void notRequired() {
Log.d("godot", "callbackSuccessNoUnconsumedPurchases :");
- godotPaymentV3.callbackSuccessNoUnconsumedPurchases();
+ godotPayment.callbackSuccessNoUnconsumedPurchases();
}
}
.consumeItAll();
@@ -168,7 +167,7 @@ public class PaymentsManager {
final ArrayList<String> mySignatures = bundle.getStringArrayList("INAPP_DATA_SIGNATURE_LIST");
if (myPurchases == null || myPurchases.size() == 0) {
- godotPaymentV3.callbackPurchased("", "", "");
+ godotPayment.callbackPurchased("", "", "");
return;
}
@@ -186,7 +185,7 @@ public class PaymentsManager {
pc.setConsumableFlag("block", sku, true);
pc.setConsumableValue("token", sku, token);
- godotPaymentV3.callbackPurchased(receipt, signature, sku);
+ godotPayment.callbackPurchased(receipt, signature, sku);
} catch (JSONException e) {
}
}
@@ -203,7 +202,7 @@ public class PaymentsManager {
new HandlePurchaseTask(activity) {
@Override
protected void success(final String sku, final String signature, final String ticket) {
- godotPaymentV3.callbackSuccess(ticket, signature, sku);
+ godotPayment.callbackSuccess(ticket, signature, sku);
if (auto_consume) {
new ConsumeTask(mService, activity) {
@@ -213,7 +212,7 @@ public class PaymentsManager {
@Override
protected void error(String message) {
- godotPaymentV3.callbackFail(message);
+ godotPayment.callbackFail(message);
}
}
.consume(sku);
@@ -222,12 +221,12 @@ public class PaymentsManager {
@Override
protected void error(String message) {
- godotPaymentV3.callbackFail(message);
+ godotPayment.callbackFail(message);
}
@Override
protected void canceled() {
- godotPaymentV3.callbackCancel();
+ godotPayment.callbackCancel();
}
}
.handlePurchaseRequest(resultCode, data);
@@ -235,19 +234,19 @@ public class PaymentsManager {
public void validatePurchase(String purchaseToken, final String sku) {
- new ValidateTask(activity, godotPaymentV3) {
+ new ValidateTask(activity, godotPayment) {
@Override
protected void success() {
new ConsumeTask(mService, activity) {
@Override
protected void success(String ticket) {
- godotPaymentV3.callbackSuccess(ticket, null, sku);
+ godotPayment.callbackSuccess(ticket, null, sku);
}
@Override
protected void error(String message) {
- godotPaymentV3.callbackFail(message);
+ godotPayment.callbackFail(message);
}
}
.consume(sku);
@@ -255,12 +254,12 @@ public class PaymentsManager {
@Override
protected void error(String message) {
- godotPaymentV3.callbackFail(message);
+ godotPayment.callbackFail(message);
}
@Override
protected void canceled() {
- godotPaymentV3.callbackCancel();
+ godotPayment.callbackCancel();
}
}
.validatePurchase(sku);
@@ -274,12 +273,12 @@ public class PaymentsManager {
new ConsumeTask(mService, activity) {
@Override
protected void success(String ticket) {
- godotPaymentV3.callbackSuccessProductMassConsumed(ticket, "", sku);
+ godotPayment.callbackSuccessProductMassConsumed(ticket, "", sku);
}
@Override
protected void error(String message) {
- godotPaymentV3.callbackFailConsume(message);
+ godotPayment.callbackFailConsume(message);
}
}
.consume(sku);
@@ -387,9 +386,9 @@ public class PaymentsManager {
if (!skuDetails.containsKey("DETAILS_LIST")) {
int response = getResponseCodeFromBundle(skuDetails);
if (response != BILLING_RESPONSE_RESULT_OK) {
- godotPaymentV3.errorSkuDetail(getResponseDesc(response));
+ godotPayment.errorSkuDetail(getResponseDesc(response));
} else {
- godotPaymentV3.errorSkuDetail("No error but no detail list.");
+ godotPayment.errorSkuDetail("No error but no detail list.");
}
return;
}
@@ -398,22 +397,22 @@ public class PaymentsManager {
for (String thisResponse : responseList) {
Log.d("godot", "response = " + thisResponse);
- godotPaymentV3.addSkuDetail(thisResponse);
+ godotPayment.addSkuDetail(thisResponse);
}
} catch (RemoteException e) {
e.printStackTrace();
- godotPaymentV3.errorSkuDetail("RemoteException error!");
+ godotPayment.errorSkuDetail("RemoteException error!");
}
}
- godotPaymentV3.completeSkuDetail();
+ godotPayment.completeSkuDetail();
}
}))
.start();
}
- private GodotPaymentV3 godotPaymentV3;
+ private GodotPaymentInterface godotPayment;
- public void setBaseSingleton(GodotPaymentV3 godotPaymentV3) {
- this.godotPaymentV3 = godotPaymentV3;
+ public void setBaseSingleton(GodotPaymentInterface godotPaymentInterface) {
+ this.godotPayment = godotPaymentInterface;
}
}
diff --git a/platform/android/java/lib/src/org/godotengine/godot/payments/ValidateTask.java b/platform/android/java/lib/src/org/godotengine/godot/payments/ValidateTask.java
index dbb6b8a783..10c314aecf 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/payments/ValidateTask.java
+++ b/platform/android/java/lib/src/org/godotengine/godot/payments/ValidateTask.java
@@ -34,7 +34,6 @@ import android.app.Activity;
import android.app.ProgressDialog;
import android.os.AsyncTask;
import java.lang.ref.WeakReference;
-import org.godotengine.godot.GodotPaymentV3;
import org.godotengine.godot.utils.HttpRequester;
import org.godotengine.godot.utils.RequestParams;
import org.json.JSONException;
@@ -43,7 +42,7 @@ import org.json.JSONObject;
abstract public class ValidateTask {
private Activity context;
- private GodotPaymentV3 godotPaymentsV3;
+ private GodotPaymentInterface godotPayments;
private ProgressDialog dialog;
private String mSku;
@@ -80,9 +79,9 @@ abstract public class ValidateTask {
}
}
- public ValidateTask(Activity context, GodotPaymentV3 godotPaymentsV3) {
+ public ValidateTask(Activity context, GodotPaymentInterface godotPayments) {
this.context = context;
- this.godotPaymentsV3 = godotPaymentsV3;
+ this.godotPayments = godotPayments;
}
public void validatePurchase(final String sku) {
@@ -96,7 +95,7 @@ abstract public class ValidateTask {
private String doInBackground(String... params) {
PaymentsCache pc = new PaymentsCache(context);
- String url = godotPaymentsV3.getPurchaseValidationUrlPrefix();
+ String url = godotPayments.getPurchaseValidationUrlPrefix();
RequestParams param = new RequestParams();
param.setUrl(url);
param.put("ticket", pc.getConsumableValue("ticket", mSku));
diff --git a/platform/android/java/lib/src/org/godotengine/godot/plugin/GodotPlugin.java b/platform/android/java/lib/src/org/godotengine/godot/plugin/GodotPlugin.java
new file mode 100644
index 0000000000..d5bf4fc70e
--- /dev/null
+++ b/platform/android/java/lib/src/org/godotengine/godot/plugin/GodotPlugin.java
@@ -0,0 +1,256 @@
+/*************************************************************************/
+/* GodotPlugin.java */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2019 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.godot.plugin;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.view.View;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
+import javax.microedition.khronos.egl.EGLConfig;
+import javax.microedition.khronos.opengles.GL10;
+import org.godotengine.godot.Godot;
+
+/**
+ * Base class for the Godot Android plugins.
+ * <p>
+ * A Godot Android plugin is a regular Android library packaged as an aar archive file with the following caveats:
+ * <p>
+ * - The library must have a dependency on the Godot Android library (godot-lib.aar).
+ * A stable version is available for each release.
+ * <p>
+ * - The library must include a <meta-data> tag in its manifest file setup as follow:
+ * <meta-data android:name="org.godotengine.plugin.v1.[PluginName]" android:value="[plugin.init.ClassFullName]" />
+ * Where:
+ * - 'PluginName' is the name of the plugin.
+ * - 'plugin.init.ClassFullName' is the full name (package + class name) of the plugin class
+ * extending {@link GodotPlugin}.
+ *
+ * A plugin can also define and provide c/c++ gdnative libraries and nativescripts for the target
+ * app/game to leverage.
+ * The shared library for the gdnative library will be automatically bundled by the aar build
+ * system.
+ * Godot '*.gdnlib' and '*.gdns' resource files must however be manually defined in the project
+ * 'assets' directory. The recommended path for these resources in the 'assets' directory should be:
+ * 'godot/plugin/v1/[PluginName]/'
+ */
+public abstract class GodotPlugin {
+
+ private final Godot godot;
+
+ public GodotPlugin(Godot godot) {
+ this.godot = godot;
+ }
+
+ /**
+ * Provides access to the Godot engine.
+ */
+ protected Godot getGodot() {
+ return godot;
+ }
+
+ /**
+ * Register the plugin with Godot native code.
+ */
+ public final void onGLRegisterPluginWithGodotNative() {
+ nativeRegisterSingleton(getPluginName());
+
+ Class clazz = getClass();
+ Method[] methods = clazz.getDeclaredMethods();
+ for (Method method : methods) {
+ boolean found = false;
+
+ for (String s : getPluginMethods()) {
+ if (s.equals(method.getName())) {
+ found = true;
+ break;
+ }
+ }
+ if (!found)
+ continue;
+
+ List<String> ptr = new ArrayList<String>();
+
+ Class[] paramTypes = method.getParameterTypes();
+ for (Class c : paramTypes) {
+ ptr.add(c.getName());
+ }
+
+ String[] pt = new String[ptr.size()];
+ ptr.toArray(pt);
+
+ nativeRegisterMethod(getPluginName(), method.getName(), method.getReturnType().getName(), pt);
+ }
+
+ // Get the list of gdnative libraries to register.
+ Set<String> gdnativeLibrariesPaths = getPluginGDNativeLibrariesPaths();
+ if (!gdnativeLibrariesPaths.isEmpty()) {
+ nativeRegisterGDNativeLibraries(gdnativeLibrariesPaths.toArray(new String[0]));
+ }
+ }
+
+ /**
+ * Invoked once during the Godot Android initialization process after creation of the
+ * {@link org.godotengine.godot.GodotView} view.
+ * <p>
+ * This method should be overridden by descendants of this class that would like to add
+ * their view/layout to the Godot view hierarchy.
+ *
+ * @return the view to be included; null if no views should be included.
+ */
+ @Nullable
+ public View onMainCreateView(Activity activity) {
+ return null;
+ }
+
+ /**
+ * @see Activity#onActivityResult(int, int, Intent)
+ */
+ public void onMainActivityResult(int requestCode, int resultCode, Intent data) {
+ }
+
+ /**
+ * @see Activity#onRequestPermissionsResult(int, String[], int[])
+ */
+ public void onMainRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
+ }
+
+ /**
+ * @see Activity#onPause()
+ */
+ public void onMainPause() {}
+
+ /**
+ * @see Activity#onResume()
+ */
+ public void onMainResume() {}
+
+ /**
+ * @see Activity#onDestroy()
+ */
+ public void onMainDestroy() {}
+
+ /**
+ * @see Activity#onBackPressed()
+ */
+ public boolean onMainBackPressed() { return false; }
+
+ /**
+ * Invoked on the GL thread when the Godot main loop has started.
+ */
+ public void onGLGodotMainLoopStarted() {}
+
+ /**
+ * Invoked once per frame on the GL thread after the frame is drawn.
+ */
+ public void onGLDrawFrame(GL10 gl) {}
+
+ /**
+ * Called on the GL thread after the surface is created and whenever the OpenGL ES surface size
+ * changes.
+ */
+ public void onGLSurfaceChanged(GL10 gl, int width, int height) {}
+
+ /**
+ * Called on the GL thread when the surface is created or recreated.
+ */
+ public void onGLSurfaceCreated(GL10 gl, EGLConfig config) {}
+
+ /**
+ * Returns the name of the plugin.
+ * <p>
+ * This value must match the one listed in the plugin '<meta-data>' manifest entry.
+ */
+ @NonNull
+ public abstract String getPluginName();
+
+ /**
+ * Returns the list of methods to be exposed to Godot.
+ */
+ @NonNull
+ public abstract List<String> getPluginMethods();
+
+ /**
+ * Returns the paths for the plugin's gdnative libraries.
+ *
+ * The paths must be relative to the 'assets' directory and point to a '*.gdnlib' file.
+ */
+ @NonNull
+ protected Set<String> getPluginGDNativeLibrariesPaths() {
+ return Collections.emptySet();
+ }
+
+ /**
+ * Runs the specified action on the UI thread. If the current thread is the UI
+ * thread, then the action is executed immediately. If the current thread is
+ * not the UI thread, the action is posted to the event queue of the UI thread.
+ *
+ * @param action the action to run on the UI thread
+ */
+ protected void runOnUiThread(Runnable action) {
+ godot.runOnUiThread(action);
+ }
+
+ /**
+ * Queue the specified action to be run on the GL thread.
+ *
+ * @param action the action to run on the GL thread
+ */
+ protected void runOnGLThread(Runnable action) {
+ godot.runOnGLThread(action);
+ }
+
+ /**
+ * Used to setup a {@link GodotPlugin} instance.
+ * @param p_name Name of the instance.
+ */
+ private native void nativeRegisterSingleton(String p_name);
+
+ /**
+ * Used to complete registration of the {@link GodotPlugin} instance's methods.
+ * @param p_sname Name of the instance
+ * @param p_name Name of the method to register
+ * @param p_ret Return type of the registered method
+ * @param p_params Method parameters types
+ */
+ private native void nativeRegisterMethod(String p_sname, String p_name, String p_ret, String[] p_params);
+
+ /**
+ * Used to register gdnative libraries bundled by the plugin.
+ * @param gdnlibPaths Paths to the libraries relative to the 'assets' directory.
+ */
+ private native void nativeRegisterGDNativeLibraries(String[] gdnlibPaths);
+}
diff --git a/platform/android/java/lib/src/org/godotengine/godot/plugin/GodotPluginRegistry.java b/platform/android/java/lib/src/org/godotengine/godot/plugin/GodotPluginRegistry.java
new file mode 100644
index 0000000000..3562920182
--- /dev/null
+++ b/platform/android/java/lib/src/org/godotengine/godot/plugin/GodotPluginRegistry.java
@@ -0,0 +1,196 @@
+/*************************************************************************/
+/* GodotPluginRegistry.java */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2019 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.godot.plugin;
+
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.os.Bundle;
+import android.support.annotation.Nullable;
+import android.text.TextUtils;
+import android.util.Log;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import org.godotengine.godot.Godot;
+
+/**
+ * Registry used to load and access the registered Godot Android plugins.
+ */
+public final class GodotPluginRegistry {
+
+ private static final String TAG = GodotPluginRegistry.class.getSimpleName();
+
+ private static final String GODOT_PLUGIN_V1_NAME_PREFIX = "org.godotengine.plugin.v1.";
+
+ /**
+ * Name for the metadata containing the list of Godot plugins to enable.
+ */
+ private static final String GODOT_ENABLED_PLUGINS_LABEL = "custom_template_plugins";
+
+ private static GodotPluginRegistry instance;
+ private final ConcurrentHashMap<String, GodotPlugin> registry;
+
+ private GodotPluginRegistry(Godot godot) {
+ registry = new ConcurrentHashMap<>();
+ loadPlugins(godot);
+ }
+
+ /**
+ * Retrieve the plugin tied to the given plugin name.
+ * @param pluginName Name of the plugin
+ * @return {@link GodotPlugin} handle if it exists, null otherwise.
+ */
+ @Nullable
+ public GodotPlugin getPlugin(String pluginName) {
+ return registry.get(pluginName);
+ }
+
+ /**
+ * Retrieve the full set of loaded plugins.
+ */
+ public Collection<GodotPlugin> getAllPlugins() {
+ return registry.values();
+ }
+
+ /**
+ * Parse the manifest file and load all included Godot Android plugins.
+ * <p>
+ * A plugin manifest entry is a '<meta-data>' tag setup as described in the {@link GodotPlugin}
+ * documentation.
+ *
+ * @param godot Godot instance
+ * @return A singleton instance of {@link GodotPluginRegistry}. This ensures that only one instance
+ * of each Godot Android plugins is available at runtime.
+ */
+ public static GodotPluginRegistry initializePluginRegistry(Godot godot) {
+ if (instance == null) {
+ instance = new GodotPluginRegistry(godot);
+ }
+
+ return instance;
+ }
+
+ /**
+ * Return the plugin registry if it's initialized.
+ * Throws a {@link IllegalStateException} exception if not.
+ *
+ * @throws IllegalStateException if {@link GodotPluginRegistry#initializePluginRegistry(Godot)} has not been called prior to calling this method.
+ */
+ public static GodotPluginRegistry getPluginRegistry() throws IllegalStateException {
+ if (instance == null) {
+ throw new IllegalStateException("Plugin registry hasn't been initialized.");
+ }
+
+ return instance;
+ }
+
+ private void loadPlugins(Godot godot) {
+ try {
+ ApplicationInfo appInfo = godot
+ .getPackageManager()
+ .getApplicationInfo(godot.getPackageName(), PackageManager.GET_META_DATA);
+ Bundle metaData = appInfo.metaData;
+ if (metaData == null || metaData.isEmpty()) {
+ return;
+ }
+
+ // When using the Godot editor for building and exporting the apk, this is used to check
+ // which plugins to enable since the custom build template may contain prebuilt plugins.
+ // When using a custom process to generate the apk, the metadata is not needed since
+ // it's assumed that the developer is aware of the dependencies included in the apk.
+ final Set<String> enabledPluginsSet;
+ if (metaData.containsKey(GODOT_ENABLED_PLUGINS_LABEL)) {
+ String enabledPlugins = metaData.getString(GODOT_ENABLED_PLUGINS_LABEL, "");
+ String[] enabledPluginsList = enabledPlugins.split(",");
+ if (enabledPluginsList.length == 0) {
+ // No plugins to enable. Aborting early.
+ return;
+ }
+
+ enabledPluginsSet = new HashSet<>(Arrays.asList(enabledPluginsList));
+ } else {
+ enabledPluginsSet = null;
+ }
+
+ int godotPluginV1NamePrefixLength = GODOT_PLUGIN_V1_NAME_PREFIX.length();
+ for (String metaDataName : metaData.keySet()) {
+ // Parse the meta-data looking for entry with the Godot plugin name prefix.
+ if (metaDataName.startsWith(GODOT_PLUGIN_V1_NAME_PREFIX)) {
+ String pluginName = metaDataName.substring(godotPluginV1NamePrefixLength);
+ if (enabledPluginsSet != null && !enabledPluginsSet.contains(pluginName)) {
+ Log.w(TAG, "Plugin " + pluginName + " is listed in the dependencies but is not enabled.");
+ continue;
+ }
+
+ // Retrieve the plugin class full name.
+ String pluginHandleClassFullName = metaData.getString(metaDataName);
+ if (!TextUtils.isEmpty(pluginHandleClassFullName)) {
+ try {
+ // Attempt to create the plugin init class via reflection.
+ @SuppressWarnings("unchecked")
+ Class<GodotPlugin> pluginClass = (Class<GodotPlugin>)Class
+ .forName(pluginHandleClassFullName);
+ Constructor<GodotPlugin> pluginConstructor = pluginClass
+ .getConstructor(Godot.class);
+ GodotPlugin pluginHandle = pluginConstructor.newInstance(godot);
+
+ // Load the plugin initializer into the registry using the plugin name
+ // as key.
+ if (!pluginName.equals(pluginHandle.getPluginName())) {
+ Log.w(TAG,
+ "Meta-data plugin name does not match the value returned by the plugin handle: " + pluginName + " =/= " + pluginHandle.getPluginName());
+ }
+ registry.put(pluginName, pluginHandle);
+ } catch (ClassNotFoundException e) {
+ Log.w(TAG, "Unable to load Godot plugin " + pluginName, e);
+ } catch (IllegalAccessException e) {
+ Log.w(TAG, "Unable to load Godot plugin " + pluginName, e);
+ } catch (InstantiationException e) {
+ Log.w(TAG, "Unable to load Godot plugin " + pluginName, e);
+ } catch (NoSuchMethodException e) {
+ Log.w(TAG, "Unable to load Godot plugin " + pluginName, e);
+ } catch (InvocationTargetException e) {
+ Log.w(TAG, "Unable to load Godot plugin " + pluginName, e);
+ }
+ } else {
+ Log.w(TAG, "Invalid plugin loader class for " + pluginName);
+ }
+ }
+ }
+ } catch (PackageManager.NameNotFoundException e) {
+ Log.e(TAG, "Unable load Godot Android plugins from the manifest file.", e);
+ }
+ }
+}
diff --git a/drivers/windows/semaphore_windows.cpp b/platform/android/java/lib/src/org/godotengine/godot/vulkan/VkRenderer.kt
index 1b53e311ff..67faad8ddd 100644
--- a/drivers/windows/semaphore_windows.cpp
+++ b/platform/android/java/lib/src/org/godotengine/godot/vulkan/VkRenderer.kt
@@ -1,5 +1,5 @@
/*************************************************************************/
-/* semaphore_windows.cpp */
+/* VkRenderer.kt */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
@@ -28,71 +28,72 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#include "semaphore_windows.h"
-
-#if defined(WINDOWS_ENABLED)
-
-#include "core/os/memory.h"
-
-Error SemaphoreWindows::wait() {
-
- WaitForSingleObjectEx(semaphore, INFINITE, false);
- return OK;
-}
-Error SemaphoreWindows::post() {
-
- ReleaseSemaphore(semaphore, 1, NULL);
- return OK;
-}
-int SemaphoreWindows::get() const {
- long previous;
- switch (WaitForSingleObjectEx(semaphore, 0, false)) {
- case WAIT_OBJECT_0: {
- ERR_FAIL_COND_V(!ReleaseSemaphore(semaphore, 1, &previous), -1);
- return previous + 1;
- } break;
- case WAIT_TIMEOUT: {
- return 0;
- } break;
- default: {
- }
+@file:JvmName("VkRenderer")
+package org.godotengine.godot.vulkan
+
+import android.view.Surface
+
+/**
+ * Responsible to setting up and driving the Vulkan rendering logic.
+ *
+ * <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 [VkSurfaceView.queueOnVkThread] convenience method.
+ *
+ * @see [VkSurfaceView.startRenderer]
+ */
+internal class VkRenderer {
+
+ /**
+ * Called when the surface is created and signals the beginning of rendering.
+ */
+ fun onVkSurfaceCreated(surface: Surface) {
+ nativeOnVkSurfaceCreated(surface)
}
- ERR_FAIL_V(-1);
-}
-
-SemaphoreOld *SemaphoreWindows::create_semaphore_windows() {
-
- return memnew(SemaphoreWindows);
-}
-
-void SemaphoreWindows::make_default() {
+ /**
+ * Called after the surface is created and whenever its size changes.
+ */
+ fun onVkSurfaceChanged(surface: Surface, width: Int, height: Int) {
+ nativeOnVkSurfaceChanged(surface, width, height)
+ }
- create_func = create_semaphore_windows;
-}
+ /**
+ * Called to draw the current frame.
+ */
+ fun onVkDrawFrame() {
+ nativeOnVkDrawFrame()
+ }
-SemaphoreWindows::SemaphoreWindows() {
+ /**
+ * Called when the rendering thread is resumed.
+ */
+ fun onVkResume() {
+ nativeOnVkResume()
+ }
-#ifdef UWP_ENABLED
- semaphore = CreateSemaphoreEx(
- NULL,
- 0,
- 0xFFFFFFF, //wathever
- NULL,
- 0,
- SEMAPHORE_ALL_ACCESS);
-#else
- semaphore = CreateSemaphore(
- NULL,
- 0,
- 0xFFFFFFF, //wathever
- NULL);
-#endif
-}
+ /**
+ * Called when the rendering thread is paused.
+ */
+ fun onVkPause() {
+ nativeOnVkPause()
+ }
-SemaphoreWindows::~SemaphoreWindows() {
+ /**
+ * Called when the rendering thread is destroyed and used as signal to tear down the Vulkan logic.
+ */
+ fun onVkDestroy() {
+ nativeOnVkDestroy()
+ }
- CloseHandle(semaphore);
+ private external fun nativeOnVkSurfaceCreated(surface: Surface)
+ private external fun nativeOnVkSurfaceChanged(surface: Surface, width: Int, height: Int)
+ private external fun nativeOnVkResume()
+ private external fun nativeOnVkDrawFrame()
+ private external fun nativeOnVkPause()
+ private external fun nativeOnVkDestroy()
}
-
-#endif
diff --git a/platform/android/java/lib/src/org/godotengine/godot/vulkan/VkSurfaceView.kt b/platform/android/java/lib/src/org/godotengine/godot/vulkan/VkSurfaceView.kt
new file mode 100644
index 0000000000..1c594f3201
--- /dev/null
+++ b/platform/android/java/lib/src/org/godotengine/godot/vulkan/VkSurfaceView.kt
@@ -0,0 +1,136 @@
+/*************************************************************************/
+/* VkSurfaceView.kt */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2020 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. */
+/*************************************************************************/
+
+@file:JvmName("VkSurfaceView")
+package org.godotengine.godot.vulkan
+
+import android.content.Context
+import android.view.SurfaceHolder
+import android.view.SurfaceView
+
+/**
+ * An implementation of SurfaceView that uses the dedicated surface for
+ * displaying Vulkan rendering.
+ * <p>
+ * A [VkSurfaceView] 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>Accepts a user-provided [VkRenderer] object that does the actual rendering.
+ * <li>Renders on a dedicated [VkThread] thread to decouple rendering performance from the
+ * UI thread.
+ * </ul>
+ */
+internal class VkSurfaceView(context: Context) : SurfaceView(context), SurfaceHolder.Callback {
+
+ companion object {
+ fun checkState(expression: Boolean, errorMessage: Any) {
+ check(expression) { errorMessage.toString() }
+ }
+ }
+
+ /**
+ * Thread used to drive the vulkan logic.
+ */
+ private val vkThread: VkThread by lazy {
+ VkThread(this, renderer)
+ }
+
+ /**
+ * Performs the actual rendering.
+ */
+ private lateinit var renderer: VkRenderer
+
+ init {
+ isClickable = true
+ holder.addCallback(this)
+ }
+
+ /**
+ * Set the [VkRenderer] associated with the view, and starts the thread that will drive the vulkan
+ * rendering.
+ *
+ * This method should be called once and only once in the life-cycle of [VkSurfaceView].
+ */
+ fun startRenderer(renderer: VkRenderer) {
+ checkState(!this::renderer.isInitialized, "startRenderer must only be invoked once")
+ this.renderer = renderer
+ vkThread.start()
+ }
+
+ /**
+ * Queues a runnable to be run on the Vulkan rendering thread.
+ *
+ * Must not be called before a [VkRenderer] has been set.
+ */
+ fun queueOnVkThread(runnable: Runnable) {
+ vkThread.queueEvent(runnable)
+ }
+
+ /**
+ * Resumes the rendering thread.
+ *
+ * Must not be called before a [VkRenderer] has been set.
+ */
+ fun onResume() {
+ vkThread.onResume()
+ }
+
+ /**
+ * Pauses the rendering thread.
+ *
+ * Must not be called before a [VkRenderer] has been set.
+ */
+ fun onPause() {
+ vkThread.onPause()
+ }
+
+ /**
+ * Tear down the rendering thread.
+ *
+ * Must not be called before a [VkRenderer] has been set.
+ */
+ fun onDestroy() {
+ vkThread.blockingExit()
+ }
+
+ override fun surfaceChanged(holder: SurfaceHolder, format: Int, width: Int, height: Int) {
+ vkThread.onSurfaceChanged(width, height)
+ }
+
+ override fun surfaceDestroyed(holder: SurfaceHolder) {
+ vkThread.onSurfaceDestroyed()
+ }
+
+ override fun surfaceCreated(holder: SurfaceHolder) {
+ vkThread.onSurfaceCreated()
+ }
+}
diff --git a/platform/android/java/lib/src/org/godotengine/godot/vulkan/VkThread.kt b/platform/android/java/lib/src/org/godotengine/godot/vulkan/VkThread.kt
new file mode 100644
index 0000000000..2e332840bf
--- /dev/null
+++ b/platform/android/java/lib/src/org/godotengine/godot/vulkan/VkThread.kt
@@ -0,0 +1,230 @@
+/*************************************************************************/
+/* VkThread.kt */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2020 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. */
+/*************************************************************************/
+
+@file:JvmName("VkThread")
+package org.godotengine.godot.vulkan
+
+import android.util.Log
+import java.util.concurrent.locks.ReentrantLock
+import kotlin.concurrent.withLock
+
+/**
+ * Thread implementation for the [VkSurfaceView] onto which the vulkan logic is ran.
+ *
+ * The implementation is modeled after [android.opengl.GLSurfaceView]'s GLThread.
+ */
+internal class VkThread(private val vkSurfaceView: VkSurfaceView, private val vkRenderer: VkRenderer) : Thread(TAG) {
+
+ companion object {
+ private val TAG = VkThread::class.java.simpleName
+ }
+
+ /**
+ * Used to run events scheduled on the thread.
+ */
+ private val eventQueue = ArrayList<Runnable>()
+
+ /**
+ * Used to synchronize interaction with other threads (e.g: main thread).
+ */
+ private val lock = ReentrantLock()
+ private val lockCondition = lock.newCondition()
+
+ private var shouldExit = false
+ private var exited = false
+ private var rendererInitialized = false
+ private var rendererResumed = false
+ private var resumed = false
+ private var hasSurface = false
+ private var width = 0
+ private var height = 0
+
+ /**
+ * Determine when drawing can occur on the thread. This usually occurs after the
+ * [android.view.Surface] is available, the app is in a resumed state.
+ */
+ private val readyToDraw
+ get() = hasSurface && resumed
+
+ private fun threadExiting() {
+ lock.withLock {
+ exited = true
+ lockCondition.signalAll()
+ }
+ }
+
+ /**
+ * Queue an event on the [VkThread].
+ */
+ fun queueEvent(event: Runnable) {
+ lock.withLock {
+ eventQueue.add(event)
+ lockCondition.signalAll()
+ }
+ }
+
+ /**
+ * Request the thread to exit and block until it's done.
+ */
+ fun blockingExit() {
+ lock.withLock {
+ shouldExit = true
+ lockCondition.signalAll()
+ while (!exited) {
+ try {
+ Log.i(TAG, "Waiting on exit for $name")
+ lockCondition.await()
+ } catch (ex: InterruptedException) {
+ currentThread().interrupt()
+ }
+ }
+ }
+ }
+
+ /**
+ * Invoked when the app resumes.
+ */
+ fun onResume() {
+ lock.withLock {
+ resumed = true
+ lockCondition.signalAll()
+ }
+ }
+
+ /**
+ * Invoked when the app pauses.
+ */
+ fun onPause() {
+ lock.withLock {
+ resumed = false
+ lockCondition.signalAll()
+ }
+ }
+
+ /**
+ * Invoked when the [android.view.Surface] has been created.
+ */
+ fun onSurfaceCreated() {
+ // This is a no op because surface creation will always be followed by surfaceChanged()
+ // which provide all the needed information.
+ }
+
+ /**
+ * Invoked following structural updates to [android.view.Surface].
+ */
+ fun onSurfaceChanged(width: Int, height: Int) {
+ lock.withLock {
+ hasSurface = true
+ this.width = width
+ this.height = height
+ lockCondition.signalAll()
+ }
+ }
+
+ /**
+ * Invoked when the [android.view.Surface] is no longer available.
+ */
+ fun onSurfaceDestroyed() {
+ lock.withLock {
+ hasSurface = false
+ lockCondition.signalAll()
+ }
+ }
+
+ /**
+ * Thread loop modeled after [android.opengl.GLSurfaceView]'s GLThread.
+ */
+ override fun run() {
+ try {
+ while (true) {
+ var event: Runnable? = null
+ lock.withLock {
+ while (true) {
+ // Code path for exiting the thread loop.
+ if (shouldExit) {
+ vkRenderer.onVkDestroy()
+ return
+ }
+
+ // Check for events and execute them outside of the loop if found to avoid
+ // blocking the thread lifecycle by holding onto the lock.
+ if (eventQueue.isNotEmpty()) {
+ event = eventQueue.removeAt(0)
+ break;
+ }
+
+ if (readyToDraw) {
+ if (!rendererResumed) {
+ rendererResumed = true
+ vkRenderer.onVkResume()
+
+ if (!rendererInitialized) {
+ rendererInitialized = true
+ vkRenderer.onVkSurfaceCreated(vkSurfaceView.holder.surface)
+ }
+
+ vkRenderer.onVkSurfaceChanged(vkSurfaceView.holder.surface, width, height)
+ }
+
+ // Break out of the loop so drawing can occur without holding onto the lock.
+ break;
+ } else if (rendererResumed) {
+ // If we aren't ready to draw but are resumed, that means we either lost a surface
+ // or the app was paused.
+ rendererResumed = false
+ vkRenderer.onVkPause()
+ }
+ // We only reach this state if we are not ready to draw and have no queued events, so
+ // we wait.
+ // On state change, the thread will be awoken using the [lock] and [lockCondition], and
+ // we will resume execution.
+ lockCondition.await()
+ }
+ }
+
+ // Run queued event.
+ if (event != null) {
+ event?.run()
+ continue
+ }
+
+ // Draw only when there no more queued events.
+ vkRenderer.onVkDrawFrame()
+ }
+ } catch (ex: InterruptedException) {
+ Log.i(TAG, ex.message)
+ } catch (ex: IllegalStateException) {
+ Log.i(TAG, ex.message)
+ } finally {
+ threadExiting()
+ }
+ }
+
+}
diff --git a/platform/android/java/plugins/godotpayment/build.gradle b/platform/android/java/plugins/godotpayment/build.gradle
new file mode 100644
index 0000000000..4f376c4587
--- /dev/null
+++ b/platform/android/java/plugins/godotpayment/build.gradle
@@ -0,0 +1,31 @@
+apply plugin: 'com.android.library'
+
+android {
+ compileSdkVersion versions.compileSdk
+ buildToolsVersion versions.buildTools
+
+ defaultConfig {
+ minSdkVersion versions.minSdk
+ targetSdkVersion versions.targetSdk
+ }
+
+ libraryVariants.all { variant ->
+ variant.outputs.all { output ->
+ output.outputFileName = "GodotPayment.${variant.name}.aar"
+ }
+ }
+
+}
+
+dependencies {
+ implementation libraries.supportCoreUtils
+ implementation libraries.v4Support
+
+ if (rootProject.findProject(":lib")) {
+ compileOnly project(":lib")
+ } else if (rootProject.findProject(":godot:lib")) {
+ compileOnly project(":godot:lib")
+ } else {
+ compileOnly fileTree(dir: 'libs', include: ['godot-lib*.aar'])
+ }
+}
diff --git a/platform/android/java/plugins/godotpayment/src/main/AndroidManifest.xml b/platform/android/java/plugins/godotpayment/src/main/AndroidManifest.xml
new file mode 100644
index 0000000000..61afa03799
--- /dev/null
+++ b/platform/android/java/plugins/godotpayment/src/main/AndroidManifest.xml
@@ -0,0 +1,11 @@
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="org.godotengine.godot.plugin.payment">
+
+ <application>
+
+ <meta-data
+ android:name="org.godotengine.plugin.v1.GodotPayment"
+ android:value="org.godotengine.godot.plugin.payment.GodotPayment" />
+
+ </application>
+</manifest>
diff --git a/platform/android/java/lib/src/org/godotengine/godot/GodotPaymentV3.java b/platform/android/java/plugins/godotpayment/src/main/java/org/godotengine/godot/plugin/payment/GodotPayment.java
index 93265d509f..6317de9a6e 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/GodotPaymentV3.java
+++ b/platform/android/java/plugins/godotpayment/src/main/java/org/godotengine/godot/plugin/payment/GodotPayment.java
@@ -1,5 +1,5 @@
/*************************************************************************/
-/* GodotPaymentV3.java */
+/* GodotPayment.java */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
@@ -28,20 +28,24 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-package org.godotengine.godot;
+package org.godotengine.godot.plugin.payment;
-import android.app.Activity;
+import android.support.annotation.NonNull;
import android.util.Log;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
+import org.godotengine.godot.Dictionary;
+import org.godotengine.godot.Godot;
+import org.godotengine.godot.GodotLib;
+import org.godotengine.godot.payments.GodotPaymentInterface;
import org.godotengine.godot.payments.PaymentsManager;
+import org.godotengine.godot.plugin.GodotPlugin;
import org.json.JSONException;
import org.json.JSONObject;
-public class GodotPaymentV3 extends Godot.SingletonBase {
+public class GodotPayment extends GodotPlugin implements GodotPaymentInterface {
- private Godot activity;
private Integer purchaseCallbackId = 0;
private String accessToken;
private String purchaseValidationUrlPrefix;
@@ -49,8 +53,16 @@ public class GodotPaymentV3 extends Godot.SingletonBase {
private PaymentsManager mPaymentManager;
private Dictionary mSkuDetails = new Dictionary();
+ public GodotPayment(Godot godot) {
+ super(godot);
+ onGLRegisterPluginWithGodotNative();
+ mPaymentManager = godot.getPaymentsManager();
+ mPaymentManager.setBaseSingleton(this);
+ }
+
+ @Override
public void purchase(final String sku, final String transactionId) {
- activity.runOnUiThread(new Runnable() {
+ runOnUiThread(new Runnable() {
@Override
public void run() {
mPaymentManager.requestPurchase(sku, transactionId);
@@ -58,21 +70,9 @@ public class GodotPaymentV3 extends Godot.SingletonBase {
});
}
- static public Godot.SingletonBase initialize(Activity p_activity) {
-
- return new GodotPaymentV3(p_activity);
- }
-
- public GodotPaymentV3(Activity p_activity) {
-
- registerClass("GodotPayments", new String[] { "purchase", "setPurchaseCallbackId", "setPurchaseValidationUrlPrefix", "setTransactionId", "getSignature", "consumeUnconsumedPurchases", "requestPurchased", "setAutoConsume", "consume", "querySkuDetails", "isConnected" });
- activity = (Godot)p_activity;
- mPaymentManager = activity.getPaymentsManager();
- mPaymentManager.setBaseSingleton(this);
- }
-
+ @Override
public void consumeUnconsumedPurchases() {
- activity.runOnUiThread(new Runnable() {
+ runOnUiThread(new Runnable() {
@Override
public void run() {
mPaymentManager.consumeUnconsumedPurchases();
@@ -82,74 +82,91 @@ public class GodotPaymentV3 extends Godot.SingletonBase {
private String signature;
+ @Override
public String getSignature() {
return this.signature;
}
+ @Override
public void callbackSuccess(String ticket, String signature, String sku) {
GodotLib.calldeferred(purchaseCallbackId, "purchase_success", new Object[] { ticket, signature, sku });
}
+ @Override
public void callbackSuccessProductMassConsumed(String ticket, String signature, String sku) {
Log.d(this.getClass().getName(), "callbackSuccessProductMassConsumed > " + ticket + "," + signature + "," + sku);
GodotLib.calldeferred(purchaseCallbackId, "consume_success", new Object[] { ticket, signature, sku });
}
+ @Override
public void callbackSuccessNoUnconsumedPurchases() {
GodotLib.calldeferred(purchaseCallbackId, "consume_not_required", new Object[] {});
}
+ @Override
public void callbackFailConsume(String message) {
GodotLib.calldeferred(purchaseCallbackId, "consume_fail", new Object[] { message });
}
+ @Override
public void callbackFail(String message) {
GodotLib.calldeferred(purchaseCallbackId, "purchase_fail", new Object[] { message });
}
+ @Override
public void callbackCancel() {
GodotLib.calldeferred(purchaseCallbackId, "purchase_cancel", new Object[] {});
}
+ @Override
public void callbackAlreadyOwned(String sku) {
GodotLib.calldeferred(purchaseCallbackId, "purchase_owned", new Object[] { sku });
}
+ @Override
public int getPurchaseCallbackId() {
return purchaseCallbackId;
}
+ @Override
public void setPurchaseCallbackId(int purchaseCallbackId) {
this.purchaseCallbackId = purchaseCallbackId;
}
+ @Override
public String getPurchaseValidationUrlPrefix() {
return this.purchaseValidationUrlPrefix;
}
+ @Override
public void setPurchaseValidationUrlPrefix(String url) {
this.purchaseValidationUrlPrefix = url;
}
+ @Override
public String getAccessToken() {
return accessToken;
}
+ @Override
public void setAccessToken(String accessToken) {
this.accessToken = accessToken;
}
+ @Override
public void setTransactionId(String transactionId) {
this.transactionId = transactionId;
}
+ @Override
public String getTransactionId() {
return this.transactionId;
}
// request purchased items are not consumed
+ @Override
public void requestPurchased() {
- activity.runOnUiThread(new Runnable() {
+ runOnUiThread(new Runnable() {
@Override
public void run() {
mPaymentManager.requestPurchased();
@@ -158,34 +175,41 @@ public class GodotPaymentV3 extends Godot.SingletonBase {
}
// callback for requestPurchased()
+ @Override
public void callbackPurchased(String receipt, String signature, String sku) {
GodotLib.calldeferred(purchaseCallbackId, "has_purchased", new Object[] { receipt, signature, sku });
}
+ @Override
public void callbackDisconnected() {
GodotLib.calldeferred(purchaseCallbackId, "iap_disconnected", new Object[] {});
}
+ @Override
public void callbackConnected() {
GodotLib.calldeferred(purchaseCallbackId, "iap_connected", new Object[] {});
}
// true if connected, false otherwise
+ @Override
public boolean isConnected() {
return mPaymentManager.isConnected();
}
// consume item automatically after purchase. default is true.
+ @Override
public void setAutoConsume(boolean autoConsume) {
mPaymentManager.setAutoConsume(autoConsume);
}
// consume a specific item
+ @Override
public void consume(String sku) {
mPaymentManager.consume(sku);
}
// query in app item detail info
+ @Override
public void querySkuDetails(String[] list) {
List<String> nKeys = Arrays.asList(list);
List<String> cKeys = Arrays.asList(mSkuDetails.get_keys());
@@ -202,6 +226,7 @@ public class GodotPaymentV3 extends Godot.SingletonBase {
}
}
+ @Override
public void addSkuDetail(String itemJson) {
JSONObject o = null;
try {
@@ -220,11 +245,25 @@ public class GodotPaymentV3 extends Godot.SingletonBase {
}
}
+ @Override
public void completeSkuDetail() {
GodotLib.calldeferred(purchaseCallbackId, "sku_details_complete", new Object[] { mSkuDetails });
}
+ @Override
public void errorSkuDetail(String errorMessage) {
GodotLib.calldeferred(purchaseCallbackId, "sku_details_error", new Object[] { errorMessage });
}
+
+ @NonNull
+ @Override
+ public String getPluginName() {
+ return "GodotPayment";
+ }
+
+ @NonNull
+ @Override
+ public List<String> getPluginMethods() {
+ return Arrays.asList("purchase", "setPurchaseCallbackId", "setPurchaseValidationUrlPrefix", "setTransactionId", "getSignature", "consumeUnconsumedPurchases", "requestPurchased", "setAutoConsume", "consume", "querySkuDetails", "isConnected");
+ }
}
diff --git a/platform/android/java/settings.gradle b/platform/android/java/settings.gradle
index f6921c70aa..9536d3de6d 100644
--- a/platform/android/java/settings.gradle
+++ b/platform/android/java/settings.gradle
@@ -3,3 +3,4 @@ rootProject.name = "Godot"
include ':app'
include ':lib'
+include ':plugins:godotpayment'
diff --git a/platform/android/java_godot_lib_jni.cpp b/platform/android/java_godot_lib_jni.cpp
index 022d9700d5..0b1d070441 100644
--- a/platform/android/java_godot_lib_jni.cpp
+++ b/platform/android/java_godot_lib_jni.cpp
@@ -33,14 +33,15 @@
#include "java_godot_wrapper.h"
#include "android/asset_manager_jni.h"
+#include "android_keys_utils.h"
#include "api/java_class_wrapper.h"
#include "audio_driver_jandroid.h"
#include "core/engine.h"
-#include "core/os/keyboard.h"
#include "core/project_settings.h"
#include "dir_access_jandroid.h"
#include "file_access_android.h"
#include "file_access_jandroid.h"
+#include "jni_utils.h"
#include "main/input_default.h"
#include "main/main.h"
#include "net_socket_android.h"
@@ -54,551 +55,6 @@ static OS_Android *os_android = NULL;
static GodotJavaWrapper *godot_java = NULL;
static GodotIOJavaWrapper *godot_io_java = NULL;
-struct jvalret {
-
- jobject obj;
- jvalue val;
- jvalret() { obj = NULL; }
-};
-
-jvalret _variant_to_jvalue(JNIEnv *env, Variant::Type p_type, const Variant *p_arg, bool force_jobject = false) {
-
- jvalret v;
-
- switch (p_type) {
-
- case Variant::BOOL: {
-
- if (force_jobject) {
- jclass bclass = env->FindClass("java/lang/Boolean");
- jmethodID ctor = env->GetMethodID(bclass, "<init>", "(Z)V");
- jvalue val;
- val.z = (bool)(*p_arg);
- jobject obj = env->NewObjectA(bclass, ctor, &val);
- v.val.l = obj;
- v.obj = obj;
- env->DeleteLocalRef(bclass);
- } else {
- v.val.z = *p_arg;
- };
- } break;
- case Variant::INT: {
-
- if (force_jobject) {
-
- jclass bclass = env->FindClass("java/lang/Integer");
- jmethodID ctor = env->GetMethodID(bclass, "<init>", "(I)V");
- jvalue val;
- val.i = (int)(*p_arg);
- jobject obj = env->NewObjectA(bclass, ctor, &val);
- v.val.l = obj;
- v.obj = obj;
- env->DeleteLocalRef(bclass);
-
- } else {
- v.val.i = *p_arg;
- };
- } break;
- case Variant::FLOAT: {
-
- if (force_jobject) {
-
- jclass bclass = env->FindClass("java/lang/Double");
- jmethodID ctor = env->GetMethodID(bclass, "<init>", "(D)V");
- jvalue val;
- val.d = (double)(*p_arg);
- jobject obj = env->NewObjectA(bclass, ctor, &val);
- v.val.l = obj;
- v.obj = obj;
- env->DeleteLocalRef(bclass);
-
- } else {
- v.val.f = *p_arg;
- };
- } break;
- case Variant::STRING: {
-
- String s = *p_arg;
- jstring jStr = env->NewStringUTF(s.utf8().get_data());
- v.val.l = jStr;
- v.obj = jStr;
- } break;
- case Variant::PACKED_STRING_ARRAY: {
-
- Vector<String> sarray = *p_arg;
- jobjectArray arr = env->NewObjectArray(sarray.size(), env->FindClass("java/lang/String"), env->NewStringUTF(""));
-
- for (int j = 0; j < sarray.size(); j++) {
-
- jstring str = env->NewStringUTF(sarray[j].utf8().get_data());
- env->SetObjectArrayElement(arr, j, str);
- env->DeleteLocalRef(str);
- }
- v.val.l = arr;
- v.obj = arr;
-
- } break;
-
- case Variant::DICTIONARY: {
-
- Dictionary dict = *p_arg;
- jclass dclass = env->FindClass("org/godotengine/godot/Dictionary");
- jmethodID ctor = env->GetMethodID(dclass, "<init>", "()V");
- jobject jdict = env->NewObject(dclass, ctor);
-
- Array keys = dict.keys();
-
- jobjectArray jkeys = env->NewObjectArray(keys.size(), env->FindClass("java/lang/String"), env->NewStringUTF(""));
- for (int j = 0; j < keys.size(); j++) {
- jstring str = env->NewStringUTF(String(keys[j]).utf8().get_data());
- env->SetObjectArrayElement(jkeys, j, str);
- env->DeleteLocalRef(str);
- };
-
- jmethodID set_keys = env->GetMethodID(dclass, "set_keys", "([Ljava/lang/String;)V");
- jvalue val;
- val.l = jkeys;
- env->CallVoidMethodA(jdict, set_keys, &val);
- env->DeleteLocalRef(jkeys);
-
- jobjectArray jvalues = env->NewObjectArray(keys.size(), env->FindClass("java/lang/Object"), NULL);
-
- for (int j = 0; j < keys.size(); j++) {
- Variant var = dict[keys[j]];
- jvalret v = _variant_to_jvalue(env, var.get_type(), &var, true);
- env->SetObjectArrayElement(jvalues, j, v.val.l);
- if (v.obj) {
- env->DeleteLocalRef(v.obj);
- }
- };
-
- jmethodID set_values = env->GetMethodID(dclass, "set_values", "([Ljava/lang/Object;)V");
- val.l = jvalues;
- env->CallVoidMethodA(jdict, set_values, &val);
- env->DeleteLocalRef(jvalues);
- env->DeleteLocalRef(dclass);
-
- v.val.l = jdict;
- v.obj = jdict;
- } break;
-
- case Variant::PACKED_INT32_ARRAY: {
-
- Vector<int> array = *p_arg;
- jintArray arr = env->NewIntArray(array.size());
- const int *r = array.ptr();
- env->SetIntArrayRegion(arr, 0, array.size(), r.ptr());
- v.val.l = arr;
- v.obj = arr;
-
- } break;
- case Variant::PACKED_BYTE_ARRAY: {
- Vector<uint8_t> array = *p_arg;
- jbyteArray arr = env->NewByteArray(array.size());
- const uint8_t *r = array.ptr();
- env->SetByteArrayRegion(arr, 0, array.size(), reinterpret_cast<const signed char *>(r.ptr()));
- v.val.l = arr;
- v.obj = arr;
-
- } break;
- case Variant::PACKED_FLOAT32_ARRAY: {
-
- Vector<float> array = *p_arg;
- jfloatArray arr = env->NewFloatArray(array.size());
- const float *r = array.ptr();
- env->SetFloatArrayRegion(arr, 0, array.size(), r.ptr());
- v.val.l = arr;
- v.obj = arr;
-
- } break;
-#ifndef _MSC_VER
-#warning This is missing 64 bits arrays, I have no idea how to do it in JNI
-#endif
-
- default: {
-
- v.val.i = 0;
- } break;
- }
- return v;
-}
-
-String _get_class_name(JNIEnv *env, jclass cls, bool *array) {
-
- jclass cclass = env->FindClass("java/lang/Class");
- jmethodID getName = env->GetMethodID(cclass, "getName", "()Ljava/lang/String;");
- jstring clsName = (jstring)env->CallObjectMethod(cls, getName);
-
- if (array) {
- jmethodID isArray = env->GetMethodID(cclass, "isArray", "()Z");
- jboolean isarr = env->CallBooleanMethod(cls, isArray);
- (*array) = isarr ? true : false;
- }
- String name = jstring_to_string(clsName, env);
- env->DeleteLocalRef(clsName);
-
- return name;
-}
-
-Variant _jobject_to_variant(JNIEnv *env, jobject obj) {
-
- if (obj == NULL) {
- return Variant();
- }
-
- jclass c = env->GetObjectClass(obj);
- bool array;
- String name = _get_class_name(env, c, &array);
-
- if (name == "java.lang.String") {
-
- return jstring_to_string((jstring)obj, env);
- };
-
- if (name == "[Ljava.lang.String;") {
-
- jobjectArray arr = (jobjectArray)obj;
- int stringCount = env->GetArrayLength(arr);
- Vector<String> sarr;
-
- for (int i = 0; i < stringCount; i++) {
- jstring string = (jstring)env->GetObjectArrayElement(arr, i);
- sarr.push_back(jstring_to_string(string, env));
- env->DeleteLocalRef(string);
- }
-
- return sarr;
- };
-
- if (name == "java.lang.Boolean") {
-
- jmethodID boolValue = env->GetMethodID(c, "booleanValue", "()Z");
- bool ret = env->CallBooleanMethod(obj, boolValue);
- return ret;
- };
-
- if (name == "java.lang.Integer" || name == "java.lang.Long") {
-
- jclass nclass = env->FindClass("java/lang/Number");
- jmethodID longValue = env->GetMethodID(nclass, "longValue", "()J");
- jlong ret = env->CallLongMethod(obj, longValue);
- return ret;
- };
-
- if (name == "[I") {
-
- jintArray arr = (jintArray)obj;
- int fCount = env->GetArrayLength(arr);
- Vector<int> sarr;
- sarr.resize(fCount);
-
- int *w = sarr.ptrw();
- env->GetIntArrayRegion(arr, 0, fCount, w.ptr());
- w.release();
- return sarr;
- };
-
- if (name == "[B") {
-
- jbyteArray arr = (jbyteArray)obj;
- int fCount = env->GetArrayLength(arr);
- Vector<uint8_t> sarr;
- sarr.resize(fCount);
-
- uint8_t *w = sarr.ptrw();
- env->GetByteArrayRegion(arr, 0, fCount, reinterpret_cast<signed char *>(w.ptr()));
- w.release();
- return sarr;
- };
-
- if (name == "java.lang.Float" || name == "java.lang.Double") {
-
- jclass nclass = env->FindClass("java/lang/Number");
- jmethodID doubleValue = env->GetMethodID(nclass, "doubleValue", "()D");
- double ret = env->CallDoubleMethod(obj, doubleValue);
- return ret;
- };
-
- if (name == "[D") {
-
- jdoubleArray arr = (jdoubleArray)obj;
- int fCount = env->GetArrayLength(arr);
- PackedFloat32Array sarr;
- sarr.resize(fCount);
-
- real_t *w = sarr.ptrw();
-
- for (int i = 0; i < fCount; i++) {
-
- double n;
- env->GetDoubleArrayRegion(arr, i, 1, &n);
- w.ptr()[i] = n;
- };
- return sarr;
- };
-
- if (name == "[F") {
-
- jfloatArray arr = (jfloatArray)obj;
- int fCount = env->GetArrayLength(arr);
- PackedFloat32Array sarr;
- sarr.resize(fCount);
-
- real_t *w = sarr.ptrw();
-
- for (int i = 0; i < fCount; i++) {
-
- float n;
- env->GetFloatArrayRegion(arr, i, 1, &n);
- w.ptr()[i] = n;
- };
- return sarr;
- };
-
- if (name == "[Ljava.lang.Object;") {
-
- jobjectArray arr = (jobjectArray)obj;
- int objCount = env->GetArrayLength(arr);
- Array varr;
-
- for (int i = 0; i < objCount; i++) {
- jobject jobj = env->GetObjectArrayElement(arr, i);
- Variant v = _jobject_to_variant(env, jobj);
- varr.push_back(v);
- env->DeleteLocalRef(jobj);
- }
-
- return varr;
- };
-
- if (name == "java.util.HashMap" || name == "org.godotengine.godot.Dictionary") {
-
- Dictionary ret;
- jclass oclass = c;
- jmethodID get_keys = env->GetMethodID(oclass, "get_keys", "()[Ljava/lang/String;");
- jobjectArray arr = (jobjectArray)env->CallObjectMethod(obj, get_keys);
-
- PackedStringArray keys = _jobject_to_variant(env, arr);
- env->DeleteLocalRef(arr);
-
- jmethodID get_values = env->GetMethodID(oclass, "get_values", "()[Ljava/lang/Object;");
- arr = (jobjectArray)env->CallObjectMethod(obj, get_values);
-
- Array vals = _jobject_to_variant(env, arr);
- env->DeleteLocalRef(arr);
-
- for (int i = 0; i < keys.size(); i++) {
-
- ret[keys[i]] = vals[i];
- };
-
- return ret;
- };
-
- env->DeleteLocalRef(c);
-
- return Variant();
-}
-
-class JNISingleton : public Object {
-
- GDCLASS(JNISingleton, Object);
-
- struct MethodData {
-
- jmethodID method;
- Variant::Type ret_type;
- Vector<Variant::Type> argtypes;
- };
-
- jobject instance;
- Map<StringName, MethodData> method_map;
-
-public:
- virtual Variant call(const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error) {
-
- ERR_FAIL_COND_V(!instance, Variant());
-
- r_error.error = Callable::CallError::CALL_OK;
-
- Map<StringName, MethodData>::Element *E = method_map.find(p_method);
- if (!E) {
-
- r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD;
- return Variant();
- }
-
- int ac = E->get().argtypes.size();
- if (ac < p_argcount) {
-
- r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS;
- r_error.argument = ac;
- return Variant();
- }
-
- if (ac > p_argcount) {
-
- r_error.error = Callable::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS;
- r_error.argument = ac;
- return Variant();
- }
-
- for (int i = 0; i < p_argcount; i++) {
-
- if (!Variant::can_convert(p_args[i]->get_type(), E->get().argtypes[i])) {
-
- r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
- r_error.argument = i;
- r_error.expected = E->get().argtypes[i];
- }
- }
-
- jvalue *v = NULL;
-
- if (p_argcount) {
-
- v = (jvalue *)alloca(sizeof(jvalue) * p_argcount);
- }
-
- JNIEnv *env = ThreadAndroid::get_env();
-
- int res = env->PushLocalFrame(16);
-
- ERR_FAIL_COND_V(res != 0, Variant());
-
- List<jobject> to_erase;
- for (int i = 0; i < p_argcount; i++) {
-
- jvalret vr = _variant_to_jvalue(env, E->get().argtypes[i], p_args[i]);
- v[i] = vr.val;
- if (vr.obj)
- to_erase.push_back(vr.obj);
- }
-
- Variant ret;
-
- switch (E->get().ret_type) {
-
- case Variant::NIL: {
-
- env->CallVoidMethodA(instance, E->get().method, v);
- } break;
- case Variant::BOOL: {
-
- ret = env->CallBooleanMethodA(instance, E->get().method, v) == JNI_TRUE;
- } break;
- case Variant::INT: {
-
- ret = env->CallIntMethodA(instance, E->get().method, v);
- } break;
- case Variant::FLOAT: {
-
- ret = env->CallFloatMethodA(instance, E->get().method, v);
- } break;
- case Variant::STRING: {
-
- jobject o = env->CallObjectMethodA(instance, E->get().method, v);
- ret = jstring_to_string((jstring)o, env);
- env->DeleteLocalRef(o);
- } break;
- case Variant::PACKED_STRING_ARRAY: {
-
- jobjectArray arr = (jobjectArray)env->CallObjectMethodA(instance, E->get().method, v);
-
- ret = _jobject_to_variant(env, arr);
-
- env->DeleteLocalRef(arr);
- } break;
- case Variant::PACKED_INT32_ARRAY: {
-
- jintArray arr = (jintArray)env->CallObjectMethodA(instance, E->get().method, v);
-
- int fCount = env->GetArrayLength(arr);
- Vector<int> sarr;
- sarr.resize(fCount);
-
- int *w = sarr.ptrw();
- env->GetIntArrayRegion(arr, 0, fCount, w.ptr());
- w.release();
- ret = sarr;
- env->DeleteLocalRef(arr);
- } break;
- case Variant::PACKED_FLOAT32_ARRAY: {
-
- jfloatArray arr = (jfloatArray)env->CallObjectMethodA(instance, E->get().method, v);
-
- int fCount = env->GetArrayLength(arr);
- Vector<float> sarr;
- sarr.resize(fCount);
-
- float *w = sarr.ptrw();
- env->GetFloatArrayRegion(arr, 0, fCount, w.ptr());
- w.release();
- ret = sarr;
- env->DeleteLocalRef(arr);
- } break;
-
-#ifndef _MSC_VER
-#warning This is missing 64 bits arrays, I have no idea how to do it in JNI
-#endif
- case Variant::DICTIONARY: {
-
- jobject obj = env->CallObjectMethodA(instance, E->get().method, v);
- ret = _jobject_to_variant(env, obj);
- env->DeleteLocalRef(obj);
-
- } break;
- default: {
-
- env->PopLocalFrame(NULL);
- ERR_FAIL_V(Variant());
- } break;
- }
-
- while (to_erase.size()) {
- env->DeleteLocalRef(to_erase.front()->get());
- to_erase.pop_front();
- }
-
- env->PopLocalFrame(NULL);
-
- return ret;
- }
-
- jobject get_instance() const {
-
- return instance;
- }
- void set_instance(jobject p_instance) {
-
- instance = p_instance;
- }
-
- void add_method(const StringName &p_name, jmethodID p_method, const Vector<Variant::Type> &p_args, Variant::Type p_ret_type) {
-
- MethodData md;
- md.method = p_method;
- md.argtypes = p_args;
- md.ret_type = p_ret_type;
- method_map[p_name] = md;
- }
-
- JNISingleton() {
- instance = NULL;
- }
-};
-
-struct TST {
-
- int a;
- TST() {
-
- a = 5;
- }
-};
-
-TST tst;
-
static bool initialized = false;
static int step = 0;
@@ -607,20 +63,16 @@ static Vector3 accelerometer;
static Vector3 gravity;
static Vector3 magnetometer;
static Vector3 gyroscope;
-static HashMap<String, JNISingleton *> jni_singletons;
-// virtual Error native_video_play(String p_path);
-// virtual bool native_video_is_playing();
-// virtual void native_video_pause();
-// virtual void native_video_stop();
+extern "C" {
-JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_setVirtualKeyboardHeight(JNIEnv *env, jobject obj, jint p_height) {
+JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_setVirtualKeyboardHeight(JNIEnv *env, jclass clazz, jint p_height) {
if (godot_io_java) {
godot_io_java->set_vk_height(p_height);
}
}
-JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_initialize(JNIEnv *env, jobject obj, jobject activity, jobject p_asset_manager, jboolean p_use_apk_expansion) {
+JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_initialize(JNIEnv *env, jclass clazz, jobject activity, jobject p_asset_manager, jboolean p_use_apk_expansion) {
initialized = true;
@@ -653,7 +105,7 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_initialize(JNIEnv *en
godot_java->on_video_init(env);
}
-JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_ondestroy(JNIEnv *env, jobject obj, jobject activity) {
+JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_ondestroy(JNIEnv *env, jclass clazz, jobject activity) {
// lets cleanup
if (godot_io_java) {
delete godot_io_java;
@@ -666,47 +118,7 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_ondestroy(JNIEnv *env
}
}
-static void _initialize_java_modules() {
-
- if (!ProjectSettings::get_singleton()->has_setting("android/modules")) {
- return;
- }
-
- String modules = ProjectSettings::get_singleton()->get("android/modules");
- modules = modules.strip_edges();
- if (modules == String()) {
- return;
- }
- Vector<String> mods = modules.split(",", false);
-
- if (mods.size()) {
- jobject cls = godot_java->get_class_loader();
-
- // TODO create wrapper for class loader
-
- JNIEnv *env = ThreadAndroid::get_env();
- jclass classLoader = env->FindClass("java/lang/ClassLoader");
- jmethodID findClass = env->GetMethodID(classLoader, "loadClass", "(Ljava/lang/String;)Ljava/lang/Class;");
-
- for (int i = 0; i < mods.size(); i++) {
-
- String m = mods[i];
-
- print_line("Loading Android module: " + m);
- jstring strClassName = env->NewStringUTF(m.utf8().get_data());
- jclass singletonClass = (jclass)env->CallObjectMethod(cls, findClass, strClassName);
- ERR_CONTINUE_MSG(!singletonClass, "Couldn't find singleton for class: " + m + ".");
-
- jmethodID initialize = env->GetStaticMethodID(singletonClass, "initialize", "(Landroid/app/Activity;)Lorg/godotengine/godot/Godot$SingletonBase;");
- ERR_CONTINUE_MSG(!initialize, "Couldn't find proper initialize function 'public static Godot.SingletonBase Class::initialize(Activity p_activity)' initializer for singleton class: " + m + ".");
-
- jobject obj = env->CallStaticObjectMethod(singletonClass, initialize, godot_java->get_activity());
- env->NewGlobalRef(obj);
- }
- }
-}
-
-JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_setup(JNIEnv *env, jobject obj, jobjectArray p_cmdline) {
+JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_setup(JNIEnv *env, jclass clazz, jobjectArray p_cmdline) {
ThreadAndroid::setup_thread();
const char **cmdline = NULL;
@@ -746,16 +158,15 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_setup(JNIEnv *env, jo
}
java_class_wrapper = memnew(JavaClassWrapper(godot_java->get_activity()));
- _initialize_java_modules();
}
-JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_resize(JNIEnv *env, jobject obj, jint width, jint height) {
+JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_resize(JNIEnv *env, jclass clazz, jint width, jint height) {
if (os_android)
os_android->set_display_size(Size2(width, height));
}
-JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_newcontext(JNIEnv *env, jobject obj, bool p_32_bits) {
+JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_newcontext(JNIEnv *env, jclass clazz, jboolean p_32_bits) {
if (os_android) {
if (step == 0) {
@@ -770,14 +181,14 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_newcontext(JNIEnv *en
}
}
-JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_back(JNIEnv *env, jobject obj) {
+JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_back(JNIEnv *env, jclass clazz) {
if (step == 0)
return;
os_android->main_loop_request_go_back();
}
-JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_step(JNIEnv *env, jobject obj) {
+JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_step(JNIEnv *env, jclass clazz) {
if (step == -1)
return;
@@ -796,6 +207,7 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_step(JNIEnv *env, job
}
os_android->main_loop_begin();
+ godot_java->on_gl_godot_main_loop_started(env);
++step;
}
@@ -810,7 +222,7 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_step(JNIEnv *env, job
}
}
-JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_touch(JNIEnv *env, jobject obj, jint ev, jint pointer, jint count, jintArray positions) {
+JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_touch(JNIEnv *env, jclass clazz, jint ev, jint pointer, jint count, jintArray positions) {
if (step == 0)
return;
@@ -834,282 +246,28 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_touch(JNIEnv *env, jo
*/
}
-JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_hover(JNIEnv *env, jobject obj, jint p_type, jint p_x, jint p_y) {
+JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_hover(JNIEnv *env, jclass clazz, jint p_type, jint p_x, jint p_y) {
if (step == 0)
return;
os_android->process_hover(p_type, Point2(p_x, p_y));
}
-JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_doubletap(JNIEnv *env, jobject obj, jint p_x, jint p_y) {
+JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_doubletap(JNIEnv *env, jclass clazz, jint p_x, jint p_y) {
if (step == 0)
return;
os_android->process_double_tap(Point2(p_x, p_y));
}
-JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_scroll(JNIEnv *env, jobject obj, jint p_x, jint p_y) {
+JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_scroll(JNIEnv *env, jclass clazz, jint p_x, jint p_y) {
if (step == 0)
return;
os_android->process_scroll(Point2(p_x, p_y));
}
-/*
- * Android Key codes.
- */
-enum {
- AKEYCODE_UNKNOWN = 0,
- AKEYCODE_SOFT_LEFT = 1,
- AKEYCODE_SOFT_RIGHT = 2,
- AKEYCODE_HOME = 3,
- AKEYCODE_BACK = 4,
- AKEYCODE_CALL = 5,
- AKEYCODE_ENDCALL = 6,
- AKEYCODE_0 = 7,
- AKEYCODE_1 = 8,
- AKEYCODE_2 = 9,
- AKEYCODE_3 = 10,
- AKEYCODE_4 = 11,
- AKEYCODE_5 = 12,
- AKEYCODE_6 = 13,
- AKEYCODE_7 = 14,
- AKEYCODE_8 = 15,
- AKEYCODE_9 = 16,
- AKEYCODE_STAR = 17,
- AKEYCODE_POUND = 18,
- AKEYCODE_DPAD_UP = 19,
- AKEYCODE_DPAD_DOWN = 20,
- AKEYCODE_DPAD_LEFT = 21,
- AKEYCODE_DPAD_RIGHT = 22,
- AKEYCODE_DPAD_CENTER = 23,
- AKEYCODE_VOLUME_UP = 24,
- AKEYCODE_VOLUME_DOWN = 25,
- AKEYCODE_POWER = 26,
- AKEYCODE_CAMERA = 27,
- AKEYCODE_CLEAR = 28,
- AKEYCODE_A = 29,
- AKEYCODE_B = 30,
- AKEYCODE_C = 31,
- AKEYCODE_D = 32,
- AKEYCODE_E = 33,
- AKEYCODE_F = 34,
- AKEYCODE_G = 35,
- AKEYCODE_H = 36,
- AKEYCODE_I = 37,
- AKEYCODE_J = 38,
- AKEYCODE_K = 39,
- AKEYCODE_L = 40,
- AKEYCODE_M = 41,
- AKEYCODE_N = 42,
- AKEYCODE_O = 43,
- AKEYCODE_P = 44,
- AKEYCODE_Q = 45,
- AKEYCODE_R = 46,
- AKEYCODE_S = 47,
- AKEYCODE_T = 48,
- AKEYCODE_U = 49,
- AKEYCODE_V = 50,
- AKEYCODE_W = 51,
- AKEYCODE_X = 52,
- AKEYCODE_Y = 53,
- AKEYCODE_Z = 54,
- AKEYCODE_COMMA = 55,
- AKEYCODE_PERIOD = 56,
- AKEYCODE_ALT_LEFT = 57,
- AKEYCODE_ALT_RIGHT = 58,
- AKEYCODE_SHIFT_LEFT = 59,
- AKEYCODE_SHIFT_RIGHT = 60,
- AKEYCODE_TAB = 61,
- AKEYCODE_SPACE = 62,
- AKEYCODE_SYM = 63,
- AKEYCODE_EXPLORER = 64,
- AKEYCODE_ENVELOPE = 65,
- AKEYCODE_ENTER = 66,
- AKEYCODE_DEL = 67,
- AKEYCODE_GRAVE = 68,
- AKEYCODE_MINUS = 69,
- AKEYCODE_EQUALS = 70,
- AKEYCODE_LEFT_BRACKET = 71,
- AKEYCODE_RIGHT_BRACKET = 72,
- AKEYCODE_BACKSLASH = 73,
- AKEYCODE_SEMICOLON = 74,
- AKEYCODE_APOSTROPHE = 75,
- AKEYCODE_SLASH = 76,
- AKEYCODE_AT = 77,
- AKEYCODE_NUM = 78,
- AKEYCODE_HEADSETHOOK = 79,
- AKEYCODE_FOCUS = 80, // *Camera* focus
- AKEYCODE_PLUS = 81,
- AKEYCODE_MENU = 82,
- AKEYCODE_NOTIFICATION = 83,
- AKEYCODE_SEARCH = 84,
- AKEYCODE_MEDIA_PLAY_PAUSE = 85,
- AKEYCODE_MEDIA_STOP = 86,
- AKEYCODE_MEDIA_NEXT = 87,
- AKEYCODE_MEDIA_PREVIOUS = 88,
- AKEYCODE_MEDIA_REWIND = 89,
- AKEYCODE_MEDIA_FAST_FORWARD = 90,
- AKEYCODE_MUTE = 91,
- AKEYCODE_PAGE_UP = 92,
- AKEYCODE_PAGE_DOWN = 93,
- AKEYCODE_PICTSYMBOLS = 94,
- AKEYCODE_SWITCH_CHARSET = 95,
- AKEYCODE_BUTTON_A = 96,
- AKEYCODE_BUTTON_B = 97,
- AKEYCODE_BUTTON_C = 98,
- AKEYCODE_BUTTON_X = 99,
- AKEYCODE_BUTTON_Y = 100,
- AKEYCODE_BUTTON_Z = 101,
- AKEYCODE_BUTTON_L1 = 102,
- AKEYCODE_BUTTON_R1 = 103,
- AKEYCODE_BUTTON_L2 = 104,
- AKEYCODE_BUTTON_R2 = 105,
- AKEYCODE_BUTTON_THUMBL = 106,
- AKEYCODE_BUTTON_THUMBR = 107,
- AKEYCODE_BUTTON_START = 108,
- AKEYCODE_BUTTON_SELECT = 109,
- AKEYCODE_BUTTON_MODE = 110,
-
- // NOTE: If you add a new keycode here you must also add it to several other files.
- // Refer to frameworks/base/core/java/android/view/KeyEvent.java for the full list.
-};
-
-struct _WinTranslatePair {
-
- unsigned int keysym;
- unsigned int keycode;
-};
-
-static _WinTranslatePair _ak_to_keycode[] = {
- { KEY_TAB, AKEYCODE_TAB },
- { KEY_ENTER, AKEYCODE_ENTER },
- { KEY_SHIFT, AKEYCODE_SHIFT_LEFT },
- { KEY_SHIFT, AKEYCODE_SHIFT_RIGHT },
- { KEY_ALT, AKEYCODE_ALT_LEFT },
- { KEY_ALT, AKEYCODE_ALT_RIGHT },
- { KEY_MENU, AKEYCODE_MENU },
- { KEY_PAUSE, AKEYCODE_MEDIA_PLAY_PAUSE },
- { KEY_ESCAPE, AKEYCODE_BACK },
- { KEY_SPACE, AKEYCODE_SPACE },
- { KEY_PAGEUP, AKEYCODE_PAGE_UP },
- { KEY_PAGEDOWN, AKEYCODE_PAGE_DOWN },
- { KEY_HOME, AKEYCODE_HOME }, //(0x24)
- { KEY_LEFT, AKEYCODE_DPAD_LEFT },
- { KEY_UP, AKEYCODE_DPAD_UP },
- { KEY_RIGHT, AKEYCODE_DPAD_RIGHT },
- { KEY_DOWN, AKEYCODE_DPAD_DOWN },
- { KEY_PERIODCENTERED, AKEYCODE_DPAD_CENTER },
- { KEY_BACKSPACE, AKEYCODE_DEL },
- { KEY_0, AKEYCODE_0 }, ////0 key
- { KEY_1, AKEYCODE_1 }, ////1 key
- { KEY_2, AKEYCODE_2 }, ////2 key
- { KEY_3, AKEYCODE_3 }, ////3 key
- { KEY_4, AKEYCODE_4 }, ////4 key
- { KEY_5, AKEYCODE_5 }, ////5 key
- { KEY_6, AKEYCODE_6 }, ////6 key
- { KEY_7, AKEYCODE_7 }, ////7 key
- { KEY_8, AKEYCODE_8 }, ////8 key
- { KEY_9, AKEYCODE_9 }, ////9 key
- { KEY_A, AKEYCODE_A }, ////A key
- { KEY_B, AKEYCODE_B }, ////B key
- { KEY_C, AKEYCODE_C }, ////C key
- { KEY_D, AKEYCODE_D }, ////D key
- { KEY_E, AKEYCODE_E }, ////E key
- { KEY_F, AKEYCODE_F }, ////F key
- { KEY_G, AKEYCODE_G }, ////G key
- { KEY_H, AKEYCODE_H }, ////H key
- { KEY_I, AKEYCODE_I }, ////I key
- { KEY_J, AKEYCODE_J }, ////J key
- { KEY_K, AKEYCODE_K }, ////K key
- { KEY_L, AKEYCODE_L }, ////L key
- { KEY_M, AKEYCODE_M }, ////M key
- { KEY_N, AKEYCODE_N }, ////N key
- { KEY_O, AKEYCODE_O }, ////O key
- { KEY_P, AKEYCODE_P }, ////P key
- { KEY_Q, AKEYCODE_Q }, ////Q key
- { KEY_R, AKEYCODE_R }, ////R key
- { KEY_S, AKEYCODE_S }, ////S key
- { KEY_T, AKEYCODE_T }, ////T key
- { KEY_U, AKEYCODE_U }, ////U key
- { KEY_V, AKEYCODE_V }, ////V key
- { KEY_W, AKEYCODE_W }, ////W key
- { KEY_X, AKEYCODE_X }, ////X key
- { KEY_Y, AKEYCODE_Y }, ////Y key
- { KEY_Z, AKEYCODE_Z }, ////Z key
- { KEY_HOMEPAGE, AKEYCODE_EXPLORER },
- { KEY_LAUNCH0, AKEYCODE_BUTTON_A },
- { KEY_LAUNCH1, AKEYCODE_BUTTON_B },
- { KEY_LAUNCH2, AKEYCODE_BUTTON_C },
- { KEY_LAUNCH3, AKEYCODE_BUTTON_X },
- { KEY_LAUNCH4, AKEYCODE_BUTTON_Y },
- { KEY_LAUNCH5, AKEYCODE_BUTTON_Z },
- { KEY_LAUNCH6, AKEYCODE_BUTTON_L1 },
- { KEY_LAUNCH7, AKEYCODE_BUTTON_R1 },
- { KEY_LAUNCH8, AKEYCODE_BUTTON_L2 },
- { KEY_LAUNCH9, AKEYCODE_BUTTON_R2 },
- { KEY_LAUNCHA, AKEYCODE_BUTTON_THUMBL },
- { KEY_LAUNCHB, AKEYCODE_BUTTON_THUMBR },
- { KEY_LAUNCHC, AKEYCODE_BUTTON_START },
- { KEY_LAUNCHD, AKEYCODE_BUTTON_SELECT },
- { KEY_LAUNCHE, AKEYCODE_BUTTON_MODE },
- { KEY_VOLUMEMUTE, AKEYCODE_MUTE },
- { KEY_VOLUMEDOWN, AKEYCODE_VOLUME_DOWN },
- { KEY_VOLUMEUP, AKEYCODE_VOLUME_UP },
- { KEY_BACK, AKEYCODE_MEDIA_REWIND },
- { KEY_FORWARD, AKEYCODE_MEDIA_FAST_FORWARD },
- { KEY_MEDIANEXT, AKEYCODE_MEDIA_NEXT },
- { KEY_MEDIAPREVIOUS, AKEYCODE_MEDIA_PREVIOUS },
- { KEY_MEDIASTOP, AKEYCODE_MEDIA_STOP },
- { KEY_PLUS, AKEYCODE_PLUS },
- { KEY_EQUAL, AKEYCODE_EQUALS }, // the '+' key
- { KEY_COMMA, AKEYCODE_COMMA }, // the ',' key
- { KEY_MINUS, AKEYCODE_MINUS }, // the '-' key
- { KEY_SLASH, AKEYCODE_SLASH }, // the '/?' key
- { KEY_BACKSLASH, AKEYCODE_BACKSLASH },
- { KEY_BRACKETLEFT, AKEYCODE_LEFT_BRACKET },
- { KEY_BRACKETRIGHT, AKEYCODE_RIGHT_BRACKET },
- { KEY_UNKNOWN, 0 }
-};
-/*
-TODO: map these android key:
- AKEYCODE_SOFT_LEFT = 1,
- AKEYCODE_SOFT_RIGHT = 2,
- AKEYCODE_CALL = 5,
- AKEYCODE_ENDCALL = 6,
- AKEYCODE_STAR = 17,
- AKEYCODE_POUND = 18,
- AKEYCODE_POWER = 26,
- AKEYCODE_CAMERA = 27,
- AKEYCODE_CLEAR = 28,
- AKEYCODE_SYM = 63,
- AKEYCODE_ENVELOPE = 65,
- AKEYCODE_GRAVE = 68,
- AKEYCODE_SEMICOLON = 74,
- AKEYCODE_APOSTROPHE = 75,
- AKEYCODE_AT = 77,
- AKEYCODE_NUM = 78,
- AKEYCODE_HEADSETHOOK = 79,
- AKEYCODE_FOCUS = 80, // *Camera* focus
- AKEYCODE_NOTIFICATION = 83,
- AKEYCODE_SEARCH = 84,
- AKEYCODE_PICTSYMBOLS = 94,
- AKEYCODE_SWITCH_CHARSET = 95,
-*/
-
-static unsigned int android_get_keysym(unsigned int p_code) {
- for (int i = 0; _ak_to_keycode[i].keysym != KEY_UNKNOWN; i++) {
-
- if (_ak_to_keycode[i].keycode == p_code) {
-
- return _ak_to_keycode[i].keysym;
- }
- }
-
- return KEY_UNKNOWN;
-}
-
-JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_joybutton(JNIEnv *env, jobject obj, jint p_device, jint p_button, jboolean p_pressed) {
+JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_joybutton(JNIEnv *env, jclass clazz, jint p_device, jint p_button, jboolean p_pressed) {
if (step == 0)
return;
@@ -1122,7 +280,7 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_joybutton(JNIEnv *env
os_android->process_joy_event(jevent);
}
-JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_joyaxis(JNIEnv *env, jobject obj, jint p_device, jint p_axis, jfloat p_value) {
+JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_joyaxis(JNIEnv *env, jclass clazz, jint p_device, jint p_axis, jfloat p_value) {
if (step == 0)
return;
@@ -1135,7 +293,7 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_joyaxis(JNIEnv *env,
os_android->process_joy_event(jevent);
}
-JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_joyhat(JNIEnv *env, jobject obj, jint p_device, jint p_hat_x, jint p_hat_y) {
+JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_joyhat(JNIEnv *env, jclass clazz, jint p_device, jint p_hat_x, jint p_hat_y) {
if (step == 0)
return;
@@ -1160,14 +318,14 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_joyhat(JNIEnv *env, j
os_android->process_joy_event(jevent);
}
-JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_joyconnectionchanged(JNIEnv *env, jobject obj, jint p_device, jboolean p_connected, jstring p_name) {
+JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_joyconnectionchanged(JNIEnv *env, jclass clazz, jint p_device, jboolean p_connected, jstring p_name) {
if (os_android) {
String name = jstring_to_string(p_name, env);
os_android->joy_connection_changed(p_device, p_connected, name);
}
}
-JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_key(JNIEnv *env, jobject obj, jint p_keycode, jint p_scancode, jint p_unicode_char, jboolean p_pressed) {
+JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_key(JNIEnv *env, jclass clazz, jint p_keycode, jint p_scancode, jint p_unicode_char, jboolean p_pressed) {
if (step == 0)
return;
@@ -1196,23 +354,23 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_key(JNIEnv *env, jobj
os_android->process_event(ievent);
}
-JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_accelerometer(JNIEnv *env, jobject obj, jfloat x, jfloat y, jfloat z) {
+JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_accelerometer(JNIEnv *env, jclass clazz, jfloat x, jfloat y, jfloat z) {
accelerometer = Vector3(x, y, z);
}
-JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_gravity(JNIEnv *env, jobject obj, jfloat x, jfloat y, jfloat z) {
+JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_gravity(JNIEnv *env, jclass clazz, jfloat x, jfloat y, jfloat z) {
gravity = Vector3(x, y, z);
}
-JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_magnetometer(JNIEnv *env, jobject obj, jfloat x, jfloat y, jfloat z) {
+JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_magnetometer(JNIEnv *env, jclass clazz, jfloat x, jfloat y, jfloat z) {
magnetometer = Vector3(x, y, z);
}
-JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_gyroscope(JNIEnv *env, jobject obj, jfloat x, jfloat y, jfloat z) {
+JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_gyroscope(JNIEnv *env, jclass clazz, jfloat x, jfloat y, jfloat z) {
gyroscope = Vector3(x, y, z);
}
-JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_focusin(JNIEnv *env, jobject obj) {
+JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_focusin(JNIEnv *env, jclass clazz) {
if (step == 0)
return;
@@ -1220,7 +378,7 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_focusin(JNIEnv *env,
os_android->main_loop_focusin();
}
-JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_focusout(JNIEnv *env, jobject obj) {
+JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_focusout(JNIEnv *env, jclass clazz) {
if (step == 0)
return;
@@ -1228,134 +386,22 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_focusout(JNIEnv *env,
os_android->main_loop_focusout();
}
-JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_audio(JNIEnv *env, jobject obj) {
+JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_audio(JNIEnv *env, jclass clazz) {
ThreadAndroid::setup_thread();
AudioDriverAndroid::thread_func(env);
}
-JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_singleton(JNIEnv *env, jobject obj, jstring name, jobject p_object) {
-
- String singname = jstring_to_string(name, env);
- JNISingleton *s = memnew(JNISingleton);
- s->set_instance(env->NewGlobalRef(p_object));
- jni_singletons[singname] = s;
-
- Engine::get_singleton()->add_singleton(Engine::Singleton(singname, s));
- ProjectSettings::get_singleton()->set(singname, s);
-}
-
-static Variant::Type get_jni_type(const String &p_type) {
-
- static struct {
- const char *name;
- Variant::Type type;
- } _type_to_vtype[] = {
- { "void", Variant::NIL },
- { "boolean", Variant::BOOL },
- { "int", Variant::INT },
- { "float", Variant::FLOAT },
- { "double", Variant::FLOAT },
- { "java.lang.String", Variant::STRING },
- { "[I", Variant::PACKED_INT32_ARRAY },
- { "[B", Variant::PACKED_BYTE_ARRAY },
- { "[F", Variant::PACKED_FLOAT32_ARRAY },
- { "[Ljava.lang.String;", Variant::PACKED_STRING_ARRAY },
- { "org.godotengine.godot.Dictionary", Variant::DICTIONARY },
- { NULL, Variant::NIL }
- };
-
- int idx = 0;
-
- while (_type_to_vtype[idx].name) {
-
- if (p_type == _type_to_vtype[idx].name)
- return _type_to_vtype[idx].type;
-
- idx++;
- }
-
- return Variant::NIL;
-}
-
-static const char *get_jni_sig(const String &p_type) {
-
- static struct {
- const char *name;
- const char *sig;
- } _type_to_vtype[] = {
- { "void", "V" },
- { "boolean", "Z" },
- { "int", "I" },
- { "float", "F" },
- { "double", "D" },
- { "java.lang.String", "Ljava/lang/String;" },
- { "org.godotengine.godot.Dictionary", "Lorg/godotengine/godot/Dictionary;" },
- { "[I", "[I" },
- { "[B", "[B" },
- { "[F", "[F" },
- { "[Ljava.lang.String;", "[Ljava/lang/String;" },
- { NULL, "V" }
- };
-
- int idx = 0;
-
- while (_type_to_vtype[idx].name) {
-
- if (p_type == _type_to_vtype[idx].name)
- return _type_to_vtype[idx].sig;
-
- idx++;
- }
-
- return "Ljava/lang/Object;";
-}
-
-JNIEXPORT jstring JNICALL Java_org_godotengine_godot_GodotLib_getGlobal(JNIEnv *env, jobject obj, jstring path) {
+JNIEXPORT jstring JNICALL Java_org_godotengine_godot_GodotLib_getGlobal(JNIEnv *env, jclass clazz, jstring path) {
String js = jstring_to_string(path, env);
return env->NewStringUTF(ProjectSettings::get_singleton()->get(js).operator String().utf8().get_data());
}
-JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_method(JNIEnv *env, jobject obj, jstring sname, jstring name, jstring ret, jobjectArray args) {
-
- String singname = jstring_to_string(sname, env);
-
- ERR_FAIL_COND(!jni_singletons.has(singname));
-
- JNISingleton *s = jni_singletons.get(singname);
-
- String mname = jstring_to_string(name, env);
- String retval = jstring_to_string(ret, env);
- Vector<Variant::Type> types;
- String cs = "(";
-
- int stringCount = env->GetArrayLength(args);
-
- for (int i = 0; i < stringCount; i++) {
-
- jstring string = (jstring)env->GetObjectArrayElement(args, i);
- const String rawString = jstring_to_string(string, env);
- types.push_back(get_jni_type(rawString));
- cs += get_jni_sig(rawString);
- }
-
- cs += ")";
- cs += get_jni_sig(retval);
- jclass cls = env->GetObjectClass(s->get_instance());
- jmethodID mid = env->GetMethodID(cls, mname.ascii().get_data(), cs.ascii().get_data());
- if (!mid) {
-
- print_line("Failed getting method ID " + mname);
- }
-
- s->add_method(mname, mid, types, get_jni_type(retval));
-}
-
-JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_callobject(JNIEnv *env, jobject p_obj, jint ID, jstring method, jobjectArray params) {
+JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_callobject(JNIEnv *env, jclass clazz, jint ID, jstring method, jobjectArray params) {
- Object *obj = ObjectDB::get_instance(ID);
+ Object *obj = ObjectDB::get_instance(ObjectID((uint64_t)ID));
ERR_FAIL_COND(!obj);
int res = env->PushLocalFrame(16);
@@ -1385,9 +431,9 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_callobject(JNIEnv *en
env->PopLocalFrame(NULL);
}
-JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_calldeferred(JNIEnv *env, jobject p_obj, jint ID, jstring method, jobjectArray params) {
+JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_calldeferred(JNIEnv *env, jclass clazz, jint ID, jstring method, jobjectArray params) {
- Object *obj = ObjectDB::get_instance(ID);
+ Object *obj = ObjectDB::get_instance(ObjectID((uint64_t)ID));
ERR_FAIL_COND(!obj);
int res = env->PushLocalFrame(16);
@@ -1411,7 +457,7 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_calldeferred(JNIEnv *
env->PopLocalFrame(NULL);
}
-JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_requestPermissionResult(JNIEnv *env, jobject p_obj, jstring p_permission, jboolean p_result) {
+JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_requestPermissionResult(JNIEnv *env, jclass clazz, jstring p_permission, jboolean p_result) {
String permission = jstring_to_string(p_permission, env);
if (permission == "android.permission.RECORD_AUDIO" && p_result) {
AudioDriver::get_singleton()->capture_start();
@@ -1439,3 +485,4 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_onRendererPaused(JNIE
os_android->get_main_loop()->notification(MainLoop::NOTIFICATION_APP_PAUSED);
}
}
+}
diff --git a/platform/android/java_godot_lib_jni.h b/platform/android/java_godot_lib_jni.h
index fdd84ab1ef..a7a5970440 100644
--- a/platform/android/java_godot_lib_jni.h
+++ b/platform/android/java_godot_lib_jni.h
@@ -37,36 +37,34 @@
// These functions can be called from within JAVA and are the means by which our JAVA implementation calls back into our C++ code.
// See java/src/org/godotengine/godot/GodotLib.java for the JAVA side of this (yes that's why we have the long names)
extern "C" {
-JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_initialize(JNIEnv *env, jobject obj, jobject activity, jobject p_asset_manager, jboolean p_use_apk_expansion);
-JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_ondestroy(JNIEnv *env, jobject obj, jobject activity);
-JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_setup(JNIEnv *env, jobject obj, jobjectArray p_cmdline);
-JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_resize(JNIEnv *env, jobject obj, jint width, jint height);
-JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_newcontext(JNIEnv *env, jobject obj, bool p_32_bits);
-JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_step(JNIEnv *env, jobject obj);
-JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_back(JNIEnv *env, jobject obj);
-JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_touch(JNIEnv *env, jobject obj, jint ev, jint pointer, jint count, jintArray positions);
-JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_hover(JNIEnv *env, jobject obj, jint p_type, jint p_x, jint p_y);
-JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_doubletap(JNIEnv *env, jobject obj, jint p_x, jint p_y);
-JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_scroll(JNIEnv *env, jobject obj, jint p_x, jint p_y);
-JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_key(JNIEnv *env, jobject obj, jint p_keycode, jint p_scancode, jint p_unicode_char, jboolean p_pressed);
-JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_joybutton(JNIEnv *env, jobject obj, jint p_device, jint p_button, jboolean p_pressed);
-JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_joyaxis(JNIEnv *env, jobject obj, jint p_device, jint p_axis, jfloat p_value);
-JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_joyhat(JNIEnv *env, jobject obj, jint p_device, jint p_hat_x, jint p_hat_y);
-JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_joyconnectionchanged(JNIEnv *env, jobject obj, jint p_device, jboolean p_connected, jstring p_name);
-JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_audio(JNIEnv *env, jobject obj);
-JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_accelerometer(JNIEnv *env, jobject obj, jfloat x, jfloat y, jfloat z);
-JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_gravity(JNIEnv *env, jobject obj, jfloat x, jfloat y, jfloat z);
-JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_magnetometer(JNIEnv *env, jobject obj, jfloat x, jfloat y, jfloat z);
-JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_gyroscope(JNIEnv *env, jobject obj, jfloat x, jfloat y, jfloat z);
-JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_focusin(JNIEnv *env, jobject obj);
-JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_focusout(JNIEnv *env, jobject obj);
-JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_singleton(JNIEnv *env, jobject obj, jstring name, jobject p_object);
-JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_method(JNIEnv *env, jobject obj, jstring sname, jstring name, jstring ret, jobjectArray args);
-JNIEXPORT jstring JNICALL Java_org_godotengine_godot_GodotLib_getGlobal(JNIEnv *env, jobject obj, jstring path);
-JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_callobject(JNIEnv *env, jobject p_obj, jint ID, jstring method, jobjectArray params);
-JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_calldeferred(JNIEnv *env, jobject p_obj, jint ID, jstring method, jobjectArray params);
-JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_setVirtualKeyboardHeight(JNIEnv *env, jobject obj, jint p_height);
-JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_requestPermissionResult(JNIEnv *env, jobject p_obj, jstring p_permission, jboolean p_result);
+JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_initialize(JNIEnv *env, jclass clazz, jobject activity, jobject p_asset_manager, jboolean p_use_apk_expansion);
+JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_ondestroy(JNIEnv *env, jclass clazz, jobject activity);
+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, jint width, jint height);
+JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_newcontext(JNIEnv *env, jclass clazz, jboolean p_32_bits);
+JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_step(JNIEnv *env, jclass clazz);
+JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_back(JNIEnv *env, jclass clazz);
+JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_touch(JNIEnv *env, jclass clazz, jint ev, jint pointer, jint count, jintArray positions);
+JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_hover(JNIEnv *env, jclass clazz, jint p_type, jint p_x, jint p_y);
+JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_doubletap(JNIEnv *env, jclass clazz, jint p_x, jint p_y);
+JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_scroll(JNIEnv *env, jclass clazz, jint p_x, jint p_y);
+JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_key(JNIEnv *env, jclass clazz, jint p_keycode, jint p_scancode, jint p_unicode_char, jboolean p_pressed);
+JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_joybutton(JNIEnv *env, jclass clazz, jint p_device, jint p_button, jboolean p_pressed);
+JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_joyaxis(JNIEnv *env, jclass clazz, jint p_device, jint p_axis, jfloat p_value);
+JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_joyhat(JNIEnv *env, jclass clazz, jint p_device, jint p_hat_x, jint p_hat_y);
+JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_joyconnectionchanged(JNIEnv *env, jclass clazz, jint p_device, jboolean p_connected, jstring p_name);
+JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_audio(JNIEnv *env, jclass clazz);
+JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_accelerometer(JNIEnv *env, jclass clazz, jfloat x, jfloat y, jfloat z);
+JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_gravity(JNIEnv *env, jclass clazz, jfloat x, jfloat y, jfloat z);
+JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_magnetometer(JNIEnv *env, jclass clazz, jfloat x, jfloat y, jfloat z);
+JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_gyroscope(JNIEnv *env, jclass clazz, jfloat x, jfloat y, jfloat z);
+JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_focusin(JNIEnv *env, jclass clazz);
+JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_focusout(JNIEnv *env, jclass clazz);
+JNIEXPORT jstring JNICALL Java_org_godotengine_godot_GodotLib_getGlobal(JNIEnv *env, jclass clazz, jstring path);
+JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_callobject(JNIEnv *env, jclass clazz, jint ID, jstring method, jobjectArray params);
+JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_calldeferred(JNIEnv *env, jclass clazz, jint ID, jstring method, jobjectArray params);
+JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_setVirtualKeyboardHeight(JNIEnv *env, jclass clazz, jint p_height);
+JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_requestPermissionResult(JNIEnv *env, jclass clazz, jstring p_permission, jboolean p_result);
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_onRendererResumed(JNIEnv *env, jclass clazz);
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_onRendererPaused(JNIEnv *env, jclass clazz);
}
diff --git a/platform/android/java_godot_wrapper.cpp b/platform/android/java_godot_wrapper.cpp
index 9ac91b8ef6..7b677c186e 100644
--- a/platform/android/java_godot_wrapper.cpp
+++ b/platform/android/java_godot_wrapper.cpp
@@ -66,6 +66,7 @@ GodotJavaWrapper::GodotJavaWrapper(JNIEnv *p_env, jobject p_godot_instance) {
_is_activity_resumed = p_env->GetMethodID(cls, "isActivityResumed", "()Z");
_vibrate = p_env->GetMethodID(cls, "vibrate", "(I)V");
_get_input_fallback_mapping = p_env->GetMethodID(cls, "getInputFallbackMapping", "()Ljava/lang/String;");
+ _on_gl_godot_main_loop_started = p_env->GetMethodID(cls, "onGLGodotMainLoopStarted", "()V");
}
GodotJavaWrapper::~GodotJavaWrapper() {
@@ -107,6 +108,15 @@ void GodotJavaWrapper::on_video_init(JNIEnv *p_env) {
p_env->CallVoidMethod(godot_instance, _on_video_init);
}
+void GodotJavaWrapper::on_gl_godot_main_loop_started(JNIEnv *p_env) {
+ if (_on_gl_godot_main_loop_started) {
+ if (p_env == NULL) {
+ p_env = ThreadAndroid::get_env();
+ }
+ }
+ p_env->CallVoidMethod(godot_instance, _on_gl_godot_main_loop_started);
+}
+
void GodotJavaWrapper::restart(JNIEnv *p_env) {
if (_restart)
if (p_env == NULL)
diff --git a/platform/android/java_godot_wrapper.h b/platform/android/java_godot_wrapper.h
index f378b1ea38..cdab2ecc9c 100644
--- a/platform/android/java_godot_wrapper.h
+++ b/platform/android/java_godot_wrapper.h
@@ -61,6 +61,7 @@ private:
jmethodID _is_activity_resumed = 0;
jmethodID _vibrate = 0;
jmethodID _get_input_fallback_mapping = 0;
+ jmethodID _on_gl_godot_main_loop_started = 0;
public:
GodotJavaWrapper(JNIEnv *p_env, jobject p_godot_instance);
@@ -72,6 +73,7 @@ public:
jobject get_class_loader();
void on_video_init(JNIEnv *p_env = NULL);
+ void on_gl_godot_main_loop_started(JNIEnv *p_env = NULL);
void restart(JNIEnv *p_env = NULL);
void force_quit(JNIEnv *p_env = NULL);
void set_keep_screen_on(bool p_enabled);
diff --git a/platform/android/jni_utils.cpp b/platform/android/jni_utils.cpp
new file mode 100644
index 0000000000..3fa4e80884
--- /dev/null
+++ b/platform/android/jni_utils.cpp
@@ -0,0 +1,434 @@
+/*************************************************************************/
+/* jni_utils.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2020 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. */
+/*************************************************************************/
+
+#include "jni_utils.h"
+
+jvalret _variant_to_jvalue(JNIEnv *env, Variant::Type p_type, const Variant *p_arg, bool force_jobject) {
+
+ jvalret v;
+
+ switch (p_type) {
+
+ case Variant::BOOL: {
+
+ if (force_jobject) {
+ jclass bclass = env->FindClass("java/lang/Boolean");
+ jmethodID ctor = env->GetMethodID(bclass, "<init>", "(Z)V");
+ jvalue val;
+ val.z = (bool)(*p_arg);
+ jobject obj = env->NewObjectA(bclass, ctor, &val);
+ v.val.l = obj;
+ v.obj = obj;
+ env->DeleteLocalRef(bclass);
+ } else {
+ v.val.z = *p_arg;
+ };
+ } break;
+ case Variant::INT: {
+
+ if (force_jobject) {
+
+ jclass bclass = env->FindClass("java/lang/Integer");
+ jmethodID ctor = env->GetMethodID(bclass, "<init>", "(I)V");
+ jvalue val;
+ val.i = (int)(*p_arg);
+ jobject obj = env->NewObjectA(bclass, ctor, &val);
+ v.val.l = obj;
+ v.obj = obj;
+ env->DeleteLocalRef(bclass);
+
+ } else {
+ v.val.i = *p_arg;
+ };
+ } break;
+ case Variant::FLOAT: {
+
+ if (force_jobject) {
+
+ jclass bclass = env->FindClass("java/lang/Double");
+ jmethodID ctor = env->GetMethodID(bclass, "<init>", "(D)V");
+ jvalue val;
+ val.d = (double)(*p_arg);
+ jobject obj = env->NewObjectA(bclass, ctor, &val);
+ v.val.l = obj;
+ v.obj = obj;
+ env->DeleteLocalRef(bclass);
+
+ } else {
+ v.val.f = *p_arg;
+ };
+ } break;
+ case Variant::STRING: {
+
+ String s = *p_arg;
+ jstring jStr = env->NewStringUTF(s.utf8().get_data());
+ v.val.l = jStr;
+ v.obj = jStr;
+ } break;
+ case Variant::PACKED_STRING_ARRAY: {
+
+ Vector<String> sarray = *p_arg;
+ jobjectArray arr = env->NewObjectArray(sarray.size(), env->FindClass("java/lang/String"), env->NewStringUTF(""));
+
+ for (int j = 0; j < sarray.size(); j++) {
+
+ jstring str = env->NewStringUTF(sarray[j].utf8().get_data());
+ env->SetObjectArrayElement(arr, j, str);
+ env->DeleteLocalRef(str);
+ }
+ v.val.l = arr;
+ v.obj = arr;
+
+ } break;
+
+ case Variant::DICTIONARY: {
+
+ Dictionary dict = *p_arg;
+ jclass dclass = env->FindClass("org/godotengine/godot/Dictionary");
+ jmethodID ctor = env->GetMethodID(dclass, "<init>", "()V");
+ jobject jdict = env->NewObject(dclass, ctor);
+
+ Array keys = dict.keys();
+
+ jobjectArray jkeys = env->NewObjectArray(keys.size(), env->FindClass("java/lang/String"), env->NewStringUTF(""));
+ for (int j = 0; j < keys.size(); j++) {
+ jstring str = env->NewStringUTF(String(keys[j]).utf8().get_data());
+ env->SetObjectArrayElement(jkeys, j, str);
+ env->DeleteLocalRef(str);
+ };
+
+ jmethodID set_keys = env->GetMethodID(dclass, "set_keys", "([Ljava/lang/String;)V");
+ jvalue val;
+ val.l = jkeys;
+ env->CallVoidMethodA(jdict, set_keys, &val);
+ env->DeleteLocalRef(jkeys);
+
+ jobjectArray jvalues = env->NewObjectArray(keys.size(), env->FindClass("java/lang/Object"), NULL);
+
+ for (int j = 0; j < keys.size(); j++) {
+ Variant var = dict[keys[j]];
+ jvalret v = _variant_to_jvalue(env, var.get_type(), &var, true);
+ env->SetObjectArrayElement(jvalues, j, v.val.l);
+ if (v.obj) {
+ env->DeleteLocalRef(v.obj);
+ }
+ };
+
+ jmethodID set_values = env->GetMethodID(dclass, "set_values", "([Ljava/lang/Object;)V");
+ val.l = jvalues;
+ env->CallVoidMethodA(jdict, set_values, &val);
+ env->DeleteLocalRef(jvalues);
+ env->DeleteLocalRef(dclass);
+
+ v.val.l = jdict;
+ v.obj = jdict;
+ } break;
+
+ case Variant::PACKED_INT32_ARRAY: {
+
+ Vector<int> array = *p_arg;
+ jintArray arr = env->NewIntArray(array.size());
+ const int *r = array.ptr();
+ env->SetIntArrayRegion(arr, 0, array.size(), r);
+ v.val.l = arr;
+ v.obj = arr;
+
+ } break;
+ case Variant::PACKED_BYTE_ARRAY: {
+ Vector<uint8_t> array = *p_arg;
+ jbyteArray arr = env->NewByteArray(array.size());
+ const uint8_t *r = array.ptr();
+ env->SetByteArrayRegion(arr, 0, array.size(), reinterpret_cast<const signed char *>(r));
+ v.val.l = arr;
+ v.obj = arr;
+
+ } break;
+ case Variant::PACKED_FLOAT32_ARRAY: {
+
+ Vector<float> array = *p_arg;
+ jfloatArray arr = env->NewFloatArray(array.size());
+ const float *r = array.ptr();
+ env->SetFloatArrayRegion(arr, 0, array.size(), r);
+ v.val.l = arr;
+ v.obj = arr;
+
+ } break;
+#ifndef _MSC_VER
+#warning This is missing 64 bits arrays, I have no idea how to do it in JNI
+#endif
+
+ default: {
+
+ v.val.i = 0;
+ } break;
+ }
+ return v;
+}
+
+String _get_class_name(JNIEnv *env, jclass cls, bool *array) {
+
+ jclass cclass = env->FindClass("java/lang/Class");
+ jmethodID getName = env->GetMethodID(cclass, "getName", "()Ljava/lang/String;");
+ jstring clsName = (jstring)env->CallObjectMethod(cls, getName);
+
+ if (array) {
+ jmethodID isArray = env->GetMethodID(cclass, "isArray", "()Z");
+ jboolean isarr = env->CallBooleanMethod(cls, isArray);
+ (*array) = isarr ? true : false;
+ }
+ String name = jstring_to_string(clsName, env);
+ env->DeleteLocalRef(clsName);
+
+ return name;
+}
+
+Variant _jobject_to_variant(JNIEnv *env, jobject obj) {
+
+ if (obj == NULL) {
+ return Variant();
+ }
+
+ jclass c = env->GetObjectClass(obj);
+ bool array;
+ String name = _get_class_name(env, c, &array);
+
+ if (name == "java.lang.String") {
+
+ return jstring_to_string((jstring)obj, env);
+ };
+
+ if (name == "[Ljava.lang.String;") {
+
+ jobjectArray arr = (jobjectArray)obj;
+ int stringCount = env->GetArrayLength(arr);
+ Vector<String> sarr;
+
+ for (int i = 0; i < stringCount; i++) {
+ jstring string = (jstring)env->GetObjectArrayElement(arr, i);
+ sarr.push_back(jstring_to_string(string, env));
+ env->DeleteLocalRef(string);
+ }
+
+ return sarr;
+ };
+
+ if (name == "java.lang.Boolean") {
+
+ jmethodID boolValue = env->GetMethodID(c, "booleanValue", "()Z");
+ bool ret = env->CallBooleanMethod(obj, boolValue);
+ return ret;
+ };
+
+ if (name == "java.lang.Integer" || name == "java.lang.Long") {
+
+ jclass nclass = env->FindClass("java/lang/Number");
+ jmethodID longValue = env->GetMethodID(nclass, "longValue", "()J");
+ jlong ret = env->CallLongMethod(obj, longValue);
+ return ret;
+ };
+
+ if (name == "[I") {
+
+ jintArray arr = (jintArray)obj;
+ int fCount = env->GetArrayLength(arr);
+ Vector<int> sarr;
+ sarr.resize(fCount);
+
+ int *w = sarr.ptrw();
+ env->GetIntArrayRegion(arr, 0, fCount, w);
+ return sarr;
+ };
+
+ if (name == "[B") {
+
+ jbyteArray arr = (jbyteArray)obj;
+ int fCount = env->GetArrayLength(arr);
+ Vector<uint8_t> sarr;
+ sarr.resize(fCount);
+
+ uint8_t *w = sarr.ptrw();
+ env->GetByteArrayRegion(arr, 0, fCount, reinterpret_cast<signed char *>(w));
+ return sarr;
+ };
+
+ if (name == "java.lang.Float" || name == "java.lang.Double") {
+
+ jclass nclass = env->FindClass("java/lang/Number");
+ jmethodID doubleValue = env->GetMethodID(nclass, "doubleValue", "()D");
+ double ret = env->CallDoubleMethod(obj, doubleValue);
+ return ret;
+ };
+
+ if (name == "[D") {
+
+ jdoubleArray arr = (jdoubleArray)obj;
+ int fCount = env->GetArrayLength(arr);
+ PackedFloat32Array sarr;
+ sarr.resize(fCount);
+
+ real_t *w = sarr.ptrw();
+
+ for (int i = 0; i < fCount; i++) {
+
+ double n;
+ env->GetDoubleArrayRegion(arr, i, 1, &n);
+ w[i] = n;
+ };
+ return sarr;
+ };
+
+ if (name == "[F") {
+
+ jfloatArray arr = (jfloatArray)obj;
+ int fCount = env->GetArrayLength(arr);
+ PackedFloat32Array sarr;
+ sarr.resize(fCount);
+
+ real_t *w = sarr.ptrw();
+
+ for (int i = 0; i < fCount; i++) {
+
+ float n;
+ env->GetFloatArrayRegion(arr, i, 1, &n);
+ w[i] = n;
+ };
+ return sarr;
+ };
+
+ if (name == "[Ljava.lang.Object;") {
+
+ jobjectArray arr = (jobjectArray)obj;
+ int objCount = env->GetArrayLength(arr);
+ Array varr;
+
+ for (int i = 0; i < objCount; i++) {
+ jobject jobj = env->GetObjectArrayElement(arr, i);
+ Variant v = _jobject_to_variant(env, jobj);
+ varr.push_back(v);
+ env->DeleteLocalRef(jobj);
+ }
+
+ return varr;
+ };
+
+ if (name == "java.util.HashMap" || name == "org.godotengine.godot.Dictionary") {
+
+ Dictionary ret;
+ jclass oclass = c;
+ jmethodID get_keys = env->GetMethodID(oclass, "get_keys", "()[Ljava/lang/String;");
+ jobjectArray arr = (jobjectArray)env->CallObjectMethod(obj, get_keys);
+
+ PackedStringArray keys = _jobject_to_variant(env, arr);
+ env->DeleteLocalRef(arr);
+
+ jmethodID get_values = env->GetMethodID(oclass, "get_values", "()[Ljava/lang/Object;");
+ arr = (jobjectArray)env->CallObjectMethod(obj, get_values);
+
+ Array vals = _jobject_to_variant(env, arr);
+ env->DeleteLocalRef(arr);
+
+ for (int i = 0; i < keys.size(); i++) {
+
+ ret[keys[i]] = vals[i];
+ };
+
+ return ret;
+ };
+
+ env->DeleteLocalRef(c);
+
+ return Variant();
+}
+
+Variant::Type get_jni_type(const String &p_type) {
+
+ static struct {
+ const char *name;
+ Variant::Type type;
+ } _type_to_vtype[] = {
+ { "void", Variant::NIL },
+ { "boolean", Variant::BOOL },
+ { "int", Variant::INT },
+ { "float", Variant::FLOAT },
+ { "double", Variant::FLOAT },
+ { "java.lang.String", Variant::STRING },
+ { "[I", Variant::PACKED_INT32_ARRAY },
+ { "[B", Variant::PACKED_BYTE_ARRAY },
+ { "[F", Variant::PACKED_FLOAT32_ARRAY },
+ { "[Ljava.lang.String;", Variant::PACKED_STRING_ARRAY },
+ { "org.godotengine.godot.Dictionary", Variant::DICTIONARY },
+ { NULL, Variant::NIL }
+ };
+
+ int idx = 0;
+
+ while (_type_to_vtype[idx].name) {
+
+ if (p_type == _type_to_vtype[idx].name)
+ return _type_to_vtype[idx].type;
+
+ idx++;
+ }
+
+ return Variant::NIL;
+}
+
+const char *get_jni_sig(const String &p_type) {
+
+ static struct {
+ const char *name;
+ const char *sig;
+ } _type_to_vtype[] = {
+ { "void", "V" },
+ { "boolean", "Z" },
+ { "int", "I" },
+ { "float", "F" },
+ { "double", "D" },
+ { "java.lang.String", "Ljava/lang/String;" },
+ { "org.godotengine.godot.Dictionary", "Lorg/godotengine/godot/Dictionary;" },
+ { "[I", "[I" },
+ { "[B", "[B" },
+ { "[F", "[F" },
+ { "[Ljava.lang.String;", "[Ljava/lang/String;" },
+ { NULL, "V" }
+ };
+
+ int idx = 0;
+
+ while (_type_to_vtype[idx].name) {
+
+ if (p_type == _type_to_vtype[idx].name)
+ return _type_to_vtype[idx].sig;
+
+ idx++;
+ }
+
+ return "Ljava/lang/Object;";
+}
diff --git a/platform/android/jni_utils.h b/platform/android/jni_utils.h
new file mode 100644
index 0000000000..925340a680
--- /dev/null
+++ b/platform/android/jni_utils.h
@@ -0,0 +1,242 @@
+/*************************************************************************/
+/* jni_utils.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2020 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. */
+/*************************************************************************/
+
+#ifndef JNI_UTILS_H
+#define JNI_UTILS_H
+
+#include "string_android.h"
+#include <core/engine.h>
+#include <core/variant.h>
+#include <jni.h>
+
+struct jvalret {
+
+ jobject obj;
+ jvalue val;
+ jvalret() { obj = NULL; }
+};
+
+jvalret _variant_to_jvalue(JNIEnv *env, Variant::Type p_type, const Variant *p_arg, bool force_jobject = false);
+
+String _get_class_name(JNIEnv *env, jclass cls, bool *array);
+
+Variant _jobject_to_variant(JNIEnv *env, jobject obj);
+
+Variant::Type get_jni_type(const String &p_type);
+
+const char *get_jni_sig(const String &p_type);
+
+class JNISingleton : public Object {
+
+ GDCLASS(JNISingleton, Object);
+
+ struct MethodData {
+
+ jmethodID method;
+ Variant::Type ret_type;
+ Vector<Variant::Type> argtypes;
+ };
+
+ jobject instance;
+ Map<StringName, MethodData> method_map;
+
+public:
+ virtual Variant call(const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error) {
+
+ ERR_FAIL_COND_V(!instance, Variant());
+
+ r_error.error = Callable::CallError::CALL_OK;
+
+ Map<StringName, MethodData>::Element *E = method_map.find(p_method);
+ if (!E) {
+
+ r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD;
+ return Variant();
+ }
+
+ int ac = E->get().argtypes.size();
+ if (ac < p_argcount) {
+
+ r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS;
+ r_error.argument = ac;
+ return Variant();
+ }
+
+ if (ac > p_argcount) {
+
+ r_error.error = Callable::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS;
+ r_error.argument = ac;
+ return Variant();
+ }
+
+ for (int i = 0; i < p_argcount; i++) {
+
+ if (!Variant::can_convert(p_args[i]->get_type(), E->get().argtypes[i])) {
+
+ r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
+ r_error.argument = i;
+ r_error.expected = E->get().argtypes[i];
+ }
+ }
+
+ jvalue *v = NULL;
+
+ if (p_argcount) {
+
+ v = (jvalue *)alloca(sizeof(jvalue) * p_argcount);
+ }
+
+ JNIEnv *env = ThreadAndroid::get_env();
+
+ int res = env->PushLocalFrame(16);
+
+ ERR_FAIL_COND_V(res != 0, Variant());
+
+ List<jobject> to_erase;
+ for (int i = 0; i < p_argcount; i++) {
+
+ jvalret vr = _variant_to_jvalue(env, E->get().argtypes[i], p_args[i]);
+ v[i] = vr.val;
+ if (vr.obj)
+ to_erase.push_back(vr.obj);
+ }
+
+ Variant ret;
+
+ switch (E->get().ret_type) {
+
+ case Variant::NIL: {
+
+ env->CallVoidMethodA(instance, E->get().method, v);
+ } break;
+ case Variant::BOOL: {
+
+ ret = env->CallBooleanMethodA(instance, E->get().method, v) == JNI_TRUE;
+ } break;
+ case Variant::INT: {
+
+ ret = env->CallIntMethodA(instance, E->get().method, v);
+ } break;
+ case Variant::FLOAT: {
+
+ ret = env->CallFloatMethodA(instance, E->get().method, v);
+ } break;
+ case Variant::STRING: {
+
+ jobject o = env->CallObjectMethodA(instance, E->get().method, v);
+ ret = jstring_to_string((jstring)o, env);
+ env->DeleteLocalRef(o);
+ } break;
+ case Variant::PACKED_STRING_ARRAY: {
+
+ jobjectArray arr = (jobjectArray)env->CallObjectMethodA(instance, E->get().method, v);
+
+ ret = _jobject_to_variant(env, arr);
+
+ env->DeleteLocalRef(arr);
+ } break;
+ case Variant::PACKED_INT32_ARRAY: {
+
+ jintArray arr = (jintArray)env->CallObjectMethodA(instance, E->get().method, v);
+
+ int fCount = env->GetArrayLength(arr);
+ Vector<int> sarr;
+ sarr.resize(fCount);
+
+ int *w = sarr.ptrw();
+ env->GetIntArrayRegion(arr, 0, fCount, w);
+ ret = sarr;
+ env->DeleteLocalRef(arr);
+ } break;
+ case Variant::PACKED_FLOAT32_ARRAY: {
+
+ jfloatArray arr = (jfloatArray)env->CallObjectMethodA(instance, E->get().method, v);
+
+ int fCount = env->GetArrayLength(arr);
+ Vector<float> sarr;
+ sarr.resize(fCount);
+
+ float *w = sarr.ptrw();
+ env->GetFloatArrayRegion(arr, 0, fCount, w);
+ ret = sarr;
+ env->DeleteLocalRef(arr);
+ } break;
+
+#ifndef _MSC_VER
+#warning This is missing 64 bits arrays, I have no idea how to do it in JNI
+#endif
+ case Variant::DICTIONARY: {
+
+ jobject obj = env->CallObjectMethodA(instance, E->get().method, v);
+ ret = _jobject_to_variant(env, obj);
+ env->DeleteLocalRef(obj);
+
+ } break;
+ default: {
+
+ env->PopLocalFrame(NULL);
+ ERR_FAIL_V(Variant());
+ } break;
+ }
+
+ while (to_erase.size()) {
+ env->DeleteLocalRef(to_erase.front()->get());
+ to_erase.pop_front();
+ }
+
+ env->PopLocalFrame(NULL);
+
+ return ret;
+ }
+
+ jobject get_instance() const {
+
+ return instance;
+ }
+ void set_instance(jobject p_instance) {
+
+ instance = p_instance;
+ }
+
+ void add_method(const StringName &p_name, jmethodID p_method, const Vector<Variant::Type> &p_args, Variant::Type p_ret_type) {
+
+ MethodData md;
+ md.method = p_method;
+ md.argtypes = p_args;
+ md.ret_type = p_ret_type;
+ method_map[p_name] = md;
+ }
+
+ JNISingleton() {
+ instance = NULL;
+ }
+};
+
+#endif // JNI_UTILS_H
diff --git a/platform/android/os_android.cpp b/platform/android/os_android.cpp
index 15e3ac48c7..7e2b0d948e 100644
--- a/platform/android/os_android.cpp
+++ b/platform/android/os_android.cpp
@@ -32,7 +32,9 @@
#include "core/io/file_access_buffered_fa.h"
#include "core/project_settings.h"
+#if defined(OPENGL_ENABLED)
#include "drivers/gles2/rasterizer_gles2.h"
+#endif
#include "drivers/unix/dir_access_unix.h"
#include "drivers/unix/file_access_unix.h"
#include "file_access_android.h"
@@ -120,23 +122,27 @@ int OS_Android::get_current_video_driver() const {
Error OS_Android::initialize(const VideoMode &p_desired, int p_video_driver, int p_audio_driver) {
- bool gl_initialization_error = false;
-
// FIXME: Add Vulkan support. Readd fallback code from Vulkan to GLES2?
- if (RasterizerGLES2::is_viable() == OK) {
- RasterizerGLES2::register_config();
- RasterizerGLES2::make_current();
- } else {
- gl_initialization_error = true;
- }
-
- if (gl_initialization_error) {
- OS::get_singleton()->alert("Your device does not support any of the supported OpenGL versions.\n"
- "Please try updating your Android version.",
- "Unable to initialize video driver");
- return ERR_UNAVAILABLE;
+#if defined(OPENGL_ENABLED)
+ if (video_driver_index == VIDEO_DRIVER_GLES2) {
+ bool gl_initialization_error = false;
+
+ if (RasterizerGLES2::is_viable() == OK) {
+ RasterizerGLES2::register_config();
+ RasterizerGLES2::make_current();
+ } else {
+ gl_initialization_error = true;
+ }
+
+ if (gl_initialization_error) {
+ OS::get_singleton()->alert("Your device does not support any of the supported OpenGL versions.\n"
+ "Please try updating your Android version.",
+ "Unable to initialize video driver");
+ return ERR_UNAVAILABLE;
+ }
}
+#endif
video_driver_index = p_video_driver;
@@ -753,6 +759,8 @@ OS_Android::OS_Android(GodotJavaWrapper *p_godot_java, GodotIOJavaWrapper *p_god
//rasterizer = NULL;
use_gl2 = false;
+ visual_server = NULL;
+
godot_java = p_godot_java;
godot_io_java = p_godot_io_java;
diff --git a/platform/android/plugin/godot_plugin_jni.cpp b/platform/android/plugin/godot_plugin_jni.cpp
new file mode 100644
index 0000000000..7413236e5d
--- /dev/null
+++ b/platform/android/plugin/godot_plugin_jni.cpp
@@ -0,0 +1,115 @@
+/*************************************************************************/
+/* godot_plugin_jni.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2020 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. */
+/*************************************************************************/
+
+#include "godot_plugin_jni.h"
+
+#include <core/engine.h>
+#include <core/error_macros.h>
+#include <core/project_settings.h>
+#include <platform/android/jni_utils.h>
+#include <platform/android/string_android.h>
+
+static HashMap<String, JNISingleton *> jni_singletons;
+
+extern "C" {
+
+JNIEXPORT void JNICALL Java_org_godotengine_godot_plugin_GodotPlugin_nativeRegisterSingleton(JNIEnv *env, jobject obj, jstring name) {
+
+ String singname = jstring_to_string(name, env);
+ JNISingleton *s = memnew(JNISingleton);
+ s->set_instance(env->NewGlobalRef(obj));
+ jni_singletons[singname] = s;
+
+ Engine::get_singleton()->add_singleton(Engine::Singleton(singname, s));
+ ProjectSettings::get_singleton()->set(singname, s);
+}
+
+JNIEXPORT void JNICALL Java_org_godotengine_godot_plugin_GodotPlugin_nativeRegisterMethod(JNIEnv *env, jobject obj, jstring sname, jstring name, jstring ret, jobjectArray args) {
+
+ String singname = jstring_to_string(sname, env);
+
+ ERR_FAIL_COND(!jni_singletons.has(singname));
+
+ JNISingleton *s = jni_singletons.get(singname);
+
+ String mname = jstring_to_string(name, env);
+ String retval = jstring_to_string(ret, env);
+ Vector<Variant::Type> types;
+ String cs = "(";
+
+ int stringCount = env->GetArrayLength(args);
+
+ for (int i = 0; i < stringCount; i++) {
+
+ jstring string = (jstring)env->GetObjectArrayElement(args, i);
+ const String rawString = jstring_to_string(string, env);
+ types.push_back(get_jni_type(rawString));
+ cs += get_jni_sig(rawString);
+ }
+
+ cs += ")";
+ cs += get_jni_sig(retval);
+ jclass cls = env->GetObjectClass(s->get_instance());
+ jmethodID mid = env->GetMethodID(cls, mname.ascii().get_data(), cs.ascii().get_data());
+ if (!mid) {
+
+ print_line("Failed getting method ID " + mname);
+ }
+
+ s->add_method(mname, mid, types, get_jni_type(retval));
+}
+
+JNIEXPORT void JNICALL Java_org_godotengine_godot_plugin_GodotPlugin_nativeRegisterGDNativeLibraries(JNIEnv *env, jobject obj, jobjectArray gdnlib_paths) {
+ int gdnlib_count = env->GetArrayLength(gdnlib_paths);
+ if (gdnlib_count == 0) {
+ return;
+ }
+
+ // Retrieve the current list of gdnative libraries.
+ Array singletons = Array();
+ if (ProjectSettings::get_singleton()->has_setting("gdnative/singletons")) {
+ singletons = ProjectSettings::get_singleton()->get("gdnative/singletons");
+ }
+
+ // Insert the libraries provided by the plugin
+ for (int i = 0; i < gdnlib_count; i++) {
+ jstring relative_path = (jstring)env->GetObjectArrayElement(gdnlib_paths, i);
+
+ String path = "res://" + jstring_to_string(relative_path, env);
+ if (!singletons.has(path)) {
+ singletons.push_back(path);
+ }
+ env->DeleteLocalRef(relative_path);
+ }
+
+ // Insert the updated list back into project settings.
+ ProjectSettings::get_singleton()->set("gdnative/singletons", singletons);
+}
+}
diff --git a/platform/osx/semaphore_osx.h b/platform/android/plugin/godot_plugin_jni.h
index 9aa2b47bc8..0d613d3bfe 100644
--- a/platform/osx/semaphore_osx.h
+++ b/platform/android/plugin/godot_plugin_jni.h
@@ -1,5 +1,5 @@
/*************************************************************************/
-/* semaphore_osx.h */
+/* godot_plugin_jni.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
@@ -28,32 +28,16 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#ifndef SEMAPHORE_OSX_H
-#define SEMAPHORE_OSX_H
+#ifndef GODOT_PLUGIN_JNI_H
+#define GODOT_PLUGIN_JNI_H
-struct cgsem {
- int pipefd[2];
-};
+#include <android/log.h>
+#include <jni.h>
-typedef struct cgsem cgsem_t;
+extern "C" {
+JNIEXPORT void JNICALL Java_org_godotengine_godot_plugin_GodotPlugin_nativeRegisterSingleton(JNIEnv *env, jobject obj, jstring name);
+JNIEXPORT void JNICALL Java_org_godotengine_godot_plugin_GodotPlugin_nativeRegisterMethod(JNIEnv *env, jobject obj, jstring sname, jstring name, jstring ret, jobjectArray args);
+JNIEXPORT void JNICALL Java_org_godotengine_godot_plugin_GodotPlugin_nativeRegisterGDNativeLibraries(JNIEnv *env, jobject obj, jobjectArray gdnlib_paths);
+}
-#include "core/os/semaphore.h"
-
-class SemaphoreOSX : public SemaphoreOld {
-
- mutable cgsem_t sem;
-
- static SemaphoreOld *create_semaphore_osx();
-
-public:
- virtual Error wait();
- virtual Error post();
- virtual int get() const;
-
- static void make_default();
- SemaphoreOSX();
-
- ~SemaphoreOSX();
-};
-
-#endif // SEMAPHORE_OSX_H
+#endif // GODOT_PLUGIN_JNI_H
diff --git a/drivers/unix/semaphore_posix.cpp b/platform/android/vulkan/vk_renderer_jni.cpp
index b532b09cd6..3026e7daad 100644
--- a/drivers/unix/semaphore_posix.cpp
+++ b/platform/android/vulkan/vk_renderer_jni.cpp
@@ -1,5 +1,5 @@
/*************************************************************************/
-/* semaphore_posix.cpp */
+/* vk_renderer_jni.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
@@ -28,60 +28,31 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#include "semaphore_posix.h"
+#include "vk_renderer_jni.h"
-#if (defined(UNIX_ENABLED) || defined(PTHREAD_ENABLED)) && !defined(OSX_ENABLED) && !defined(IPHONE_ENABLED)
+extern "C" {
-#include "core/os/memory.h"
-#include <errno.h>
-#include <stdio.h>
-
-Error SemaphorePosix::wait() {
-
- while (sem_wait(&sem)) {
- if (errno == EINTR) {
- errno = 0;
- continue;
- } else {
- perror("sem waiting");
- return ERR_BUSY;
- }
- }
- return OK;
+JNIEXPORT void JNICALL Java_org_godotengine_godot_vulkan_VkRenderer_nativeOnVkSurfaceCreated(JNIEnv *env, jobject obj, jobject j_surface) {
+ // TODO: complete
}
-Error SemaphorePosix::post() {
-
- return (sem_post(&sem) == 0) ? OK : ERR_BUSY;
+JNIEXPORT void JNICALL Java_org_godotengine_godot_vulkan_VkRenderer_nativeOnVkSurfaceChanged(JNIEnv *env, jobject object, jobject j_surface, jint width, jint height) {
+ // TODO: complete
}
-int SemaphorePosix::get() const {
- int val;
- sem_getvalue(&sem, &val);
-
- return val;
+JNIEXPORT void JNICALL Java_org_godotengine_godot_vulkan_VkRenderer_nativeOnVkResume(JNIEnv *env, jobject obj) {
+ // TODO: complete
}
-SemaphoreOld *SemaphorePosix::create_semaphore_posix() {
-
- return memnew(SemaphorePosix);
+JNIEXPORT void JNICALL Java_org_godotengine_godot_vulkan_VkRenderer_nativeOnVkDrawFrame(JNIEnv *env, jobject obj) {
+ // TODO: complete
}
-void SemaphorePosix::make_default() {
-
- create_func = create_semaphore_posix;
+JNIEXPORT void JNICALL Java_org_godotengine_godot_vulkan_VkRenderer_nativeOnVkPause(JNIEnv *env, jobject obj) {
+ // TODO: complete
}
-SemaphorePosix::SemaphorePosix() {
-
- int r = sem_init(&sem, 0, 0);
- if (r != 0)
- perror("sem creating");
+JNIEXPORT void JNICALL Java_org_godotengine_godot_vulkan_VkRenderer_nativeOnVkDestroy(JNIEnv *env, jobject obj) {
+ // TODO: complete
}
-
-SemaphorePosix::~SemaphorePosix() {
-
- sem_destroy(&sem);
}
-
-#endif
diff --git a/drivers/unix/semaphore_posix.h b/platform/android/vulkan/vk_renderer_jni.h
index 2bffe6933d..017766fea2 100644
--- a/drivers/unix/semaphore_posix.h
+++ b/platform/android/vulkan/vk_renderer_jni.h
@@ -1,5 +1,5 @@
/*************************************************************************/
-/* semaphore_posix.h */
+/* vk_renderer_jni.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
@@ -28,31 +28,19 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#ifndef SEMAPHORE_POSIX_H
-#define SEMAPHORE_POSIX_H
+#ifndef VK_RENDERER_JNI_H
+#define VK_RENDERER_JNI_H
-#include "core/os/semaphore.h"
+#include <android/log.h>
+#include <jni.h>
-#if (defined(UNIX_ENABLED) || defined(PTHREAD_ENABLED)) && !defined(OSX_ENABLED) && !defined(IPHONE_ENABLED)
+extern "C" {
+JNIEXPORT void JNICALL Java_org_godotengine_godot_vulkan_VkRenderer_nativeOnVkSurfaceCreated(JNIEnv *env, jobject obj, jobject j_surface);
+JNIEXPORT void JNICALL Java_org_godotengine_godot_vulkan_VkRenderer_nativeOnVkSurfaceChanged(JNIEnv *env, jobject object, jobject j_surface, jint width, jint height);
+JNIEXPORT void JNICALL Java_org_godotengine_godot_vulkan_VkRenderer_nativeOnVkResume(JNIEnv *env, jobject obj);
+JNIEXPORT void JNICALL Java_org_godotengine_godot_vulkan_VkRenderer_nativeOnVkDrawFrame(JNIEnv *env, jobject obj);
+JNIEXPORT void JNICALL Java_org_godotengine_godot_vulkan_VkRenderer_nativeOnVkPause(JNIEnv *env, jobject obj);
+JNIEXPORT void JNICALL Java_org_godotengine_godot_vulkan_VkRenderer_nativeOnVkDestroy(JNIEnv *env, jobject obj);
+}
-#include <semaphore.h>
-
-class SemaphorePosix : public SemaphoreOld {
-
- mutable sem_t sem;
-
- static SemaphoreOld *create_semaphore_posix();
-
-public:
- virtual Error wait();
- virtual Error post();
- virtual int get() const;
-
- static void make_default();
- SemaphorePosix();
-
- ~SemaphorePosix();
-};
-
-#endif
-#endif
+#endif // VK_RENDERER_JNI_H
diff --git a/platform/iphone/os_iphone.cpp b/platform/iphone/os_iphone.cpp
index 8a50f5a28b..497f2f747d 100644
--- a/platform/iphone/os_iphone.cpp
+++ b/platform/iphone/os_iphone.cpp
@@ -100,7 +100,6 @@ String OSIPhone::get_unique_id() const {
void OSIPhone::initialize_core() {
OS_Unix::initialize_core();
- SemaphoreIphone::make_default();
set_data_dir(data_dir);
};
diff --git a/platform/iphone/semaphore_iphone.h b/platform/iphone/semaphore_iphone.h
deleted file mode 100644
index 54ff3c17f9..0000000000
--- a/platform/iphone/semaphore_iphone.h
+++ /dev/null
@@ -1,59 +0,0 @@
-/*************************************************************************/
-/* semaphore_iphone.h */
-/*************************************************************************/
-/* This file is part of: */
-/* GODOT ENGINE */
-/* https://godotengine.org */
-/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 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. */
-/*************************************************************************/
-
-#ifndef SEMAPHORE_IPHONE_H
-#define SEMAPHORE_IPHONE_H
-
-struct cgsem {
- int pipefd[2];
-};
-
-typedef struct cgsem cgsem_t;
-
-#include "core/os/semaphore.h"
-
-class SemaphoreIphone : public SemaphoreOld {
-
- mutable cgsem_t sem;
-
- static SemaphoreOld *create_semaphore_iphone();
-
-public:
- virtual Error wait();
- virtual Error post();
- virtual int get() const;
-
- static void make_default();
- SemaphoreIphone();
-
- ~SemaphoreIphone();
-};
-
-#endif // SEMAPHORE_IPHONE_H
diff --git a/platform/javascript/os_javascript.cpp b/platform/javascript/os_javascript.cpp
index 299c469bf4..037f78c7af 100644
--- a/platform/javascript/os_javascript.cpp
+++ b/platform/javascript/os_javascript.cpp
@@ -982,10 +982,10 @@ Error OS_JavaScript::initialize(const VideoMode &p_desired, int p_video_driver,
SET_EM_CALLBACK(GODOT_CANVAS_SELECTOR, mousedown, mouse_button_callback)
SET_EM_CALLBACK(EMSCRIPTEN_EVENT_TARGET_WINDOW, mouseup, mouse_button_callback)
SET_EM_CALLBACK(EMSCRIPTEN_EVENT_TARGET_WINDOW, wheel, wheel_callback)
- SET_EM_CALLBACK(EMSCRIPTEN_EVENT_TARGET_WINDOW, touchstart, touch_press_callback)
- SET_EM_CALLBACK(EMSCRIPTEN_EVENT_TARGET_WINDOW, touchmove, touchmove_callback)
- SET_EM_CALLBACK(EMSCRIPTEN_EVENT_TARGET_WINDOW, touchend, touch_press_callback)
- SET_EM_CALLBACK(EMSCRIPTEN_EVENT_TARGET_WINDOW, touchcancel, touch_press_callback)
+ SET_EM_CALLBACK(GODOT_CANVAS_SELECTOR, touchstart, touch_press_callback)
+ SET_EM_CALLBACK(GODOT_CANVAS_SELECTOR, touchmove, touchmove_callback)
+ SET_EM_CALLBACK(GODOT_CANVAS_SELECTOR, touchend, touch_press_callback)
+ SET_EM_CALLBACK(GODOT_CANVAS_SELECTOR, touchcancel, touch_press_callback)
SET_EM_CALLBACK(GODOT_CANVAS_SELECTOR, keydown, keydown_callback)
SET_EM_CALLBACK(GODOT_CANVAS_SELECTOR, keypress, keypress_callback)
SET_EM_CALLBACK(GODOT_CANVAS_SELECTOR, keyup, keyup_callback)
diff --git a/platform/osx/SCsub b/platform/osx/SCsub
index d764ac4b50..0a4e0a45e1 100644
--- a/platform/osx/SCsub
+++ b/platform/osx/SCsub
@@ -9,7 +9,6 @@ files = [
'crash_handler_osx.mm',
'os_osx.mm',
'godot_main_osx.mm',
- 'semaphore_osx.cpp',
'dir_access_osx.mm',
'joypad_osx.cpp',
'vulkan_context_osx.mm',
diff --git a/platform/osx/detect.py b/platform/osx/detect.py
index 12ca5c10dc..b2f4032f66 100644
--- a/platform/osx/detect.py
+++ b/platform/osx/detect.py
@@ -151,13 +151,13 @@ def configure(env):
env.Prepend(CPPPATH=['#platform/osx'])
env.Append(CPPDEFINES=['OSX_ENABLED', 'UNIX_ENABLED', 'APPLE_STYLE_KEYS', 'COREAUDIO_ENABLED', 'COREMIDI_ENABLED'])
- env.Append(LINKFLAGS=['-framework', 'Cocoa', '-framework', 'Carbon', '-framework', 'AudioUnit', '-framework', 'CoreAudio', '-framework', 'CoreMIDI', '-framework', 'IOKit', '-framework', 'ForceFeedback', '-framework', 'CoreVideo', '-framework', 'AVFoundation', '-framework', 'CoreMedia'])
+ env.AppendUnique(FRAMEWORKS=['Cocoa', 'Carbon', 'AudioUnit', 'CoreAudio', 'CoreMIDI', 'IOKit', 'ForceFeedback', 'CoreVideo', 'AVFoundation', 'CoreMedia'])
env.Append(LIBS=['pthread', 'z'])
env.Append(CPPDEFINES=['VULKAN_ENABLED'])
- env.Append(LINKFLAGS=['-framework', 'Metal', '-framework', 'QuartzCore', '-framework', 'IOSurface'])
+ env.AppendUnique(FRAMEWORKS=['Metal', 'QuartzCore', 'IOSurface'])
if (env['use_static_mvk']):
- env.Append(LINKFLAGS=['-framework', 'MoltenVK'])
+ env.AppendUnique(FRAMEWORKS=['MoltenVK'])
env['builtin_vulkan'] = False
elif not env['builtin_vulkan']:
env.Append(LIBS=['vulkan'])
diff --git a/platform/osx/joypad_osx.cpp b/platform/osx/joypad_osx.cpp
index 13ece678f3..e9f46fb5a4 100644
--- a/platform/osx/joypad_osx.cpp
+++ b/platform/osx/joypad_osx.cpp
@@ -208,9 +208,8 @@ void joypad::add_hid_elements(CFArrayRef p_array) {
CFArrayApplyFunction(p_array, range, hid_element_added, this);
}
-static void joypad_removed_callback(void *ctx, IOReturn result, void *sender) {
- int id = (intptr_t)ctx;
- self->_device_removed(id);
+static void joypad_removed_callback(void *ctx, IOReturn res, void *sender, IOHIDDeviceRef ioHIDDeviceObject) {
+ self->_device_removed(res, ioHIDDeviceObject);
}
static void joypad_added_callback(void *ctx, IOReturn res, void *sender, IOHIDDeviceRef ioHIDDeviceObject) {
@@ -261,16 +260,15 @@ void JoypadOSX::_device_added(IOReturn p_res, IOHIDDeviceRef p_device) {
#endif
device_list.push_back(new_joypad);
}
- IOHIDDeviceRegisterRemovalCallback(p_device, joypad_removed_callback, (void *)(intptr_t)new_joypad.id);
IOHIDDeviceScheduleWithRunLoop(p_device, CFRunLoopGetCurrent(), GODOT_JOY_LOOP_RUN_MODE);
}
-void JoypadOSX::_device_removed(int p_id) {
+void JoypadOSX::_device_removed(IOReturn p_res, IOHIDDeviceRef p_device) {
- int device = get_joy_index(p_id);
+ int device = get_joy_ref(p_device);
ERR_FAIL_COND(device == -1);
- input->joy_connection_changed(p_id, false, "");
+ input->joy_connection_changed(device_list[device].id, false, "");
device_list.write[device].free();
device_list.remove(device);
}
@@ -516,6 +514,13 @@ int JoypadOSX::get_joy_index(int p_id) const {
return -1;
}
+int JoypadOSX::get_joy_ref(IOHIDDeviceRef p_device) const {
+ for (int i = 0; i < device_list.size(); i++) {
+ if (device_list[i].device_ref == p_device) return i;
+ }
+ return -1;
+}
+
bool JoypadOSX::have_device(IOHIDDeviceRef p_device) const {
for (int i = 0; i < device_list.size(); i++) {
if (device_list[i].device_ref == p_device) {
@@ -558,6 +563,7 @@ void JoypadOSX::config_hid_manager(CFArrayRef p_matching_array) const {
IOHIDManagerSetDeviceMatchingMultiple(hid_manager, p_matching_array);
IOHIDManagerRegisterDeviceMatchingCallback(hid_manager, joypad_added_callback, NULL);
+ IOHIDManagerRegisterDeviceRemovalCallback(hid_manager, joypad_removed_callback, NULL);
IOHIDManagerScheduleWithRunLoop(hid_manager, runloop, GODOT_JOY_LOOP_RUN_MODE);
while (CFRunLoopRunInMode(GODOT_JOY_LOOP_RUN_MODE, 0, TRUE) == kCFRunLoopRunHandledSource) {
diff --git a/platform/osx/joypad_osx.h b/platform/osx/joypad_osx.h
index 388251016b..2c076b3680 100644
--- a/platform/osx/joypad_osx.h
+++ b/platform/osx/joypad_osx.h
@@ -103,6 +103,7 @@ private:
bool configure_joypad(IOHIDDeviceRef p_device_ref, joypad *p_joy);
int get_joy_index(int p_id) const;
+ int get_joy_ref(IOHIDDeviceRef p_device) const;
void poll_joypads() const;
void setup_joypad_objects();
@@ -115,7 +116,7 @@ public:
void process_joypads();
void _device_added(IOReturn p_res, IOHIDDeviceRef p_device);
- void _device_removed(int p_id);
+ void _device_removed(IOReturn p_res, IOHIDDeviceRef p_device);
JoypadOSX();
~JoypadOSX();
diff --git a/platform/osx/os_osx.mm b/platform/osx/os_osx.mm
index 952360c6aa..4c70beee00 100644
--- a/platform/osx/os_osx.mm
+++ b/platform/osx/os_osx.mm
@@ -44,7 +44,6 @@
#endif
#include "main/main.h"
-#include "semaphore_osx.h"
#include "servers/visual/visual_server_raster.h"
#include "servers/visual/visual_server_wrap_mt.h"
@@ -1464,8 +1463,6 @@ void OS_OSX::initialize_core() {
DirAccess::make_default<DirAccessOSX>(DirAccess::ACCESS_RESOURCES);
DirAccess::make_default<DirAccessOSX>(DirAccess::ACCESS_USERDATA);
DirAccess::make_default<DirAccessOSX>(DirAccess::ACCESS_FILESYSTEM);
-
- SemaphoreOSX::make_default();
}
static bool keyboard_layout_dirty = true;
diff --git a/platform/osx/semaphore_osx.cpp b/platform/osx/semaphore_osx.cpp
deleted file mode 100644
index e4e5991637..0000000000
--- a/platform/osx/semaphore_osx.cpp
+++ /dev/null
@@ -1,107 +0,0 @@
-/*************************************************************************/
-/* semaphore_osx.cpp */
-/*************************************************************************/
-/* This file is part of: */
-/* GODOT ENGINE */
-/* https://godotengine.org */
-/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 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. */
-/*************************************************************************/
-
-#include "semaphore_osx.h"
-
-#include <fcntl.h>
-#include <unistd.h>
-
-void cgsem_init(cgsem_t *cgsem) {
- int flags, fd, i;
-
- pipe(cgsem->pipefd);
-
- /* Make the pipes FD_CLOEXEC to allow them to close should we call
- * execv on restart. */
- for (i = 0; i < 2; i++) {
- fd = cgsem->pipefd[i];
- flags = fcntl(fd, F_GETFD, 0);
- flags |= FD_CLOEXEC;
- fcntl(fd, F_SETFD, flags);
- }
-}
-
-void cgsem_post(cgsem_t *cgsem) {
- const char buf = 1;
-
- write(cgsem->pipefd[1], &buf, 1);
-}
-
-void cgsem_wait(cgsem_t *cgsem) {
- char buf;
-
- read(cgsem->pipefd[0], &buf, 1);
-}
-
-void cgsem_destroy(cgsem_t *cgsem) {
- close(cgsem->pipefd[1]);
- close(cgsem->pipefd[0]);
-}
-
-#include "core/os/memory.h"
-
-#include <errno.h>
-
-Error SemaphoreOSX::wait() {
-
- cgsem_wait(&sem);
- return OK;
-}
-
-Error SemaphoreOSX::post() {
-
- cgsem_post(&sem);
-
- return OK;
-}
-int SemaphoreOSX::get() const {
-
- return 0;
-}
-
-SemaphoreOld *SemaphoreOSX::create_semaphore_osx() {
-
- return memnew(SemaphoreOSX);
-}
-
-void SemaphoreOSX::make_default() {
-
- create_func = create_semaphore_osx;
-}
-
-SemaphoreOSX::SemaphoreOSX() {
-
- cgsem_init(&sem);
-}
-
-SemaphoreOSX::~SemaphoreOSX() {
-
- cgsem_destroy(&sem);
-}
diff --git a/platform/server/SCsub b/platform/server/SCsub
index e8538f03a6..8364164114 100644
--- a/platform/server/SCsub
+++ b/platform/server/SCsub
@@ -10,7 +10,6 @@ common_server = [\
if sys.platform == "darwin":
common_server.append("#platform/osx/crash_handler_osx.mm")
- common_server.append("#platform/osx/semaphore_osx.cpp")
else:
common_server.append("#platform/x11/crash_handler_x11.cpp")
diff --git a/platform/server/os_server.cpp b/platform/server/os_server.cpp
index 8a66332ff1..3257ec261c 100644
--- a/platform/server/os_server.cpp
+++ b/platform/server/os_server.cpp
@@ -68,10 +68,6 @@ void OS_Server::initialize_core() {
crash_handler.initialize();
OS_Unix::initialize_core();
-
-#ifdef __APPLE__
- SemaphoreOSX::make_default();
-#endif
}
Error OS_Server::initialize(const VideoMode &p_desired, int p_video_driver, int p_audio_driver) {
diff --git a/platform/uwp/os_uwp.cpp b/platform/uwp/os_uwp.cpp
index 97c8dafe65..4ddb5463d0 100644
--- a/platform/uwp/os_uwp.cpp
+++ b/platform/uwp/os_uwp.cpp
@@ -140,7 +140,6 @@ void OS_UWP::initialize_core() {
//RedirectIOToConsole();
ThreadUWP::make_default();
- SemaphoreWindows::make_default();
RWLockWindows::make_default();
FileAccess::make_default<FileAccessWindows>(FileAccess::ACCESS_RESOURCES);
diff --git a/platform/windows/os_windows.cpp b/platform/windows/os_windows.cpp
index 7f4392c624..63f0cd53a4 100644
--- a/platform/windows/os_windows.cpp
+++ b/platform/windows/os_windows.cpp
@@ -48,7 +48,6 @@
#include "drivers/windows/dir_access_windows.h"
#include "drivers/windows/file_access_windows.h"
#include "drivers/windows/rw_lock_windows.h"
-#include "drivers/windows/semaphore_windows.h"
#include "drivers/windows/thread_windows.h"
#include "joypad_windows.h"
#include "lang_table.h"
@@ -228,7 +227,6 @@ void OS_Windows::initialize_core() {
borderless = false;
ThreadWindows::make_default();
- SemaphoreWindows::make_default();
RWLockWindows::make_default();
FileAccess::make_default<FileAccessWindows>(FileAccess::ACCESS_RESOURCES);
@@ -2724,7 +2722,7 @@ Error OS_Windows::execute(const String &p_path, const List<String> &p_arguments,
if (p_pipe_mutex) {
p_pipe_mutex->lock();
}
- (*r_pipe) += buf;
+ (*r_pipe) += String::utf8(buf);
if (p_pipe_mutex) {
p_pipe_mutex->unlock();
}
diff --git a/platform/x11/detect.py b/platform/x11/detect.py
index cd22ee9ff6..8952aa5e82 100644
--- a/platform/x11/detect.py
+++ b/platform/x11/detect.py
@@ -1,7 +1,7 @@
import os
import platform
import sys
-from methods import get_compiler_version, using_gcc, using_clang
+from methods import using_gcc, using_clang
def is_active():
diff --git a/scene/2d/camera_2d.cpp b/scene/2d/camera_2d.cpp
index 1419fb561e..a8860c3d81 100644
--- a/scene/2d/camera_2d.cpp
+++ b/scene/2d/camera_2d.cpp
@@ -128,9 +128,9 @@ Transform2D Camera2D::get_camera_transform() {
} else {
if (v_ofs < 0) {
- camera_pos.y = new_camera_pos.y + screen_size.y * 0.5 * drag_margin[MARGIN_TOP] * v_ofs;
- } else {
camera_pos.y = new_camera_pos.y + screen_size.y * 0.5 * drag_margin[MARGIN_BOTTOM] * v_ofs;
+ } else {
+ camera_pos.y = new_camera_pos.y + screen_size.y * 0.5 * drag_margin[MARGIN_TOP] * v_ofs;
}
v_offset_changed = false;
diff --git a/scene/3d/collision_shape.cpp b/scene/3d/collision_shape.cpp
index 35e4a61cd6..d825c8daf7 100644
--- a/scene/3d/collision_shape.cpp
+++ b/scene/3d/collision_shape.cpp
@@ -123,6 +123,14 @@ String CollisionShape::get_configuration_warning() const {
return TTR("A shape must be provided for CollisionShape to function. Please create a shape resource for it.");
}
+ if (Object::cast_to<RigidBody>(get_parent())) {
+ if (Object::cast_to<ConcavePolygonShape>(*shape)) {
+ if (Object::cast_to<RigidBody>(get_parent())->get_mode() != RigidBody::MODE_STATIC) {
+ return TTR("ConcavePolygonShape doesn't support RigidBody in another mode than static.");
+ }
+ }
+ }
+
return String();
}
diff --git a/scene/3d/physics_body.cpp b/scene/3d/physics_body.cpp
index eba45a5604..2f8dc31cb6 100644
--- a/scene/3d/physics_body.cpp
+++ b/scene/3d/physics_body.cpp
@@ -518,6 +518,7 @@ void RigidBody::set_mode(Mode p_mode) {
PhysicsServer::get_singleton()->body_set_mode(get_rid(), PhysicsServer::BODY_MODE_KINEMATIC);
} break;
}
+ update_configuration_warning();
}
RigidBody::Mode RigidBody::get_mode() const {
diff --git a/scene/animation/tween.cpp b/scene/animation/tween.cpp
index 4b8b537d43..628568afbb 100644
--- a/scene/animation/tween.cpp
+++ b/scene/animation/tween.cpp
@@ -838,23 +838,22 @@ float Tween::get_speed_scale() const {
return speed_scale;
}
-bool Tween::start() {
+void Tween::start() {
- ERR_FAIL_COND_V_MSG(!is_inside_tree(), false, "Tween was not added to the SceneTree!");
+ ERR_FAIL_COND_MSG(!is_inside_tree(), "Tween was not added to the SceneTree!");
// Are there any pending updates?
if (pending_update != 0) {
// Start the tweens after deferring
call_deferred("start");
- return true;
+ return;
}
// We want to be activated
set_active(true);
- return true;
}
-bool Tween::reset(Object *p_object, StringName p_key) {
+void Tween::reset(Object *p_object, StringName p_key) {
// Find all interpolations that use the same object and target string
pending_update++;
for (List<InterpolateData>::Element *E = interpolates.front(); E; E = E->next()) {
@@ -876,10 +875,9 @@ bool Tween::reset(Object *p_object, StringName p_key) {
}
}
pending_update--;
- return true;
}
-bool Tween::reset_all() {
+void Tween::reset_all() {
// Go through all interpolations
pending_update++;
for (List<InterpolateData>::Element *E = interpolates.front(); E; E = E->next()) {
@@ -893,10 +891,9 @@ bool Tween::reset_all() {
_apply_tween_value(data, data.initial_val);
}
pending_update--;
- return true;
}
-bool Tween::stop(Object *p_object, StringName p_key) {
+void Tween::stop(Object *p_object, StringName p_key) {
// Find the tween that has the given target object and string key
pending_update++;
for (List<InterpolateData>::Element *E = interpolates.front(); E; E = E->next()) {
@@ -913,10 +910,9 @@ bool Tween::stop(Object *p_object, StringName p_key) {
data.active = false;
}
pending_update--;
- return true;
}
-bool Tween::stop_all() {
+void Tween::stop_all() {
// We no longer need to be active since all tweens have been stopped
set_active(false);
@@ -928,10 +924,9 @@ bool Tween::stop_all() {
data.active = false;
}
pending_update--;
- return true;
}
-bool Tween::resume(Object *p_object, StringName p_key) {
+void Tween::resume(Object *p_object, StringName p_key) {
// We need to be activated
// TODO: What if no tween is found??
set_active(true);
@@ -950,10 +945,9 @@ bool Tween::resume(Object *p_object, StringName p_key) {
data.active = true;
}
pending_update--;
- return true;
}
-bool Tween::resume_all() {
+void Tween::resume_all() {
// Set ourselves active so we can process tweens
// TODO: What if there are no tweens? We get set to active for no reason!
set_active(true);
@@ -966,14 +960,13 @@ bool Tween::resume_all() {
data.active = true;
}
pending_update--;
- return true;
}
-bool Tween::remove(Object *p_object, StringName p_key) {
+void Tween::remove(Object *p_object, StringName p_key) {
// If we are still updating, call this function again later
if (pending_update != 0) {
call_deferred("remove", p_object, p_key);
- return true;
+ return;
}
// For each interpolation...
@@ -996,7 +989,6 @@ bool Tween::remove(Object *p_object, StringName p_key) {
// Erase it
interpolates.erase(E->get());
}
- return true;
}
void Tween::_remove_by_uid(int uid) {
@@ -1026,11 +1018,11 @@ void Tween::_push_interpolate_data(InterpolateData &p_data) {
pending_update--;
}
-bool Tween::remove_all() {
+void Tween::remove_all() {
// If we are still updating, call this function again later
if (pending_update != 0) {
call_deferred("remove_all");
- return true;
+ return;
}
// We no longer need to be active
set_active(false);
@@ -1038,11 +1030,9 @@ bool Tween::remove_all() {
// Clear out all interpolations and reset the uid
interpolates.clear();
uid = 0;
-
- return true;
}
-bool Tween::seek(real_t p_time) {
+void Tween::seek(real_t p_time) {
// Go through each interpolation...
pending_update++;
for (List<InterpolateData>::Element *E = interpolates.front(); E; E = E->next()) {
@@ -1076,7 +1066,6 @@ bool Tween::seek(real_t p_time) {
_apply_tween_value(data, result);
}
pending_update--;
- return true;
}
real_t Tween::tell() const {
@@ -1260,7 +1249,7 @@ bool Tween::_calc_delta_val(const Variant &p_initial_val, const Variant &p_final
return true;
}
-bool Tween::_build_interpolation(InterpolateType p_interpolation_type, Object *p_object, NodePath *p_property, StringName *p_method, Variant p_initial_val, Variant p_final_val, real_t p_duration, TransitionType p_trans_type, EaseType p_ease_type, real_t p_delay) {
+void Tween::_build_interpolation(InterpolateType p_interpolation_type, Object *p_object, NodePath *p_property, StringName *p_method, Variant p_initial_val, Variant p_final_val, real_t p_duration, TransitionType p_trans_type, EaseType p_ease_type, real_t p_delay) {
// TODO: Add initialization+implementation for remaining interpolation types
// TODO: Fix this method's organization to take advantage of the type
@@ -1275,28 +1264,28 @@ bool Tween::_build_interpolation(InterpolateType p_interpolation_type, Object *p
// Validate and apply interpolation data
// Give it the object
- ERR_FAIL_COND_V_MSG(p_object == NULL, false, "Invalid object provided to Tween.");
+ ERR_FAIL_COND_MSG(p_object == NULL, "Invalid object provided to Tween.");
data.id = p_object->get_instance_id();
// Validate the initial and final values
- ERR_FAIL_COND_V_MSG(p_initial_val.get_type() != p_final_val.get_type(), false, "Initial value type '" + Variant::get_type_name(p_initial_val.get_type()) + "' does not match final value type '" + Variant::get_type_name(p_final_val.get_type()) + "'.");
+ ERR_FAIL_COND_MSG(p_initial_val.get_type() != p_final_val.get_type(), "Initial value type '" + Variant::get_type_name(p_initial_val.get_type()) + "' does not match final value type '" + Variant::get_type_name(p_final_val.get_type()) + "'.");
data.initial_val = p_initial_val;
data.final_val = p_final_val;
// Check the Duration
- ERR_FAIL_COND_V_MSG(p_duration < 0, false, "Only non-negative duration values allowed in Tweens.");
+ ERR_FAIL_COND_MSG(p_duration < 0, "Only non-negative duration values allowed in Tweens.");
data.duration = p_duration;
// Tween Delay
- ERR_FAIL_COND_V_MSG(p_delay < 0, false, "Only non-negative delay values allowed in Tweens.");
+ ERR_FAIL_COND_MSG(p_delay < 0, "Only non-negative delay values allowed in Tweens.");
data.delay = p_delay;
// Transition type
- ERR_FAIL_COND_V_MSG(p_trans_type < 0 || p_trans_type >= TRANS_COUNT, false, "Invalid transition type provided to Tween.");
+ ERR_FAIL_COND_MSG(p_trans_type < 0 || p_trans_type >= TRANS_COUNT, "Invalid transition type provided to Tween.");
data.trans_type = p_trans_type;
// Easing type
- ERR_FAIL_COND_V_MSG(p_ease_type < 0 || p_ease_type >= EASE_COUNT, false, "Invalid easing type provided to Tween.");
+ ERR_FAIL_COND_MSG(p_ease_type < 0 || p_ease_type >= EASE_COUNT, "Invalid easing type provided to Tween.");
data.ease_type = p_ease_type;
// Is the property defined?
@@ -1304,7 +1293,7 @@ bool Tween::_build_interpolation(InterpolateType p_interpolation_type, Object *p
// Check that the object actually contains the given property
bool prop_valid = false;
p_object->get_indexed(p_property->get_subnames(), &prop_valid);
- ERR_FAIL_COND_V_MSG(!prop_valid, false, "Tween target object has no property named: " + p_property->get_concatenated_subnames() + ".");
+ ERR_FAIL_COND_MSG(!prop_valid, "Tween target object has no property named: " + p_property->get_concatenated_subnames() + ".");
data.key = p_property->get_subnames();
data.concatenated_key = p_property->get_concatenated_subnames();
@@ -1313,7 +1302,7 @@ bool Tween::_build_interpolation(InterpolateType p_interpolation_type, Object *p
// Is the method defined?
if (p_method) {
// Does the object even have the requested method?
- ERR_FAIL_COND_V_MSG(!p_object->has_method(*p_method), false, "Tween target object has no method named: " + *p_method + ".");
+ ERR_FAIL_COND_MSG(!p_object->has_method(*p_method), "Tween target object has no method named: " + *p_method + ".");
data.key.push_back(*p_method);
data.concatenated_key = *p_method;
@@ -1321,18 +1310,17 @@ bool Tween::_build_interpolation(InterpolateType p_interpolation_type, Object *p
// Is there not a valid delta?
if (!_calc_delta_val(data.initial_val, data.final_val, data.delta_val))
- return false;
+ return;
// Add this interpolation to the total
_push_interpolate_data(data);
- return true;
}
-bool Tween::interpolate_property(Object *p_object, NodePath p_property, Variant p_initial_val, Variant p_final_val, real_t p_duration, TransitionType p_trans_type, EaseType p_ease_type, real_t p_delay) {
+void Tween::interpolate_property(Object *p_object, NodePath p_property, Variant p_initial_val, Variant p_final_val, real_t p_duration, TransitionType p_trans_type, EaseType p_ease_type, real_t p_delay) {
// If we are busy updating, call this function again later
if (pending_update != 0) {
_add_pending_command("interpolate_property", p_object, p_property, p_initial_val, p_final_val, p_duration, p_trans_type, p_ease_type, p_delay);
- return true;
+ return;
}
// Get the property from the node path
@@ -1347,15 +1335,14 @@ bool Tween::interpolate_property(Object *p_object, NodePath p_property, Variant
if (p_final_val.get_type() == Variant::INT) p_final_val = p_final_val.operator real_t();
// Build the interpolation data
- bool result = _build_interpolation(INTER_PROPERTY, p_object, &p_property, NULL, p_initial_val, p_final_val, p_duration, p_trans_type, p_ease_type, p_delay);
- return result;
+ _build_interpolation(INTER_PROPERTY, p_object, &p_property, NULL, p_initial_val, p_final_val, p_duration, p_trans_type, p_ease_type, p_delay);
}
-bool Tween::interpolate_method(Object *p_object, StringName p_method, Variant p_initial_val, Variant p_final_val, real_t p_duration, TransitionType p_trans_type, EaseType p_ease_type, real_t p_delay) {
+void Tween::interpolate_method(Object *p_object, StringName p_method, Variant p_initial_val, Variant p_final_val, real_t p_duration, TransitionType p_trans_type, EaseType p_ease_type, real_t p_delay) {
// If we are busy updating, call this function again later
if (pending_update != 0) {
_add_pending_command("interpolate_method", p_object, p_method, p_initial_val, p_final_val, p_duration, p_trans_type, p_ease_type, p_delay);
- return true;
+ return;
}
// Convert any integers into REALs as they are better for interpolation
@@ -1363,25 +1350,24 @@ bool Tween::interpolate_method(Object *p_object, StringName p_method, Variant p_
if (p_final_val.get_type() == Variant::INT) p_final_val = p_final_val.operator real_t();
// Build the interpolation data
- bool result = _build_interpolation(INTER_METHOD, p_object, NULL, &p_method, p_initial_val, p_final_val, p_duration, p_trans_type, p_ease_type, p_delay);
- return result;
+ _build_interpolation(INTER_METHOD, p_object, NULL, &p_method, p_initial_val, p_final_val, p_duration, p_trans_type, p_ease_type, p_delay);
}
-bool Tween::interpolate_callback(Object *p_object, real_t p_duration, String p_callback, VARIANT_ARG_DECLARE) {
+void Tween::interpolate_callback(Object *p_object, real_t p_duration, String p_callback, VARIANT_ARG_DECLARE) {
// If we are already updating, call this function again later
if (pending_update != 0) {
_add_pending_command("interpolate_callback", p_object, p_duration, p_callback, p_arg1, p_arg2, p_arg3, p_arg4, p_arg5);
- return true;
+ return;
}
// Check that the target object is valid
- ERR_FAIL_COND_V(p_object == NULL, false);
+ ERR_FAIL_COND(p_object == NULL);
// Duration cannot be negative
- ERR_FAIL_COND_V(p_duration < 0, false);
+ ERR_FAIL_COND(p_duration < 0);
// Check whether the object even has the callback
- ERR_FAIL_COND_V_MSG(!p_object->has_method(p_callback), false, "Object has no callback named: " + p_callback + ".");
+ ERR_FAIL_COND_MSG(!p_object->has_method(p_callback), "Object has no callback named: " + p_callback + ".");
// Build a new InterpolationData
InterpolateData data;
@@ -1422,24 +1408,23 @@ bool Tween::interpolate_callback(Object *p_object, real_t p_duration, String p_c
// Add the new interpolation
_push_interpolate_data(data);
- return true;
}
-bool Tween::interpolate_deferred_callback(Object *p_object, real_t p_duration, String p_callback, VARIANT_ARG_DECLARE) {
+void Tween::interpolate_deferred_callback(Object *p_object, real_t p_duration, String p_callback, VARIANT_ARG_DECLARE) {
// If we are already updating, call this function again later
if (pending_update != 0) {
_add_pending_command("interpolate_deferred_callback", p_object, p_duration, p_callback, p_arg1, p_arg2, p_arg3, p_arg4, p_arg5);
- return true;
+ return;
}
// Check that the target object is valid
- ERR_FAIL_COND_V(p_object == NULL, false);
+ ERR_FAIL_COND(p_object == NULL);
// No negative durations allowed
- ERR_FAIL_COND_V(p_duration < 0, false);
+ ERR_FAIL_COND(p_duration < 0);
// Confirm the callback exists on the object
- ERR_FAIL_COND_V_MSG(!p_object->has_method(p_callback), false, "Object has no callback named: " + p_callback + ".");
+ ERR_FAIL_COND_MSG(!p_object->has_method(p_callback), "Object has no callback named: " + p_callback + ".");
// Create a new InterpolateData for the callback
InterpolateData data;
@@ -1480,14 +1465,13 @@ bool Tween::interpolate_deferred_callback(Object *p_object, real_t p_duration, S
// Add the new interpolation
_push_interpolate_data(data);
- return true;
}
-bool Tween::follow_property(Object *p_object, NodePath p_property, Variant p_initial_val, Object *p_target, NodePath p_target_property, real_t p_duration, TransitionType p_trans_type, EaseType p_ease_type, real_t p_delay) {
+void Tween::follow_property(Object *p_object, NodePath p_property, Variant p_initial_val, Object *p_target, NodePath p_target_property, real_t p_duration, TransitionType p_trans_type, EaseType p_ease_type, real_t p_delay) {
// If we are already updating, call this function again later
if (pending_update != 0) {
_add_pending_command("follow_property", p_object, p_property, p_initial_val, p_target, p_target_property, p_duration, p_trans_type, p_ease_type, p_delay);
- return true;
+ return;
}
// Get the two properties from their paths
@@ -1502,33 +1486,33 @@ bool Tween::follow_property(Object *p_object, NodePath p_property, Variant p_ini
if (p_initial_val.get_type() == Variant::INT) p_initial_val = p_initial_val.operator real_t();
// Confirm the source and target objects are valid
- ERR_FAIL_COND_V(p_object == NULL, false);
- ERR_FAIL_COND_V(p_target == NULL, false);
+ ERR_FAIL_COND(p_object == NULL);
+ ERR_FAIL_COND(p_target == NULL);
// No negative durations
- ERR_FAIL_COND_V(p_duration < 0, false);
+ ERR_FAIL_COND(p_duration < 0);
// Ensure transition and easing types are valid
- ERR_FAIL_COND_V(p_trans_type < 0 || p_trans_type >= TRANS_COUNT, false);
- ERR_FAIL_COND_V(p_ease_type < 0 || p_ease_type >= EASE_COUNT, false);
+ ERR_FAIL_COND(p_trans_type < 0 || p_trans_type >= TRANS_COUNT);
+ ERR_FAIL_COND(p_ease_type < 0 || p_ease_type >= EASE_COUNT);
// No negative delays
- ERR_FAIL_COND_V(p_delay < 0, false);
+ ERR_FAIL_COND(p_delay < 0);
// Confirm the source and target objects have the desired properties
bool prop_valid = false;
p_object->get_indexed(p_property.get_subnames(), &prop_valid);
- ERR_FAIL_COND_V(!prop_valid, false);
+ ERR_FAIL_COND(!prop_valid);
bool target_prop_valid = false;
Variant target_val = p_target->get_indexed(p_target_property.get_subnames(), &target_prop_valid);
- ERR_FAIL_COND_V(!target_prop_valid, false);
+ ERR_FAIL_COND(!target_prop_valid);
// Convert target INT to FLOAT since it is better for interpolation
if (target_val.get_type() == Variant::INT) target_val = target_val.operator real_t();
// Verify that the target value and initial value are the same type
- ERR_FAIL_COND_V(target_val.get_type() != p_initial_val.get_type(), false);
+ ERR_FAIL_COND(target_val.get_type() != p_initial_val.get_type());
// Create a new InterpolateData
InterpolateData data;
@@ -1551,44 +1535,43 @@ bool Tween::follow_property(Object *p_object, NodePath p_property, Variant p_ini
// Add the interpolation
_push_interpolate_data(data);
- return true;
}
-bool Tween::follow_method(Object *p_object, StringName p_method, Variant p_initial_val, Object *p_target, StringName p_target_method, real_t p_duration, TransitionType p_trans_type, EaseType p_ease_type, real_t p_delay) {
+void Tween::follow_method(Object *p_object, StringName p_method, Variant p_initial_val, Object *p_target, StringName p_target_method, real_t p_duration, TransitionType p_trans_type, EaseType p_ease_type, real_t p_delay) {
// If we are currently updating, call this function again later
if (pending_update != 0) {
_add_pending_command("follow_method", p_object, p_method, p_initial_val, p_target, p_target_method, p_duration, p_trans_type, p_ease_type, p_delay);
- return true;
+ return;
}
// Convert initial INT values to FLOAT as they are better for interpolation
if (p_initial_val.get_type() == Variant::INT) p_initial_val = p_initial_val.operator real_t();
// Verify the source and target objects are valid
- ERR_FAIL_COND_V(p_object == NULL, false);
- ERR_FAIL_COND_V(p_target == NULL, false);
+ ERR_FAIL_COND(p_object == NULL);
+ ERR_FAIL_COND(p_target == NULL);
// No negative durations
- ERR_FAIL_COND_V(p_duration < 0, false);
+ ERR_FAIL_COND(p_duration < 0);
// Ensure that the transition and ease types are valid
- ERR_FAIL_COND_V(p_trans_type < 0 || p_trans_type >= TRANS_COUNT, false);
- ERR_FAIL_COND_V(p_ease_type < 0 || p_ease_type >= EASE_COUNT, false);
+ ERR_FAIL_COND(p_trans_type < 0 || p_trans_type >= TRANS_COUNT);
+ ERR_FAIL_COND(p_ease_type < 0 || p_ease_type >= EASE_COUNT);
// No negative delays
- ERR_FAIL_COND_V(p_delay < 0, false);
+ ERR_FAIL_COND(p_delay < 0);
// Confirm both objects have the target methods
- ERR_FAIL_COND_V_MSG(!p_object->has_method(p_method), false, "Object has no method named: " + p_method + ".");
- ERR_FAIL_COND_V_MSG(!p_target->has_method(p_target_method), false, "Target has no method named: " + p_target_method + ".");
+ ERR_FAIL_COND_MSG(!p_object->has_method(p_method), "Object has no method named: " + p_method + ".");
+ ERR_FAIL_COND_MSG(!p_target->has_method(p_target_method), "Target has no method named: " + p_target_method + ".");
// Call the method to get the target value
Callable::CallError error;
Variant target_val = p_target->call(p_target_method, NULL, 0, error);
- ERR_FAIL_COND_V(error.error != Callable::CallError::CALL_OK, false);
+ ERR_FAIL_COND(error.error != Callable::CallError::CALL_OK);
// Convert target INT values to FLOAT as they are better for interpolation
if (target_val.get_type() == Variant::INT) target_val = target_val.operator real_t();
- ERR_FAIL_COND_V(target_val.get_type() != p_initial_val.get_type(), false);
+ ERR_FAIL_COND(target_val.get_type() != p_initial_val.get_type());
// Make the new InterpolateData for the method follow
InterpolateData data;
@@ -1611,14 +1594,13 @@ bool Tween::follow_method(Object *p_object, StringName p_method, Variant p_initi
// Add the new interpolation
_push_interpolate_data(data);
- return true;
}
-bool Tween::targeting_property(Object *p_object, NodePath p_property, Object *p_initial, NodePath p_initial_property, Variant p_final_val, real_t p_duration, TransitionType p_trans_type, EaseType p_ease_type, real_t p_delay) {
+void Tween::targeting_property(Object *p_object, NodePath p_property, Object *p_initial, NodePath p_initial_property, Variant p_final_val, real_t p_duration, TransitionType p_trans_type, EaseType p_ease_type, real_t p_delay) {
// If we are currently updating, call this function again later
if (pending_update != 0) {
_add_pending_command("targeting_property", p_object, p_property, p_initial, p_initial_property, p_final_val, p_duration, p_trans_type, p_ease_type, p_delay);
- return true;
+ return;
}
// Grab the target property and the target property
p_property = p_property.get_as_property_path();
@@ -1628,31 +1610,31 @@ bool Tween::targeting_property(Object *p_object, NodePath p_property, Object *p_
if (p_final_val.get_type() == Variant::INT) p_final_val = p_final_val.operator real_t();
// Verify both objects are valid
- ERR_FAIL_COND_V(p_object == NULL, false);
- ERR_FAIL_COND_V(p_initial == NULL, false);
+ ERR_FAIL_COND(p_object == NULL);
+ ERR_FAIL_COND(p_initial == NULL);
// No negative durations
- ERR_FAIL_COND_V(p_duration < 0, false);
+ ERR_FAIL_COND(p_duration < 0);
// Ensure transition and easing types are valid
- ERR_FAIL_COND_V(p_trans_type < 0 || p_trans_type >= TRANS_COUNT, false);
- ERR_FAIL_COND_V(p_ease_type < 0 || p_ease_type >= EASE_COUNT, false);
+ ERR_FAIL_COND(p_trans_type < 0 || p_trans_type >= TRANS_COUNT);
+ ERR_FAIL_COND(p_ease_type < 0 || p_ease_type >= EASE_COUNT);
// No negative delays
- ERR_FAIL_COND_V(p_delay < 0, false);
+ ERR_FAIL_COND(p_delay < 0);
// Ensure the initial and target properties exist on their objects
bool prop_valid = false;
p_object->get_indexed(p_property.get_subnames(), &prop_valid);
- ERR_FAIL_COND_V(!prop_valid, false);
+ ERR_FAIL_COND(!prop_valid);
bool initial_prop_valid = false;
Variant initial_val = p_initial->get_indexed(p_initial_property.get_subnames(), &initial_prop_valid);
- ERR_FAIL_COND_V(!initial_prop_valid, false);
+ ERR_FAIL_COND(!initial_prop_valid);
// Convert the initial INT value to FLOAT as it is better for interpolation
if (initial_val.get_type() == Variant::INT) initial_val = initial_val.operator real_t();
- ERR_FAIL_COND_V(initial_val.get_type() != p_final_val.get_type(), false);
+ ERR_FAIL_COND(initial_val.get_type() != p_final_val.get_type());
// Build the InterpolateData object
InterpolateData data;
@@ -1675,50 +1657,50 @@ bool Tween::targeting_property(Object *p_object, NodePath p_property, Object *p_
data.delay = p_delay;
// Ensure there is a valid delta
- if (!_calc_delta_val(data.initial_val, data.final_val, data.delta_val))
- return false;
+ if (!_calc_delta_val(data.initial_val, data.final_val, data.delta_val)) {
+ return;
+ }
// Add the interpolation
_push_interpolate_data(data);
- return true;
}
-bool Tween::targeting_method(Object *p_object, StringName p_method, Object *p_initial, StringName p_initial_method, Variant p_final_val, real_t p_duration, TransitionType p_trans_type, EaseType p_ease_type, real_t p_delay) {
+void Tween::targeting_method(Object *p_object, StringName p_method, Object *p_initial, StringName p_initial_method, Variant p_final_val, real_t p_duration, TransitionType p_trans_type, EaseType p_ease_type, real_t p_delay) {
// If we are currently updating, call this function again later
if (pending_update != 0) {
_add_pending_command("targeting_method", p_object, p_method, p_initial, p_initial_method, p_final_val, p_duration, p_trans_type, p_ease_type, p_delay);
- return true;
+ return;
}
// Convert final INT values to FLOAT as they are better for interpolation
if (p_final_val.get_type() == Variant::INT) p_final_val = p_final_val.operator real_t();
// Make sure the given objects are valid
- ERR_FAIL_COND_V(p_object == NULL, false);
- ERR_FAIL_COND_V(p_initial == NULL, false);
+ ERR_FAIL_COND(p_object == NULL);
+ ERR_FAIL_COND(p_initial == NULL);
// No negative durations
- ERR_FAIL_COND_V(p_duration < 0, false);
+ ERR_FAIL_COND(p_duration < 0);
// Ensure transition and easing types are valid
- ERR_FAIL_COND_V(p_trans_type < 0 || p_trans_type >= TRANS_COUNT, false);
- ERR_FAIL_COND_V(p_ease_type < 0 || p_ease_type >= EASE_COUNT, false);
+ ERR_FAIL_COND(p_trans_type < 0 || p_trans_type >= TRANS_COUNT);
+ ERR_FAIL_COND(p_ease_type < 0 || p_ease_type >= EASE_COUNT);
// No negative delays
- ERR_FAIL_COND_V(p_delay < 0, false);
+ ERR_FAIL_COND(p_delay < 0);
// Make sure both objects have the given method
- ERR_FAIL_COND_V_MSG(!p_object->has_method(p_method), false, "Object has no method named: " + p_method + ".");
- ERR_FAIL_COND_V_MSG(!p_initial->has_method(p_initial_method), false, "Initial Object has no method named: " + p_initial_method + ".");
+ ERR_FAIL_COND_MSG(!p_object->has_method(p_method), "Object has no method named: " + p_method + ".");
+ ERR_FAIL_COND_MSG(!p_initial->has_method(p_initial_method), "Initial Object has no method named: " + p_initial_method + ".");
// Call the method to get the initial value
Callable::CallError error;
Variant initial_val = p_initial->call(p_initial_method, NULL, 0, error);
- ERR_FAIL_COND_V(error.error != Callable::CallError::CALL_OK, false);
+ ERR_FAIL_COND(error.error != Callable::CallError::CALL_OK);
// Convert initial INT values to FLOAT as they aer better for interpolation
if (initial_val.get_type() == Variant::INT) initial_val = initial_val.operator real_t();
- ERR_FAIL_COND_V(initial_val.get_type() != p_final_val.get_type(), false);
+ ERR_FAIL_COND(initial_val.get_type() != p_final_val.get_type());
// Build the new InterpolateData object
InterpolateData data;
@@ -1741,12 +1723,12 @@ bool Tween::targeting_method(Object *p_object, StringName p_method, Object *p_in
data.delay = p_delay;
// Ensure there is a valid delta
- if (!_calc_delta_val(data.initial_val, data.final_val, data.delta_val))
- return false;
+ if (!_calc_delta_val(data.initial_val, data.final_val, data.delta_val)) {
+ return;
+ }
// Add the interpolation
_push_interpolate_data(data);
- return true;
}
Tween::Tween() {
diff --git a/scene/animation/tween.h b/scene/animation/tween.h
index f1218cd698..f74df50f68 100644
--- a/scene/animation/tween.h
+++ b/scene/animation/tween.h
@@ -142,7 +142,7 @@ private:
void _tween_process(float p_delta);
void _remove_by_uid(int uid);
void _push_interpolate_data(InterpolateData &p_data);
- bool _build_interpolation(InterpolateType p_interpolation_type, Object *p_object, NodePath *p_property, StringName *p_method, Variant p_initial_val, Variant p_final_val, real_t p_duration, TransitionType p_trans_type, EaseType p_ease_type, real_t p_delay);
+ void _build_interpolation(InterpolateType p_interpolation_type, Object *p_object, NodePath *p_property, StringName *p_method, Variant p_initial_val, Variant p_final_val, real_t p_duration, TransitionType p_trans_type, EaseType p_ease_type, real_t p_delay);
protected:
bool _set(const StringName &p_name, const Variant &p_value);
@@ -165,28 +165,28 @@ public:
void set_speed_scale(float p_speed);
float get_speed_scale() const;
- bool start();
- bool reset(Object *p_object, StringName p_key);
- bool reset_all();
- bool stop(Object *p_object, StringName p_key);
- bool stop_all();
- bool resume(Object *p_object, StringName p_key);
- bool resume_all();
- bool remove(Object *p_object, StringName p_key);
- bool remove_all();
-
- bool seek(real_t p_time);
+ void start();
+ void reset(Object *p_object, StringName p_key);
+ void reset_all();
+ void stop(Object *p_object, StringName p_key);
+ void stop_all();
+ void resume(Object *p_object, StringName p_key);
+ void resume_all();
+ void remove(Object *p_object, StringName p_key);
+ void remove_all();
+
+ void seek(real_t p_time);
real_t tell() const;
real_t get_runtime() const;
- bool interpolate_property(Object *p_object, NodePath p_property, Variant p_initial_val, Variant p_final_val, real_t p_duration, TransitionType p_trans_type = TRANS_LINEAR, EaseType p_ease_type = EASE_IN_OUT, real_t p_delay = 0);
- bool interpolate_method(Object *p_object, StringName p_method, Variant p_initial_val, Variant p_final_val, real_t p_duration, TransitionType p_trans_type = TRANS_LINEAR, EaseType p_ease_type = EASE_IN_OUT, real_t p_delay = 0);
- bool interpolate_callback(Object *p_object, real_t p_duration, String p_callback, VARIANT_ARG_DECLARE);
- bool interpolate_deferred_callback(Object *p_object, real_t p_duration, String p_callback, VARIANT_ARG_DECLARE);
- bool follow_property(Object *p_object, NodePath p_property, Variant p_initial_val, Object *p_target, NodePath p_target_property, real_t p_duration, TransitionType p_trans_type = TRANS_LINEAR, EaseType p_ease_type = EASE_IN_OUT, real_t p_delay = 0);
- bool follow_method(Object *p_object, StringName p_method, Variant p_initial_val, Object *p_target, StringName p_target_method, real_t p_duration, TransitionType p_trans_type = TRANS_LINEAR, EaseType p_ease_type = EASE_IN_OUT, real_t p_delay = 0);
- bool targeting_property(Object *p_object, NodePath p_property, Object *p_initial, NodePath p_initial_property, Variant p_final_val, real_t p_duration, TransitionType p_trans_type = TRANS_LINEAR, EaseType p_ease_type = EASE_IN_OUT, real_t p_delay = 0);
- bool targeting_method(Object *p_object, StringName p_method, Object *p_initial, StringName p_initial_method, Variant p_final_val, real_t p_duration, TransitionType p_trans_type = TRANS_LINEAR, EaseType p_ease_type = EASE_IN_OUT, real_t p_delay = 0);
+ void interpolate_property(Object *p_object, NodePath p_property, Variant p_initial_val, Variant p_final_val, real_t p_duration, TransitionType p_trans_type = TRANS_LINEAR, EaseType p_ease_type = EASE_IN_OUT, real_t p_delay = 0);
+ void interpolate_method(Object *p_object, StringName p_method, Variant p_initial_val, Variant p_final_val, real_t p_duration, TransitionType p_trans_type = TRANS_LINEAR, EaseType p_ease_type = EASE_IN_OUT, real_t p_delay = 0);
+ void interpolate_callback(Object *p_object, real_t p_duration, String p_callback, VARIANT_ARG_DECLARE);
+ void interpolate_deferred_callback(Object *p_object, real_t p_duration, String p_callback, VARIANT_ARG_DECLARE);
+ void follow_property(Object *p_object, NodePath p_property, Variant p_initial_val, Object *p_target, NodePath p_target_property, real_t p_duration, TransitionType p_trans_type = TRANS_LINEAR, EaseType p_ease_type = EASE_IN_OUT, real_t p_delay = 0);
+ void follow_method(Object *p_object, StringName p_method, Variant p_initial_val, Object *p_target, StringName p_target_method, real_t p_duration, TransitionType p_trans_type = TRANS_LINEAR, EaseType p_ease_type = EASE_IN_OUT, real_t p_delay = 0);
+ void targeting_property(Object *p_object, NodePath p_property, Object *p_initial, NodePath p_initial_property, Variant p_final_val, real_t p_duration, TransitionType p_trans_type = TRANS_LINEAR, EaseType p_ease_type = EASE_IN_OUT, real_t p_delay = 0);
+ void targeting_method(Object *p_object, StringName p_method, Object *p_initial, StringName p_initial_method, Variant p_final_val, real_t p_duration, TransitionType p_trans_type = TRANS_LINEAR, EaseType p_ease_type = EASE_IN_OUT, real_t p_delay = 0);
Tween();
~Tween();
diff --git a/scene/gui/file_dialog.cpp b/scene/gui/file_dialog.cpp
index 1d813d8081..3be77ff4b3 100644
--- a/scene/gui/file_dialog.cpp
+++ b/scene/gui/file_dialog.cpp
@@ -135,7 +135,8 @@ Vector<String> FileDialog::get_selected_files() const {
void FileDialog::update_dir() {
- dir->set_text(dir_access->get_current_dir());
+ dir->set_text(dir_access->get_current_dir(false));
+
if (drives->is_visible()) {
drives->select(dir_access->get_current_drive());
}
@@ -789,6 +790,12 @@ void FileDialog::_update_drives() {
drives->hide();
} else {
drives->clear();
+ Node *dp = drives->get_parent();
+ if (dp) {
+ dp->remove_child(drives);
+ }
+ dp = dir_access->drives_are_shortcuts() ? shortcuts_container : drives_container;
+ dp->add_child(drives);
drives->show();
for (int i = 0; i < dir_access->get_drive_count(); i++) {
@@ -890,11 +897,14 @@ FileDialog::FileDialog() {
hbc->add_child(dir_up);
dir_up->connect("pressed", callable_mp(this, &FileDialog::_go_up));
+ hbc->add_child(memnew(Label(RTR("Path:"))));
+
+ drives_container = memnew(HBoxContainer);
+ hbc->add_child(drives_container);
+
drives = memnew(OptionButton);
- hbc->add_child(drives);
drives->connect("item_selected", callable_mp(this, &FileDialog::_select_drive));
- hbc->add_child(memnew(Label(RTR("Path:"))));
dir = memnew(LineEdit);
hbc->add_child(dir);
dir->set_h_size_flags(SIZE_EXPAND_FILL);
@@ -911,6 +921,9 @@ FileDialog::FileDialog() {
show_hidden->connect("toggled", callable_mp(this, &FileDialog::set_show_hidden_files));
hbc->add_child(show_hidden);
+ shortcuts_container = memnew(HBoxContainer);
+ hbc->add_child(shortcuts_container);
+
makedir = memnew(Button);
makedir->set_text(RTR("Create Folder"));
makedir->connect("pressed", callable_mp(this, &FileDialog::_make_dir));
diff --git a/scene/gui/file_dialog.h b/scene/gui/file_dialog.h
index 9f6650c276..a7dc101d12 100644
--- a/scene/gui/file_dialog.h
+++ b/scene/gui/file_dialog.h
@@ -76,6 +76,8 @@ private:
VBoxContainer *vbox;
Mode mode;
LineEdit *dir;
+ HBoxContainer *drives_container;
+ HBoxContainer *shortcuts_container;
OptionButton *drives;
Tree *tree;
HBoxContainer *file_box;
diff --git a/scene/resources/resource_format_text.cpp b/scene/resources/resource_format_text.cpp
index 97bd12c119..238bdf05ef 100644
--- a/scene/resources/resource_format_text.cpp
+++ b/scene/resources/resource_format_text.cpp
@@ -403,8 +403,6 @@ Ref<PackedScene> ResourceLoaderText::_parse_node_tag(VariantParser::ResourcePars
return Ref<PackedScene>();
}
}
-
- return packed_scene;
}
Error ResourceLoaderText::load() {
@@ -671,10 +669,6 @@ Error ResourceLoaderText::load() {
return error;
}
}
-
- if (progress) {
- *progress = resource_current / float(resources_total);
- }
}
//for scene files
@@ -731,9 +725,15 @@ void ResourceLoaderText::set_translation_remapped(bool p_remapped) {
}
ResourceLoaderText::ResourceLoaderText() {
+ resources_total = 0;
+ resource_current = 0;
+ use_sub_threads = false;
+
progress = nullptr;
+ lines = false;
translation_remapped = false;
use_sub_threads = false;
+ error = OK;
}
ResourceLoaderText::~ResourceLoaderText() {
diff --git a/servers/arvr/arvr_positional_tracker.cpp b/servers/arvr/arvr_positional_tracker.cpp
index 9acc36a139..a64e36b6a2 100644
--- a/servers/arvr/arvr_positional_tracker.cpp
+++ b/servers/arvr/arvr_positional_tracker.cpp
@@ -38,6 +38,7 @@ void ARVRPositionalTracker::_bind_methods() {
// this class is read only from GDScript, so we only have access to getters..
ClassDB::bind_method(D_METHOD("get_type"), &ARVRPositionalTracker::get_type);
+ ClassDB::bind_method(D_METHOD("get_tracker_id"), &ARVRPositionalTracker::get_tracker_id);
ClassDB::bind_method(D_METHOD("get_name"), &ARVRPositionalTracker::get_name);
ClassDB::bind_method(D_METHOD("get_joy_id"), &ARVRPositionalTracker::get_joy_id);
ClassDB::bind_method(D_METHOD("get_tracks_orientation"), &ARVRPositionalTracker::get_tracks_orientation);
diff --git a/servers/physics_2d/physics_2d_server_wrap_mt.cpp b/servers/physics_2d/physics_2d_server_wrap_mt.cpp
index 7070902f77..76036930c6 100644
--- a/servers/physics_2d/physics_2d_server_wrap_mt.cpp
+++ b/servers/physics_2d/physics_2d_server_wrap_mt.cpp
@@ -40,7 +40,7 @@ void Physics2DServerWrapMT::thread_exit() {
void Physics2DServerWrapMT::thread_step(real_t p_delta) {
physics_2d_server->step(p_delta);
- step_sem->post();
+ step_sem.post();
}
void Physics2DServerWrapMT::_thread_callback(void *_instance) {
@@ -84,11 +84,11 @@ void Physics2DServerWrapMT::step(real_t p_step) {
void Physics2DServerWrapMT::sync() {
- if (step_sem) {
+ if (thread) {
if (first_frame)
first_frame = false;
else
- step_sem->wait(); //must not wait if a step was not issued
+ step_sem.wait(); //must not wait if a step was not issued
}
physics_2d_server->sync();
}
@@ -107,11 +107,8 @@ void Physics2DServerWrapMT::init() {
if (create_thread) {
- step_sem = SemaphoreOld::create();
//OS::get_singleton()->release_rendering_thread();
- if (create_thread) {
- thread = Thread::create(_thread_callback, this);
- }
+ thread = Thread::create(_thread_callback, this);
while (!step_thread_up) {
OS::get_singleton()->delay_usec(1000);
}
@@ -146,9 +143,6 @@ void Physics2DServerWrapMT::finish() {
space_free_cached_ids();
area_free_cached_ids();
body_free_cached_ids();
-
- if (step_sem)
- memdelete(step_sem);
}
Physics2DServerWrapMT::Physics2DServerWrapMT(Physics2DServer *p_contained, bool p_create_thread) :
@@ -157,7 +151,6 @@ Physics2DServerWrapMT::Physics2DServerWrapMT(Physics2DServer *p_contained, bool
physics_2d_server = p_contained;
create_thread = p_create_thread;
thread = NULL;
- step_sem = NULL;
step_pending = 0;
step_thread_up = false;
diff --git a/servers/physics_2d/physics_2d_server_wrap_mt.h b/servers/physics_2d/physics_2d_server_wrap_mt.h
index 0648b8651f..4d5e317c8c 100644
--- a/servers/physics_2d/physics_2d_server_wrap_mt.h
+++ b/servers/physics_2d/physics_2d_server_wrap_mt.h
@@ -58,7 +58,7 @@ class Physics2DServerWrapMT : public Physics2DServer {
volatile bool step_thread_up;
bool create_thread;
- SemaphoreOld *step_sem;
+ Semaphore step_sem;
int step_pending;
void thread_step(real_t p_delta);
void thread_flush();
diff --git a/servers/visual/rasterizer.h b/servers/visual/rasterizer.h
index 3a219ec7e3..0a55c78133 100644
--- a/servers/visual/rasterizer.h
+++ b/servers/visual/rasterizer.h
@@ -1171,6 +1171,7 @@ public:
Command *n = c->next;
if (c == commands) {
memdelete(commands);
+ commands = NULL;
} else {
c->~Command();
}
diff --git a/servers/visual/rasterizer_rd/rasterizer_canvas_rd.cpp b/servers/visual/rasterizer_rd/rasterizer_canvas_rd.cpp
index 54f330ecdd..78fff0c381 100644
--- a/servers/visual/rasterizer_rd/rasterizer_canvas_rd.cpp
+++ b/servers/visual/rasterizer_rd/rasterizer_canvas_rd.cpp
@@ -2480,7 +2480,7 @@ RasterizerCanvasRD::RasterizerCanvasRD(RasterizerStorageRD *p_storage) {
state.time = 0;
- ERR_FAIL_COND(sizeof(PushConstant) != 128);
+ static_assert(sizeof(PushConstant) == 128);
}
bool RasterizerCanvasRD::free(RID p_rid) {
diff --git a/servers/visual/rasterizer_rd/rasterizer_effects_rd.cpp b/servers/visual/rasterizer_rd/rasterizer_effects_rd.cpp
index f4d59e9e54..6b6c750fd3 100644
--- a/servers/visual/rasterizer_rd/rasterizer_effects_rd.cpp
+++ b/servers/visual/rasterizer_rd/rasterizer_effects_rd.cpp
@@ -248,26 +248,30 @@ void RasterizerEffectsRD::gaussian_glow(RID p_source_rd_texture, RID p_framebuff
RD::get_singleton()->draw_list_end();
}
-void RasterizerEffectsRD::cubemap_roughness(RID p_source_rd_texture, bool p_source_is_panorama, RID p_dest_framebuffer, uint32_t p_face_id, uint32_t p_sample_count, float p_roughness) {
+void RasterizerEffectsRD::cubemap_roughness(RID p_source_rd_texture, bool p_source_is_panorama, RID p_dest_framebuffer, uint32_t p_face_id, uint32_t p_sample_count, float p_roughness, float p_size) {
zeromem(&roughness.push_constant, sizeof(CubemapRoughnessPushConstant));
- roughness.push_constant.face_id = p_face_id;
+ roughness.push_constant.face_id = p_face_id > 9 ? 0 : p_face_id;
roughness.push_constant.roughness = p_roughness;
roughness.push_constant.sample_count = p_sample_count;
roughness.push_constant.use_direct_write = p_roughness == 0.0;
+ roughness.push_constant.face_size = p_size;
- //RUN
- RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(p_dest_framebuffer, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_DISCARD);
- RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, roughness.pipelines[p_source_is_panorama ? CUBEMAP_ROUGHNESS_SOURCE_PANORAMA : CUBEMAP_ROUGHNESS_SOURCE_CUBEMAP].get_render_pipeline(RD::INVALID_ID, RD::get_singleton()->framebuffer_get_format(p_dest_framebuffer)));
+ RD::ComputeListID compute_list = RD::get_singleton()->compute_list_begin();
+ RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, roughness.pipelines[p_source_is_panorama ? CUBEMAP_ROUGHNESS_SOURCE_PANORAMA : CUBEMAP_ROUGHNESS_SOURCE_CUBEMAP]);
- RD::get_singleton()->draw_list_bind_uniform_set(draw_list, _get_uniform_set_from_texture(p_source_rd_texture), 0);
- RD::get_singleton()->draw_list_bind_index_array(draw_list, index_array);
+ RD::get_singleton()->compute_list_bind_uniform_set(compute_list, _get_compute_uniform_set_from_texture(p_source_rd_texture), 0);
+ RD::get_singleton()->compute_list_bind_uniform_set(compute_list, _get_uniform_set_from_image(p_dest_framebuffer), 1);
- RD::get_singleton()->draw_list_set_push_constant(draw_list, &roughness.push_constant, sizeof(CubemapRoughnessPushConstant));
+ RD::get_singleton()->compute_list_set_push_constant(compute_list, &roughness.push_constant, sizeof(CubemapRoughnessPushConstant));
- RD::get_singleton()->draw_list_draw(draw_list, true);
- RD::get_singleton()->draw_list_end();
+ int x_groups = (p_size - 1) / 8 + 1;
+ int y_groups = (p_size - 1) / 8 + 1;
+
+ RD::get_singleton()->compute_list_dispatch(compute_list, x_groups, y_groups, p_face_id > 9 ? 6 : 1);
+
+ RD::get_singleton()->compute_list_end();
}
void RasterizerEffectsRD::render_panorama(RD::DrawListID p_list, RenderingDevice::FramebufferFormatID p_fb_format, RID p_panorama, const CameraMatrix &p_camera, const Basis &p_orientation, float p_alpha, float p_multipler) {
@@ -841,7 +845,7 @@ RasterizerEffectsRD::RasterizerEffectsRD() {
roughness.shader_version = roughness.shader.version_create();
for (int i = 0; i < CUBEMAP_ROUGHNESS_SOURCE_MAX; i++) {
- roughness.pipelines[i].setup(roughness.shader.version_get_shader(roughness.shader_version, i), RD::RENDER_PRIMITIVE_TRIANGLES, RD::PipelineRasterizationState(), RD::PipelineMultisampleState(), RD::PipelineDepthStencilState(), RD::PipelineColorBlendState::create_disabled(), 0);
+ roughness.pipelines[i] = RD::get_singleton()->compute_pipeline_create(roughness.shader.version_get_shader(roughness.shader_version, i));
}
}
@@ -1077,7 +1081,7 @@ RasterizerEffectsRD::~RasterizerEffectsRD() {
RD::get_singleton()->free(filter.image_uniform_set);
}
- if (RD::get_singleton()->uniform_set_is_valid(filter.image_uniform_set)) {
+ if (RD::get_singleton()->uniform_set_is_valid(filter.uniform_set)) {
RD::get_singleton()->free(filter.uniform_set);
}
diff --git a/servers/visual/rasterizer_rd/rasterizer_effects_rd.h b/servers/visual/rasterizer_rd/rasterizer_effects_rd.h
index 8ec202c399..fbf6b39ecb 100644
--- a/servers/visual/rasterizer_rd/rasterizer_effects_rd.h
+++ b/servers/visual/rasterizer_rd/rasterizer_effects_rd.h
@@ -127,6 +127,8 @@ class RasterizerEffectsRD {
uint32_t sample_count;
float roughness;
uint32_t use_direct_write;
+ float face_size;
+ float pad[3];
};
struct CubemapRoughness {
@@ -134,7 +136,7 @@ class RasterizerEffectsRD {
CubemapRoughnessPushConstant push_constant;
CubemapRoughnessShaderRD shader;
RID shader_version;
- RenderPipelineVertexFormatCacheRD pipelines[CUBEMAP_ROUGHNESS_SOURCE_MAX];
+ RID pipelines[CUBEMAP_ROUGHNESS_SOURCE_MAX];
} roughness;
struct SkyPushConstant {
@@ -419,7 +421,7 @@ public:
void gaussian_blur(RID p_source_rd_texture, RID p_framebuffer_half, RID p_rd_texture_half, RID p_dest_framebuffer, const Vector2 &p_pixel_size, const Rect2 &p_region);
void gaussian_glow(RID p_source_rd_texture, RID p_framebuffer_half, RID p_rd_texture_half, RID p_dest_framebuffer, const Vector2 &p_pixel_size, float p_strength = 1.0, bool p_first_pass = false, float p_luminance_cap = 16.0, float p_exposure = 1.0, float p_bloom = 0.0, float p_hdr_bleed_treshold = 1.0, float p_hdr_bleed_scale = 1.0, RID p_auto_exposure = RID(), float p_auto_exposure_grey = 1.0);
- void cubemap_roughness(RID p_source_rd_texture, bool p_source_is_panorama, RID p_dest_framebuffer, uint32_t p_face_id, uint32_t p_sample_count, float p_roughness);
+ void cubemap_roughness(RID p_source_rd_texture, bool p_source_is_panorama, RID p_dest_framebuffer, uint32_t p_face_id, uint32_t p_sample_count, float p_roughness, float p_size);
void render_panorama(RD::DrawListID p_list, RenderingDevice::FramebufferFormatID p_fb_format, RID p_panorama, const CameraMatrix &p_camera, const Basis &p_orientation, float p_alpha, float p_multipler);
void make_mipmap(RID p_source_rd_texture, RID p_framebuffer_half, const Vector2 &p_pixel_size);
void copy_cubemap_to_dp(RID p_source_rd_texture, RID p_dest_framebuffer, const Rect2 &p_rect, float p_z_near, float p_z_far, float p_bias, bool p_dp_flip);
diff --git a/servers/visual/rasterizer_rd/rasterizer_scene_rd.cpp b/servers/visual/rasterizer_rd/rasterizer_scene_rd.cpp
index 4a78fa89ba..457f6970c8 100644
--- a/servers/visual/rasterizer_rd/rasterizer_scene_rd.cpp
+++ b/servers/visual/rasterizer_rd/rasterizer_scene_rd.cpp
@@ -146,9 +146,7 @@ void RasterizerSceneRD::_create_reflection_from_panorama(ReflectionData &rd, RID
if (p_quality) {
//render directly to the layers
for (int i = 0; i < rd.layers.size(); i++) {
- for (int j = 0; j < 6; j++) {
- storage->get_effects()->cubemap_roughness(p_panorama, true, rd.layers[i].mipmaps[0].framebuffers[j], j, sky_ggx_samples_quality, float(i) / (rd.layers.size() - 1.0));
- }
+ storage->get_effects()->cubemap_roughness(p_panorama, true, rd.layers[i].views[0], 10, sky_ggx_samples_quality, float(i) / (rd.layers.size() - 1.0), rd.layers[i].mipmaps[0].size.x);
}
} else {
// Use fast filtering. Render directly to base mip levels
@@ -169,9 +167,7 @@ void RasterizerSceneRD::_create_reflection_from_panorama(ReflectionData &rd, RID
if (p_quality) {
//render directly to the layers
for (int i = 0; i < rd.layers[0].mipmaps.size(); i++) {
- for (int j = 0; j < 6; j++) {
- storage->get_effects()->cubemap_roughness(p_panorama, true, rd.layers[0].mipmaps[i].framebuffers[j], j, sky_ggx_samples_quality, float(i) / (rd.layers[0].mipmaps.size() - 1.0));
- }
+ storage->get_effects()->cubemap_roughness(p_panorama, true, rd.layers[0].views[i], 10, sky_ggx_samples_quality, float(i) / (rd.layers[0].mipmaps.size() - 1.0), rd.layers[0].mipmaps[i].size.x);
}
} else {
// Use fast filtering. Render directly to each mip level
@@ -185,15 +181,13 @@ void RasterizerSceneRD::_create_reflection_from_panorama(ReflectionData &rd, RID
}
}
-void RasterizerSceneRD::_create_reflection_from_base_mipmap(ReflectionData &rd, bool p_use_arrays, bool p_quality, int p_cube_side) {
+void RasterizerSceneRD::_create_reflection_from_base_mipmap(ReflectionData &rd, bool p_use_arrays, bool p_quality, int p_cube_side, int p_base_layer) {
if (p_use_arrays) {
if (p_quality) {
//render directly to the layers
- for (int i = 1; i < rd.layers.size(); i++) {
- storage->get_effects()->cubemap_roughness(rd.radiance_base_cubemap, false, rd.layers[i].mipmaps[0].framebuffers[p_cube_side], p_cube_side, sky_ggx_samples_quality, float(i) / (rd.layers.size() - 1.0));
- }
+ storage->get_effects()->cubemap_roughness(rd.radiance_base_cubemap, false, rd.layers[p_base_layer].views[0], p_cube_side, sky_ggx_samples_quality, float(p_base_layer) / (rd.layers.size() - 1.0), rd.layers[p_base_layer].mipmaps[0].size.x);
} else {
storage->get_effects()->cubemap_downsample(rd.radiance_base_cubemap, false, rd.downsampled_layer.mipmaps[0].view, rd.downsampled_layer.mipmaps[0].size);
@@ -211,10 +205,8 @@ void RasterizerSceneRD::_create_reflection_from_base_mipmap(ReflectionData &rd,
} else {
if (p_quality) {
- //render directly to the layers
- for (int i = 1; i < rd.layers[0].mipmaps.size(); i++) {
- storage->get_effects()->cubemap_roughness(rd.radiance_base_cubemap, false, rd.layers[0].mipmaps[i].framebuffers[p_cube_side], p_cube_side, sky_ggx_samples_quality, float(i) / (rd.layers[0].mipmaps.size() - 1.0));
- }
+
+ storage->get_effects()->cubemap_roughness(rd.layers[0].views[p_base_layer - 1], false, rd.layers[0].views[p_base_layer], p_cube_side, sky_ggx_samples_quality, float(p_base_layer) / (rd.layers[0].mipmaps.size() - 1.0), rd.layers[0].mipmaps[p_base_layer].size.x);
} else {
storage->get_effects()->cubemap_downsample(rd.radiance_base_cubemap, false, rd.downsampled_layer.mipmaps[0].view, rd.downsampled_layer.mipmaps[0].size);
@@ -742,6 +734,21 @@ bool RasterizerSceneRD::reflection_probe_instance_begin_render(RID p_instance, R
reflection_atlas_set_size(p_reflection_atlas, 128, atlas->count);
}
+ if (storage->reflection_probe_get_update_mode(rpi->probe) == VS::REFLECTION_PROBE_UPDATE_ALWAYS && atlas->reflection.is_valid() && atlas->reflections[0].data.layers[0].mipmaps.size() != 7) {
+ // Invalidate reflection atlas, need to regenerate
+ RD::get_singleton()->free(atlas->reflection);
+ atlas->reflection = RID();
+
+ for (int i = 0; i < atlas->reflections.size(); i++) {
+ if (atlas->reflections[i].owner.is_null()) {
+ continue;
+ }
+ reflection_probe_release_atlas_index(atlas->reflections[i].owner);
+ }
+
+ atlas->reflections.clear();
+ }
+
if (atlas->reflection.is_null()) {
int mipmaps = MIN(roughness_layers, Image::get_image_required_mipmaps(atlas->size, atlas->size, Image::FORMAT_RGBAH) + 1);
mipmaps = storage->reflection_probe_get_update_mode(rpi->probe) == VS::REFLECTION_PROBE_UPDATE_ALWAYS ? 7 : mipmaps; // always use 7 mipmaps with real time filtering
@@ -808,6 +815,7 @@ bool RasterizerSceneRD::reflection_probe_instance_begin_render(RID p_instance, R
rpi->atlas = p_reflection_atlas;
rpi->rendering = true;
rpi->dirty = false;
+ rpi->processing_layer = 1;
rpi->processing_side = 0;
return true;
@@ -827,24 +835,36 @@ bool RasterizerSceneRD::reflection_probe_instance_postprocess_step(RID p_instanc
return false;
}
- _create_reflection_from_base_mipmap(atlas->reflections.write[rpi->atlas_index].data, false, storage->reflection_probe_get_update_mode(rpi->probe) == VS::REFLECTION_PROBE_UPDATE_ONCE, rpi->processing_side);
+ if (rpi->processing_layer > 1) {
+ _create_reflection_from_base_mipmap(atlas->reflections.write[rpi->atlas_index].data, false, storage->reflection_probe_get_update_mode(rpi->probe) == VS::REFLECTION_PROBE_UPDATE_ONCE, 10, rpi->processing_layer);
+ rpi->processing_layer++;
+ if (rpi->processing_layer == atlas->reflections[rpi->atlas_index].data.layers[0].mipmaps.size()) {
+ rpi->rendering = false;
+ rpi->processing_side = 0;
+ rpi->processing_layer = 1;
+ return true;
+ }
+ return false;
+
+ } else {
+ _create_reflection_from_base_mipmap(atlas->reflections.write[rpi->atlas_index].data, false, storage->reflection_probe_get_update_mode(rpi->probe) == VS::REFLECTION_PROBE_UPDATE_ONCE, rpi->processing_side, rpi->processing_layer);
+ }
if (storage->reflection_probe_get_update_mode(rpi->probe) == VS::REFLECTION_PROBE_UPDATE_ALWAYS) {
// Using real time reflections, all roughness is done in one step
rpi->rendering = false;
rpi->processing_side = 0;
+ rpi->processing_layer = 1;
return true;
}
rpi->processing_side++;
-
if (rpi->processing_side == 6) {
- rpi->rendering = false;
rpi->processing_side = 0;
- return true;
- } else {
- return false;
+ rpi->processing_layer++;
}
+
+ return false;
}
uint32_t RasterizerSceneRD::reflection_probe_instance_get_resolution(RID p_instance) {
diff --git a/servers/visual/rasterizer_rd/rasterizer_scene_rd.h b/servers/visual/rasterizer_rd/rasterizer_scene_rd.h
index 3e3ad5e4f5..0fa853f2df 100644
--- a/servers/visual/rasterizer_rd/rasterizer_scene_rd.h
+++ b/servers/visual/rasterizer_rd/rasterizer_scene_rd.h
@@ -108,7 +108,7 @@ private:
void _clear_reflection_data(ReflectionData &rd);
void _update_reflection_data(ReflectionData &rd, int p_size, int p_mipmaps, bool p_use_array, RID p_base_cube, int p_base_layer, bool p_low_quality);
void _create_reflection_from_panorama(ReflectionData &rd, RID p_panorama, bool p_quality);
- void _create_reflection_from_base_mipmap(ReflectionData &rd, bool p_use_arrays, bool p_quality, int p_cube_side);
+ void _create_reflection_from_base_mipmap(ReflectionData &rd, bool p_use_arrays, bool p_quality, int p_cube_side, int p_base_layer);
void _update_reflection_mipmaps(ReflectionData &rd, bool p_quality);
/* SKY */
@@ -165,6 +165,7 @@ private:
bool dirty = true;
bool rendering = false;
+ int processing_layer = 1;
int processing_side = 0;
uint32_t render_step = 0;
diff --git a/servers/visual/rasterizer_rd/rasterizer_storage_rd.cpp b/servers/visual/rasterizer_rd/rasterizer_storage_rd.cpp
index 15cbc61b76..52cefa7511 100644
--- a/servers/visual/rasterizer_rd/rasterizer_storage_rd.cpp
+++ b/servers/visual/rasterizer_rd/rasterizer_storage_rd.cpp
@@ -1906,8 +1906,10 @@ void RasterizerStorageRD::mesh_add_surface(RID p_mesh, const VS::SurfaceData &p_
for (int i = 0; i < p_surface.blend_shapes.size(); i++) {
- ERR_FAIL_COND(p_surface.blend_shapes[i].size() != p_surface.vertex_data.size());
-
+ if (p_surface.blend_shapes[i].size() != p_surface.vertex_data.size()) {
+ memdelete(s);
+ ERR_FAIL_COND(p_surface.blend_shapes[i].size() != p_surface.vertex_data.size());
+ }
RID vertex_buffer = RD::get_singleton()->vertex_buffer_create(p_surface.blend_shapes[i].size(), p_surface.blend_shapes[i]);
s->blend_shapes.push_back(vertex_buffer);
}
diff --git a/servers/visual/rasterizer_rd/shaders/cubemap_roughness.glsl b/servers/visual/rasterizer_rd/shaders/cubemap_roughness.glsl
index 011dfce985..3dba143e56 100644
--- a/servers/visual/rasterizer_rd/shaders/cubemap_roughness.glsl
+++ b/servers/visual/rasterizer_rd/shaders/cubemap_roughness.glsl
@@ -1,83 +1,38 @@
/* clang-format off */
-[vertex]
+[compute]
#version 450
VERSION_DEFINES
-layout(location = 0) out highp vec2 uv_interp;
-/* clang-format on */
-
-void main() {
-
- vec2 base_arr[4] = vec2[](vec2(0.0, 0.0), vec2(0.0, 1.0), vec2(1.0, 1.0), vec2(1.0, 0.0));
- uv_interp = base_arr[gl_VertexIndex];
- gl_Position = vec4(uv_interp * 2.0 - 1.0, 0.0, 1.0);
-}
-
-/* clang-format off */
-[fragment]
-
-#version 450
+#define GROUP_SIZE 8
-VERSION_DEFINES
+layout(local_size_x = GROUP_SIZE, local_size_y = GROUP_SIZE, local_size_z = 1) in;
+/* clang-format on */
#ifdef MODE_SOURCE_PANORAMA
layout(set = 0, binding = 0) uniform sampler2D source_panorama;
-/* clang-format on */
#endif
#ifdef MODE_SOURCE_CUBEMAP
layout(set = 0, binding = 0) uniform samplerCube source_cube;
#endif
+layout(rgba16f, set = 1, binding = 0) uniform restrict writeonly imageCube dest_cubemap;
+
layout(push_constant, binding = 1, std430) uniform Params {
uint face_id;
uint sample_count;
float roughness;
bool use_direct_write;
+ float face_size;
}
params;
-layout(location = 0) in vec2 uv_interp;
-
-layout(location = 0) out vec4 frag_color;
-
#define M_PI 3.14159265359
vec3 texelCoordToVec(vec2 uv, uint faceID) {
mat3 faceUvVectors[6];
- /*
- // -x
- faceUvVectors[0][0] = vec3(0.0, 0.0, 1.0); // u -> +z
- faceUvVectors[0][1] = vec3(0.0, -1.0, 0.0); // v -> -y
- faceUvVectors[0][2] = vec3(-1.0, 0.0, 0.0); // -x face
-
- // +x
- faceUvVectors[1][0] = vec3(0.0, 0.0, -1.0); // u -> -z
- faceUvVectors[1][1] = vec3(0.0, -1.0, 0.0); // v -> -y
- faceUvVectors[1][2] = vec3(1.0, 0.0, 0.0); // +x face
-
- // -y
- faceUvVectors[2][0] = vec3(1.0, 0.0, 0.0); // u -> +x
- faceUvVectors[2][1] = vec3(0.0, 0.0, -1.0); // v -> -z
- faceUvVectors[2][2] = vec3(0.0, -1.0, 0.0); // -y face
-
- // +y
- faceUvVectors[3][0] = vec3(1.0, 0.0, 0.0); // u -> +x
- faceUvVectors[3][1] = vec3(0.0, 0.0, 1.0); // v -> +z
- faceUvVectors[3][2] = vec3(0.0, 1.0, 0.0); // +y face
-
- // -z
- faceUvVectors[4][0] = vec3(-1.0, 0.0, 0.0); // u -> -x
- faceUvVectors[4][1] = vec3(0.0, -1.0, 0.0); // v -> -y
- faceUvVectors[4][2] = vec3(0.0, 0.0, -1.0); // -z face
-
- // +z
- faceUvVectors[5][0] = vec3(1.0, 0.0, 0.0); // u -> +x
- faceUvVectors[5][1] = vec3(0.0, -1.0, 0.0); // v -> -y
- faceUvVectors[5][2] = vec3(0.0, 0.0, 1.0); // +z face
- */
// -x
faceUvVectors[1][0] = vec3(0.0, 0.0, 1.0); // u -> +z
@@ -179,21 +134,23 @@ vec4 texturePanorama(vec3 normal, sampler2D pano) {
#endif
void main() {
+ uvec3 id = gl_GlobalInvocationID;
+ id.z += params.face_id;
- vec2 uv = (uv_interp * 2.0) - 1.0;
- vec3 N = texelCoordToVec(uv, params.face_id);
+ vec2 uv = ((vec2(id.xy) * 2.0 + 1.0) / (params.face_size) - 1.0);
+ vec3 N = texelCoordToVec(uv, id.z);
//vec4 color = color_interp;
if (params.use_direct_write) {
#ifdef MODE_SOURCE_PANORAMA
-
- frag_color = vec4(texturePanorama(N, source_panorama).rgb, 1.0);
+ imageStore(dest_cubemap, ivec3(id), vec4(texturePanorama(N, source_panorama).rgb, 1.0));
#endif
#ifdef MODE_SOURCE_CUBEMAP
- frag_color = vec4(texture(source_cube, N).rgb, 1.0);
+ imageStore(dest_cubemap, ivec3(id), vec4(texture(source_cube, N).rgb, 1.0));
+
#endif
} else {
@@ -222,6 +179,6 @@ void main() {
}
sum /= sum.a;
- frag_color = vec4(sum.rgb, 1.0);
+ imageStore(dest_cubemap, ivec3(id), vec4(sum.rgb, 1.0));
}
}
diff --git a/servers/visual/visual_server_canvas.cpp b/servers/visual/visual_server_canvas.cpp
index c1833fea44..c192b77988 100644
--- a/servers/visual/visual_server_canvas.cpp
+++ b/servers/visual/visual_server_canvas.cpp
@@ -183,7 +183,7 @@ void VisualServerCanvas::_cull_canvas_item(Item *p_canvas_item, const Transform2
VisualServerRaster::redraw_request();
}
- if ((ci->commands != NULL && p_clip_rect.intersects_touch(global_rect)) || ci->vp_render || ci->copy_back_buffer) {
+ if ((ci->commands != NULL && p_clip_rect.intersects(global_rect, true)) || ci->vp_render || ci->copy_back_buffer) {
//something to draw?
ci->final_transform = xform;
ci->final_modulate = Color(modulate.r * ci->self_modulate.r, modulate.g * ci->self_modulate.g, modulate.b * ci->self_modulate.b, modulate.a * ci->self_modulate.a);
diff --git a/thirdparty/README.md b/thirdparty/README.md
index 934d719ca6..2099bd773f 100644
--- a/thirdparty/README.md
+++ b/thirdparty/README.md
@@ -281,7 +281,7 @@ changes are marked with `// -- GODOT --` comments.
## mbedtls
- Upstream: https://tls.mbed.org/
-- Version: 2.16.4
+- Version: 2.16.5
- License: Apache 2.0
File extracted from upstream release tarball (`-apache.tgz` variant):
@@ -291,9 +291,6 @@ File extracted from upstream release tarball (`-apache.tgz` variant):
- LICENSE and apache-2.0.txt files
- Applied the patch in `thirdparty/mbedtls/patches/1453.diff` (PR 1453).
Soon to be merged upstream. Check it out at next update.
-- Applied the patch in `thirdparty/mbedtls/patches/padlock.diff`. This disables VIA
- padlock support which defines a symbol `unsupported` which clashes with
- a symbol in libwebsockets.
- Added 2 files `godot_core_mbedtls_platform.{c,h}` providing configuration
for light bundling with core.
diff --git a/thirdparty/mbedtls/include/mbedtls/config.h b/thirdparty/mbedtls/include/mbedtls/config.h
index 8d9c31a504..834cced87f 100644
--- a/thirdparty/mbedtls/include/mbedtls/config.h
+++ b/thirdparty/mbedtls/include/mbedtls/config.h
@@ -2542,9 +2542,7 @@
*
* This modules adds support for the VIA PadLock on x86.
*/
-// -- GODOT start --
-// #define MBEDTLS_PADLOCK_C
-// -- GODOT end --
+#define MBEDTLS_PADLOCK_C
/**
* \def MBEDTLS_PEM_PARSE_C
diff --git a/thirdparty/mbedtls/include/mbedtls/version.h b/thirdparty/mbedtls/include/mbedtls/version.h
index aeffb16699..8e2ce03c32 100644
--- a/thirdparty/mbedtls/include/mbedtls/version.h
+++ b/thirdparty/mbedtls/include/mbedtls/version.h
@@ -40,16 +40,16 @@
*/
#define MBEDTLS_VERSION_MAJOR 2
#define MBEDTLS_VERSION_MINOR 16
-#define MBEDTLS_VERSION_PATCH 4
+#define MBEDTLS_VERSION_PATCH 5
/**
* The single version number has the following structure:
* MMNNPP00
* Major version | Minor version | Patch version
*/
-#define MBEDTLS_VERSION_NUMBER 0x02100400
-#define MBEDTLS_VERSION_STRING "2.16.4"
-#define MBEDTLS_VERSION_STRING_FULL "mbed TLS 2.16.4"
+#define MBEDTLS_VERSION_NUMBER 0x02100500
+#define MBEDTLS_VERSION_STRING "2.16.5"
+#define MBEDTLS_VERSION_STRING_FULL "mbed TLS 2.16.5"
#if defined(MBEDTLS_VERSION_C)
diff --git a/thirdparty/mbedtls/library/bignum.c b/thirdparty/mbedtls/library/bignum.c
index 6713bcbf6f..87ccf42fad 100644
--- a/thirdparty/mbedtls/library/bignum.c
+++ b/thirdparty/mbedtls/library/bignum.c
@@ -157,9 +157,10 @@ int mbedtls_mpi_shrink( mbedtls_mpi *X, size_t nblimbs )
if( nblimbs > MBEDTLS_MPI_MAX_LIMBS )
return( MBEDTLS_ERR_MPI_ALLOC_FAILED );
- /* Actually resize up in this case */
+ /* Actually resize up if there are currently fewer than nblimbs limbs. */
if( X->n <= nblimbs )
return( mbedtls_mpi_grow( X, nblimbs ) );
+ /* After this point, then X->n > nblimbs and in particular X->n > 0. */
for( i = X->n - 1; i > 0; i-- )
if( X->p[i] != 0 )
@@ -198,7 +199,7 @@ int mbedtls_mpi_copy( mbedtls_mpi *X, const mbedtls_mpi *Y )
if( X == Y )
return( 0 );
- if( Y->p == NULL )
+ if( Y->n == 0 )
{
mbedtls_mpi_free( X );
return( 0 );
diff --git a/thirdparty/mbedtls/library/cipher.c b/thirdparty/mbedtls/library/cipher.c
index 273997577b..8d010b59ac 100644
--- a/thirdparty/mbedtls/library/cipher.c
+++ b/thirdparty/mbedtls/library/cipher.c
@@ -361,6 +361,10 @@ int mbedtls_cipher_update( mbedtls_cipher_context_t *ctx, const unsigned char *i
*olen = 0;
block_size = mbedtls_cipher_get_block_size( ctx );
+ if ( 0 == block_size )
+ {
+ return( MBEDTLS_ERR_CIPHER_INVALID_CONTEXT );
+ }
if( ctx->cipher_info->mode == MBEDTLS_MODE_ECB )
{
@@ -396,11 +400,6 @@ int mbedtls_cipher_update( mbedtls_cipher_context_t *ctx, const unsigned char *i
}
#endif
- if ( 0 == block_size )
- {
- return( MBEDTLS_ERR_CIPHER_INVALID_CONTEXT );
- }
-
if( input == output &&
( ctx->unprocessed_len != 0 || ilen % block_size ) )
{
@@ -459,11 +458,6 @@ int mbedtls_cipher_update( mbedtls_cipher_context_t *ctx, const unsigned char *i
*/
if( 0 != ilen )
{
- if( 0 == block_size )
- {
- return( MBEDTLS_ERR_CIPHER_INVALID_CONTEXT );
- }
-
/* Encryption: only cache partial blocks
* Decryption w/ padding: always keep at least one whole block
* Decryption w/o padding: only cache partial blocks
diff --git a/thirdparty/mbedtls/library/ecdsa.c b/thirdparty/mbedtls/library/ecdsa.c
index 3cf3d7cc4f..6b72e0d927 100644
--- a/thirdparty/mbedtls/library/ecdsa.c
+++ b/thirdparty/mbedtls/library/ecdsa.c
@@ -297,7 +297,7 @@ static int ecdsa_sign_restartable( mbedtls_ecp_group *grp,
*p_sign_tries = 0;
do
{
- if( *p_sign_tries++ > 10 )
+ if( (*p_sign_tries)++ > 10 )
{
ret = MBEDTLS_ERR_ECP_RANDOM_FAILED;
goto cleanup;
@@ -310,7 +310,7 @@ static int ecdsa_sign_restartable( mbedtls_ecp_group *grp,
*p_key_tries = 0;
do
{
- if( *p_key_tries++ > 10 )
+ if( (*p_key_tries)++ > 10 )
{
ret = MBEDTLS_ERR_ECP_RANDOM_FAILED;
goto cleanup;
diff --git a/thirdparty/mbedtls/library/entropy_poll.c b/thirdparty/mbedtls/library/entropy_poll.c
index ba56b70f77..4556f88a55 100644
--- a/thirdparty/mbedtls/library/entropy_poll.c
+++ b/thirdparty/mbedtls/library/entropy_poll.c
@@ -61,43 +61,28 @@
#define _WIN32_WINNT 0x0400
#endif
#include <windows.h>
-#include <bcrypt.h>
-#if defined(_MSC_VER) && _MSC_VER <= 1600
-/* Visual Studio 2010 and earlier issue a warning when both <stdint.h> and
- * <intsafe.h> are included, as they redefine a number of <TYPE>_MAX constants.
- * These constants are guaranteed to be the same, though, so we suppress the
- * warning when including intsafe.h.
- */
-#pragma warning( push )
-#pragma warning( disable : 4005 )
-#endif
-#include <intsafe.h>
-#if defined(_MSC_VER) && _MSC_VER <= 1600
-#pragma warning( pop )
-#endif
+#include <wincrypt.h>
int mbedtls_platform_entropy_poll( void *data, unsigned char *output, size_t len,
size_t *olen )
{
- ULONG len_as_ulong = 0;
+ HCRYPTPROV provider;
((void) data);
*olen = 0;
- /*
- * BCryptGenRandom takes ULONG for size, which is smaller than size_t on
- * 64-bit Windows platforms. Ensure len's value can be safely converted into
- * a ULONG.
- */
- if ( FAILED( SizeTToULong( len, &len_as_ulong ) ) )
+ if( CryptAcquireContext( &provider, NULL, NULL,
+ PROV_RSA_FULL, CRYPT_VERIFYCONTEXT ) == FALSE )
{
return( MBEDTLS_ERR_ENTROPY_SOURCE_FAILED );
}
- if ( !BCRYPT_SUCCESS( BCryptGenRandom( NULL, output, len_as_ulong, BCRYPT_USE_SYSTEM_PREFERRED_RNG ) ) )
+ if( CryptGenRandom( provider, (DWORD) len, output ) == FALSE )
{
+ CryptReleaseContext( provider, 0 );
return( MBEDTLS_ERR_ENTROPY_SOURCE_FAILED );
}
+ CryptReleaseContext( provider, 0 );
*olen = len;
return( 0 );
diff --git a/thirdparty/mbedtls/library/pkparse.c b/thirdparty/mbedtls/library/pkparse.c
index ae210bca6a..d5004577a1 100644
--- a/thirdparty/mbedtls/library/pkparse.c
+++ b/thirdparty/mbedtls/library/pkparse.c
@@ -678,6 +678,32 @@ int mbedtls_pk_parse_subpubkey( unsigned char **p, const unsigned char *end,
#if defined(MBEDTLS_RSA_C)
/*
+ * Wrapper around mbedtls_asn1_get_mpi() that rejects zero.
+ *
+ * The value zero is:
+ * - never a valid value for an RSA parameter
+ * - interpreted as "omitted, please reconstruct" by mbedtls_rsa_complete().
+ *
+ * Since values can't be omitted in PKCS#1, passing a zero value to
+ * rsa_complete() would be incorrect, so reject zero values early.
+ */
+static int asn1_get_nonzero_mpi( unsigned char **p,
+ const unsigned char *end,
+ mbedtls_mpi *X )
+{
+ int ret;
+
+ ret = mbedtls_asn1_get_mpi( p, end, X );
+ if( ret != 0 )
+ return( ret );
+
+ if( mbedtls_mpi_cmp_int( X, 0 ) == 0 )
+ return( MBEDTLS_ERR_PK_KEY_INVALID_FORMAT );
+
+ return( 0 );
+}
+
+/*
* Parse a PKCS#1 encoded private RSA key
*/
static int pk_parse_key_pkcs1_der( mbedtls_rsa_context *rsa,
@@ -729,54 +755,84 @@ static int pk_parse_key_pkcs1_der( mbedtls_rsa_context *rsa,
}
/* Import N */
- if( ( ret = mbedtls_asn1_get_tag( &p, end, &len,
- MBEDTLS_ASN1_INTEGER ) ) != 0 ||
- ( ret = mbedtls_rsa_import_raw( rsa, p, len, NULL, 0, NULL, 0,
- NULL, 0, NULL, 0 ) ) != 0 )
+ if( ( ret = asn1_get_nonzero_mpi( &p, end, &T ) ) != 0 ||
+ ( ret = mbedtls_rsa_import( rsa, &T, NULL, NULL,
+ NULL, NULL ) ) != 0 )
goto cleanup;
- p += len;
/* Import E */
- if( ( ret = mbedtls_asn1_get_tag( &p, end, &len,
- MBEDTLS_ASN1_INTEGER ) ) != 0 ||
- ( ret = mbedtls_rsa_import_raw( rsa, NULL, 0, NULL, 0, NULL, 0,
- NULL, 0, p, len ) ) != 0 )
+ if( ( ret = asn1_get_nonzero_mpi( &p, end, &T ) ) != 0 ||
+ ( ret = mbedtls_rsa_import( rsa, NULL, NULL, NULL,
+ NULL, &T ) ) != 0 )
goto cleanup;
- p += len;
/* Import D */
- if( ( ret = mbedtls_asn1_get_tag( &p, end, &len,
- MBEDTLS_ASN1_INTEGER ) ) != 0 ||
- ( ret = mbedtls_rsa_import_raw( rsa, NULL, 0, NULL, 0, NULL, 0,
- p, len, NULL, 0 ) ) != 0 )
+ if( ( ret = asn1_get_nonzero_mpi( &p, end, &T ) ) != 0 ||
+ ( ret = mbedtls_rsa_import( rsa, NULL, NULL, NULL,
+ &T, NULL ) ) != 0 )
goto cleanup;
- p += len;
/* Import P */
- if( ( ret = mbedtls_asn1_get_tag( &p, end, &len,
- MBEDTLS_ASN1_INTEGER ) ) != 0 ||
- ( ret = mbedtls_rsa_import_raw( rsa, NULL, 0, p, len, NULL, 0,
- NULL, 0, NULL, 0 ) ) != 0 )
+ if( ( ret = asn1_get_nonzero_mpi( &p, end, &T ) ) != 0 ||
+ ( ret = mbedtls_rsa_import( rsa, NULL, &T, NULL,
+ NULL, NULL ) ) != 0 )
goto cleanup;
- p += len;
/* Import Q */
- if( ( ret = mbedtls_asn1_get_tag( &p, end, &len,
- MBEDTLS_ASN1_INTEGER ) ) != 0 ||
- ( ret = mbedtls_rsa_import_raw( rsa, NULL, 0, NULL, 0, p, len,
- NULL, 0, NULL, 0 ) ) != 0 )
+ if( ( ret = asn1_get_nonzero_mpi( &p, end, &T ) ) != 0 ||
+ ( ret = mbedtls_rsa_import( rsa, NULL, NULL, &T,
+ NULL, NULL ) ) != 0 )
goto cleanup;
- p += len;
- /* Complete the RSA private key */
- if( ( ret = mbedtls_rsa_complete( rsa ) ) != 0 )
- goto cleanup;
+#if !defined(MBEDTLS_RSA_NO_CRT) && !defined(MBEDTLS_RSA_ALT)
+ /*
+ * The RSA CRT parameters DP, DQ and QP are nominally redundant, in
+ * that they can be easily recomputed from D, P and Q. However by
+ * parsing them from the PKCS1 structure it is possible to avoid
+ * recalculating them which both reduces the overhead of loading
+ * RSA private keys into memory and also avoids side channels which
+ * can arise when computing those values, since all of D, P, and Q
+ * are secret. See https://eprint.iacr.org/2020/055 for a
+ * description of one such attack.
+ */
+
+ /* Import DP */
+ if( ( ret = asn1_get_nonzero_mpi( &p, end, &T ) ) != 0 ||
+ ( ret = mbedtls_mpi_copy( &rsa->DP, &T ) ) != 0 )
+ goto cleanup;
+
+ /* Import DQ */
+ if( ( ret = asn1_get_nonzero_mpi( &p, end, &T ) ) != 0 ||
+ ( ret = mbedtls_mpi_copy( &rsa->DQ, &T ) ) != 0 )
+ goto cleanup;
+
+ /* Import QP */
+ if( ( ret = asn1_get_nonzero_mpi( &p, end, &T ) ) != 0 ||
+ ( ret = mbedtls_mpi_copy( &rsa->QP, &T ) ) != 0 )
+ goto cleanup;
+
+#else
+ /* Verify existance of the CRT params */
+ if( ( ret = asn1_get_nonzero_mpi( &p, end, &T ) ) != 0 ||
+ ( ret = asn1_get_nonzero_mpi( &p, end, &T ) ) != 0 ||
+ ( ret = asn1_get_nonzero_mpi( &p, end, &T ) ) != 0 )
+ goto cleanup;
+#endif
- /* Check optional parameters */
- if( ( ret = mbedtls_asn1_get_mpi( &p, end, &T ) ) != 0 ||
- ( ret = mbedtls_asn1_get_mpi( &p, end, &T ) ) != 0 ||
- ( ret = mbedtls_asn1_get_mpi( &p, end, &T ) ) != 0 )
+ /* rsa_complete() doesn't complete anything with the default
+ * implementation but is still called:
+ * - for the benefit of alternative implementation that may want to
+ * pre-compute stuff beyond what's provided (eg Montgomery factors)
+ * - as is also sanity-checks the key
+ *
+ * Furthermore, we also check the public part for consistency with
+ * mbedtls_pk_parse_pubkey(), as it includes size minima for example.
+ */
+ if( ( ret = mbedtls_rsa_complete( rsa ) ) != 0 ||
+ ( ret = mbedtls_rsa_check_pubkey( rsa ) ) != 0 )
+ {
goto cleanup;
+ }
if( p != end )
{
diff --git a/thirdparty/mbedtls/library/rsa.c b/thirdparty/mbedtls/library/rsa.c
index af1a878599..09fd379fdb 100644
--- a/thirdparty/mbedtls/library/rsa.c
+++ b/thirdparty/mbedtls/library/rsa.c
@@ -249,6 +249,9 @@ int mbedtls_rsa_complete( mbedtls_rsa_context *ctx )
{
int ret = 0;
int have_N, have_P, have_Q, have_D, have_E;
+#if !defined(MBEDTLS_RSA_NO_CRT)
+ int have_DP, have_DQ, have_QP;
+#endif
int n_missing, pq_missing, d_missing, is_pub, is_priv;
RSA_VALIDATE_RET( ctx != NULL );
@@ -259,6 +262,12 @@ int mbedtls_rsa_complete( mbedtls_rsa_context *ctx )
have_D = ( mbedtls_mpi_cmp_int( &ctx->D, 0 ) != 0 );
have_E = ( mbedtls_mpi_cmp_int( &ctx->E, 0 ) != 0 );
+#if !defined(MBEDTLS_RSA_NO_CRT)
+ have_DP = ( mbedtls_mpi_cmp_int( &ctx->DP, 0 ) != 0 );
+ have_DQ = ( mbedtls_mpi_cmp_int( &ctx->DQ, 0 ) != 0 );
+ have_QP = ( mbedtls_mpi_cmp_int( &ctx->QP, 0 ) != 0 );
+#endif
+
/*
* Check whether provided parameters are enough
* to deduce all others. The following incomplete
@@ -324,7 +333,7 @@ int mbedtls_rsa_complete( mbedtls_rsa_context *ctx )
*/
#if !defined(MBEDTLS_RSA_NO_CRT)
- if( is_priv )
+ if( is_priv && ! ( have_DP && have_DQ && have_QP ) )
{
ret = mbedtls_rsa_deduce_crt( &ctx->P, &ctx->Q, &ctx->D,
&ctx->DP, &ctx->DQ, &ctx->QP );
diff --git a/thirdparty/mbedtls/library/x509_crt.c b/thirdparty/mbedtls/library/x509_crt.c
index a3697f13f9..9c2e36547e 100644
--- a/thirdparty/mbedtls/library/x509_crt.c
+++ b/thirdparty/mbedtls/library/x509_crt.c
@@ -65,19 +65,6 @@
#if defined(_WIN32) && !defined(EFIX64) && !defined(EFI32)
#include <windows.h>
-#if defined(_MSC_VER) && _MSC_VER <= 1600
-/* Visual Studio 2010 and earlier issue a warning when both <stdint.h> and
- * <intsafe.h> are included, as they redefine a number of <TYPE>_MAX constants.
- * These constants are guaranteed to be the same, though, so we suppress the
- * warning when including intsafe.h.
- */
-#pragma warning( push )
-#pragma warning( disable : 4005 )
-#endif
-#include <intsafe.h>
-#if defined(_MSC_VER) && _MSC_VER <= 1600
-#pragma warning( pop )
-#endif
#else
#include <time.h>
#endif
@@ -1290,7 +1277,6 @@ int mbedtls_x509_crt_parse_path( mbedtls_x509_crt *chain, const char *path )
char filename[MAX_PATH];
char *p;
size_t len = strlen( path );
- int lengthAsInt = 0;
WIN32_FIND_DATAW file_data;
HANDLE hFind;
@@ -1305,18 +1291,7 @@ int mbedtls_x509_crt_parse_path( mbedtls_x509_crt *chain, const char *path )
p = filename + len;
filename[len++] = '*';
- if ( FAILED ( SizeTToInt( len, &lengthAsInt ) ) )
- return( MBEDTLS_ERR_X509_FILE_IO_ERROR );
-
- /*
- * Note this function uses the code page CP_ACP, and assumes the incoming
- * string is encoded in ANSI, before translating it into Unicode. If the
- * incoming string were changed to be UTF-8, then the length check needs to
- * change to check the number of characters, not the number of bytes, in the
- * incoming string are less than MAX_PATH to avoid a buffer overrun with
- * MultiByteToWideChar().
- */
- w_ret = MultiByteToWideChar( CP_ACP, 0, filename, lengthAsInt, szDir,
+ w_ret = MultiByteToWideChar( CP_ACP, 0, filename, (int)len, szDir,
MAX_PATH - 3 );
if( w_ret == 0 )
return( MBEDTLS_ERR_X509_BAD_INPUT_DATA );
@@ -1333,11 +1308,8 @@ int mbedtls_x509_crt_parse_path( mbedtls_x509_crt *chain, const char *path )
if( file_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY )
continue;
- if ( FAILED( SizeTToInt( wcslen( file_data.cFileName ), &lengthAsInt ) ) )
- return( MBEDTLS_ERR_X509_FILE_IO_ERROR );
-
w_ret = WideCharToMultiByte( CP_ACP, 0, file_data.cFileName,
- lengthAsInt,
+ lstrlenW( file_data.cFileName ),
p, (int) len - 1,
NULL, NULL );
if( w_ret == 0 )
diff --git a/thirdparty/mbedtls/library/x509write_csr.c b/thirdparty/mbedtls/library/x509write_csr.c
index b65a11c6aa..7406a97542 100644
--- a/thirdparty/mbedtls/library/x509write_csr.c
+++ b/thirdparty/mbedtls/library/x509write_csr.c
@@ -226,7 +226,9 @@ int mbedtls_x509write_csr_der( mbedtls_x509write_csr *ctx, unsigned char *buf, s
/*
* Prepare signature
*/
- mbedtls_md( mbedtls_md_info_from_type( ctx->md_alg ), c, len, hash );
+ ret = mbedtls_md( mbedtls_md_info_from_type( ctx->md_alg ), c, len, hash );
+ if( ret != 0 )
+ return( ret );
if( ( ret = mbedtls_pk_sign( ctx->key, ctx->md_alg, hash, 0, sig, &sig_len,
f_rng, p_rng ) ) != 0 )
diff --git a/thirdparty/mbedtls/patches/padlock.diff b/thirdparty/mbedtls/patches/padlock.diff
deleted file mode 100644
index 6ace48891c..0000000000
--- a/thirdparty/mbedtls/patches/padlock.diff
+++ /dev/null
@@ -1,13 +0,0 @@
---- a/thirdparty/mbedtls/include/mbedtls/config.h
-+++ b/thirdparty/mbedtls/include/mbedtls/config.h
-@@ -2477,7 +2477,9 @@
- *
- * This modules adds support for the VIA PadLock on x86.
- */
--#define MBEDTLS_PADLOCK_C
-+// -- GODOT start --
-+// #define MBEDTLS_PADLOCK_C
-+// -- GODOT end --
-
- /**
- * \def MBEDTLS_PEM_PARSE_C