summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.github/workflows/ios_builds.yml50
-rw-r--r--.github/workflows/linux_builds.yml2
-rw-r--r--.github/workflows/macos_builds.yml4
-rw-r--r--.github/workflows/windows_builds.yml2
-rw-r--r--SConstruct17
-rw-r--r--core/bind/core_bind.cpp1
-rw-r--r--core/callable_method_pointer.h4
-rw-r--r--core/global_constants.cpp32
-rw-r--r--core/global_constants.h1
-rw-r--r--core/object.cpp93
-rw-r--r--core/object.h3
-rw-r--r--core/project_settings.cpp19
-rw-r--r--core/project_settings.h10
-rw-r--r--core/script_language.cpp23
-rw-r--r--core/script_language.h5
-rw-r--r--core/ustring.cpp6
-rw-r--r--core/variant.cpp4
-rw-r--r--doc/classes/@GlobalScope.xml2
-rw-r--r--doc/classes/ProjectSettings.xml6
-rw-r--r--doc/classes/TileSet.xml12
-rw-r--r--drivers/vulkan/SCsub32
-rw-r--r--editor/code_editor.cpp2
-rw-r--r--editor/doc_data.cpp32
-rw-r--r--editor/doc_data.h1
-rw-r--r--editor/plugins/script_editor_plugin.cpp109
-rw-r--r--editor/plugins/script_editor_plugin.h3
-rw-r--r--editor/plugins/script_text_editor.cpp236
-rw-r--r--editor/plugins/script_text_editor.h43
-rw-r--r--editor/plugins/shader_editor_plugin.cpp25
-rw-r--r--editor/plugins/text_editor.cpp50
-rw-r--r--editor/plugins/text_editor.h21
-rw-r--r--main/SCsub24
-rw-r--r--main/main.cpp14
-rw-r--r--main/tests/SCsub9
-rw-r--r--misc/dist/ios_xcode/godot_ios.xcodeproj/project.pbxproj12
-rw-r--r--misc/dist/ios_xcode/godot_ios/vulkan/icd.d/MoltenVK_icd.json7
-rw-r--r--modules/SCsub13
-rw-r--r--modules/arkit/arkit_interface.h34
-rw-r--r--modules/arkit/arkit_interface.mm371
-rw-r--r--modules/arkit/arkit_session_delegate.h6
-rw-r--r--modules/bullet/shape_bullet.cpp3
-rw-r--r--modules/camera/camera_ios.mm93
-rw-r--r--modules/gdnative/nativescript/nativescript.cpp28
-rw-r--r--modules/gdnative/nativescript/nativescript.h3
-rw-r--r--modules/gdnative/pluginscript/pluginscript_instance.h6
-rw-r--r--modules/gdscript/gdscript.cpp33
-rw-r--r--modules/gdscript/gdscript.h5
-rw-r--r--modules/gdscript/gdscript_compiler.cpp2
-rw-r--r--modules/modules_builders.py11
-rw-r--r--modules/mono/csharp_script.cpp39
-rw-r--r--modules/mono/csharp_script.h4
-rw-r--r--modules/visual_script/visual_script_editor.cpp14
-rw-r--r--modules/visual_script/visual_script_editor.h1
-rw-r--r--platform/iphone/SCsub12
-rw-r--r--platform/iphone/app_delegate.h19
-rw-r--r--platform/iphone/app_delegate.mm659
-rw-r--r--platform/iphone/detect.py24
-rw-r--r--platform/iphone/display_layer.h58
-rw-r--r--platform/iphone/display_layer.mm186
-rw-r--r--platform/iphone/display_server_iphone.h202
-rw-r--r--platform/iphone/display_server_iphone.mm751
-rw-r--r--platform/iphone/game_center.h6
-rw-r--r--platform/iphone/game_center.mm61
-rw-r--r--platform/iphone/gl_view.h123
-rw-r--r--platform/iphone/gl_view.mm702
-rw-r--r--platform/iphone/godot_iphone.mm (renamed from platform/iphone/godot_iphone.cpp)56
-rw-r--r--platform/iphone/godot_view.h56
-rw-r--r--platform/iphone/godot_view.mm499
-rw-r--r--platform/iphone/godot_view_gesture_recognizer.h44
-rw-r--r--platform/iphone/godot_view_gesture_recognizer.m171
-rw-r--r--platform/iphone/godot_view_renderer.h44
-rw-r--r--platform/iphone/godot_view_renderer.mm146
-rw-r--r--platform/iphone/icloud.h6
-rw-r--r--platform/iphone/icloud.mm30
-rw-r--r--platform/iphone/in_app_store.h4
-rw-r--r--platform/iphone/in_app_store.mm40
-rw-r--r--platform/iphone/ios.mm18
-rw-r--r--platform/iphone/joypad_iphone.h50
-rw-r--r--platform/iphone/joypad_iphone.mm380
-rw-r--r--platform/iphone/main.m15
-rw-r--r--platform/iphone/os_iphone.cpp632
-rw-r--r--platform/iphone/os_iphone.h151
-rw-r--r--platform/iphone/os_iphone.mm369
-rw-r--r--platform/iphone/view_controller.h20
-rw-r--r--platform/iphone/view_controller.mm356
-rw-r--r--platform/iphone/vulkan_context_iphone.h5
-rw-r--r--platform/iphone/vulkan_context_iphone.mm22
-rw-r--r--platform/osx/display_server_osx.mm18
-rw-r--r--scene/3d/node_3d.cpp4
-rw-r--r--scene/gui/scroll_bar.cpp2
-rw-r--r--scene/main/canvas_item.cpp2
-rw-r--r--scene/main/node.cpp12
-rw-r--r--scene/main/scene_tree.cpp14
-rw-r--r--scene/main/scene_tree.h1
-rw-r--r--scene/main/viewport.cpp8
-rw-r--r--scene/register_scene_types.cpp2
-rw-r--r--scene/resources/visual_shader.cpp71
-rw-r--r--scene/resources/visual_shader.h6
-rw-r--r--scene/resources/visual_shader_nodes.cpp4
-rw-r--r--scene/resources/visual_shader_nodes.h2
-rw-r--r--tests/SCsub15
-rw-r--r--tests/test_astar.cpp (renamed from main/tests/test_astar.cpp)0
-rw-r--r--tests/test_astar.h (renamed from main/tests/test_astar.h)0
-rw-r--r--tests/test_basis.cpp (renamed from main/tests/test_basis.cpp)0
-rw-r--r--tests/test_basis.h (renamed from main/tests/test_basis.h)0
-rw-r--r--tests/test_class_db.cpp (renamed from main/tests/test_class_db.cpp)0
-rw-r--r--tests/test_class_db.h (renamed from main/tests/test_class_db.h)0
-rw-r--r--tests/test_gdscript.cpp (renamed from main/tests/test_gdscript.cpp)0
-rw-r--r--tests/test_gdscript.h (renamed from main/tests/test_gdscript.h)0
-rw-r--r--tests/test_gui.cpp (renamed from main/tests/test_gui.cpp)0
-rw-r--r--tests/test_gui.h (renamed from main/tests/test_gui.h)0
-rw-r--r--tests/test_main.cpp (renamed from main/tests/test_main.cpp)2
-rw-r--r--tests/test_main.h (renamed from main/tests/test_main.h)0
-rw-r--r--tests/test_math.cpp (renamed from main/tests/test_math.cpp)0
-rw-r--r--tests/test_math.h (renamed from main/tests/test_math.h)0
-rw-r--r--tests/test_oa_hash_map.cpp (renamed from main/tests/test_oa_hash_map.cpp)0
-rw-r--r--tests/test_oa_hash_map.h (renamed from main/tests/test_oa_hash_map.h)0
-rw-r--r--tests/test_ordered_hash_map.cpp (renamed from main/tests/test_ordered_hash_map.cpp)0
-rw-r--r--tests/test_ordered_hash_map.h (renamed from main/tests/test_ordered_hash_map.h)0
-rw-r--r--tests/test_physics_2d.cpp (renamed from main/tests/test_physics_2d.cpp)0
-rw-r--r--tests/test_physics_2d.h (renamed from main/tests/test_physics_2d.h)0
-rw-r--r--tests/test_physics_3d.cpp (renamed from main/tests/test_physics_3d.cpp)0
-rw-r--r--tests/test_physics_3d.h (renamed from main/tests/test_physics_3d.h)0
-rw-r--r--tests/test_render.cpp (renamed from main/tests/test_render.cpp)0
-rw-r--r--tests/test_render.h (renamed from main/tests/test_render.h)0
-rw-r--r--tests/test_shader_lang.cpp (renamed from main/tests/test_shader_lang.cpp)0
-rw-r--r--tests/test_shader_lang.h (renamed from main/tests/test_shader_lang.h)0
-rw-r--r--tests/test_string.h (renamed from main/tests/test_string.h)0
-rw-r--r--tests/test_validate_testing.h (renamed from main/tests/test_validate_testing.h)0
-rw-r--r--thirdparty/vulkan/patches/VMA-assert-remove.patch29
-rw-r--r--thirdparty/vulkan/vk_mem_alloc.h18
131 files changed, 4557 insertions, 3152 deletions
diff --git a/.github/workflows/ios_builds.yml b/.github/workflows/ios_builds.yml
new file mode 100644
index 0000000000..0657a6cb18
--- /dev/null
+++ b/.github/workflows/ios_builds.yml
@@ -0,0 +1,50 @@
+name: iOS Builds
+on: [push, pull_request]
+
+# Global Cache Settings
+env:
+ GODOT_BASE_BRANCH: master
+ SCONS_CACHE_LIMIT: 4096
+
+jobs:
+ ios-template:
+ runs-on: "macos-latest"
+ name: Template (target=release, tools=no)
+
+ steps:
+ - uses: actions/checkout@v2
+
+ # Upload cache on completion and check it out now
+ - name: Load .scons_cache directory
+ id: ios-template-cache
+ uses: actions/cache@v2
+ with:
+ path: ${{github.workspace}}/.scons_cache/
+ key: ${{github.job}}-${{env.GODOT_BASE_BRANCH}}-${{github.ref}}-${{github.sha}}
+ restore-keys: |
+ ${{github.job}}-${{env.GODOT_BASE_BRANCH}}-${{github.ref}}-${{github.sha}}
+ ${{github.job}}-${{env.GODOT_BASE_BRANCH}}-${{github.ref}}
+ ${{github.job}}-${{env.GODOT_BASE_BRANCH}}
+
+ # Use python 3.x release (works cross platform)
+ - name: Set up Python 3.x
+ uses: actions/setup-python@v2
+ with:
+ # Semantic version range syntax or exact version of a Python version
+ python-version: '3.x'
+ # Optional - x64 or x86 architecture, defaults to x64
+ architecture: 'x64'
+
+ # You can test your matrix by printing the current Python version
+ - name: Configuring Python packages
+ run: |
+ python -c "import sys; print(sys.version)"
+ python -m pip install scons
+ python --version
+ scons --version
+
+ - name: Compilation
+ env:
+ SCONS_CACHE: ${{github.workspace}}/.scons_cache/
+ run: |
+ scons -j2 verbose=yes warnings=all werror=yes platform=iphone target=release tools=no
diff --git a/.github/workflows/linux_builds.yml b/.github/workflows/linux_builds.yml
index be7737593a..a05bb09931 100644
--- a/.github/workflows/linux_builds.yml
+++ b/.github/workflows/linux_builds.yml
@@ -60,7 +60,7 @@ jobs:
env:
SCONS_CACHE: ${{github.workspace}}/.scons_cache/
run: |
- scons -j2 verbose=yes warnings=all werror=yes platform=linuxbsd tools=yes target=release_debug module_mono_enabled=yes mono_glue=no
+ scons -j2 verbose=yes warnings=all werror=yes platform=linuxbsd tools=yes tests=yes target=release_debug module_mono_enabled=yes mono_glue=no
# Execute unit tests for the editor
- name: Unit Tests
diff --git a/.github/workflows/macos_builds.yml b/.github/workflows/macos_builds.yml
index 4a9fa910d0..d9f41f09f7 100644
--- a/.github/workflows/macos_builds.yml
+++ b/.github/workflows/macos_builds.yml
@@ -1,4 +1,4 @@
-name: MacOS Builds
+name: macOS Builds
on: [push, pull_request]
# Global Cache Settings
@@ -49,7 +49,7 @@ jobs:
env:
SCONS_CACHE: ${{github.workspace}}/.scons_cache/
run: |
- scons -j2 verbose=yes warnings=all werror=yes platform=osx tools=yes target=release_debug
+ scons -j2 verbose=yes warnings=all werror=yes platform=osx tools=yes tests=yes target=release_debug
# Execute unit tests for the editor
- name: Unit Tests
diff --git a/.github/workflows/windows_builds.yml b/.github/workflows/windows_builds.yml
index c58bae8275..2bc3fcfdaa 100644
--- a/.github/workflows/windows_builds.yml
+++ b/.github/workflows/windows_builds.yml
@@ -54,7 +54,7 @@ jobs:
env:
SCONS_CACHE: /.scons_cache/
run: |
- scons -j2 verbose=yes warnings=all werror=yes platform=windows tools=yes target=release_debug
+ scons -j2 verbose=yes warnings=all werror=yes platform=windows tools=yes tests=yes target=release_debug
# Execute unit tests for the editor
- name: Unit Tests
diff --git a/SConstruct b/SConstruct
index e23aa1cdbc..857daf7b0e 100644
--- a/SConstruct
+++ b/SConstruct
@@ -115,6 +115,7 @@ opts.Add(EnumVariable("target", "Compilation target", "debug", ("debug", "releas
opts.Add(EnumVariable("optimize", "Optimization type", "speed", ("speed", "size")))
opts.Add(BoolVariable("tools", "Build the tools (a.k.a. the Godot editor)", True))
+opts.Add(BoolVariable("tests", "Build the unit tests", False))
opts.Add(BoolVariable("use_lto", "Use link-time optimization", False))
opts.Add(BoolVariable("use_precise_math_checks", "Math checks use very precise epsilon (debug option)", False))
@@ -249,6 +250,10 @@ if env_base["target"] == "debug":
# http://scons.org/doc/production/HTML/scons-user/ch06s04.html
env_base.SetOption("implicit_cache", 1)
+if not env_base["tools"]:
+ # Export templates can't run unit test tool.
+ env_base["tests"] = False
+
if env_base["no_editor_splash"]:
env_base.Append(CPPDEFINES=["NO_EDITOR_SPLASH"])
@@ -312,6 +317,8 @@ if selected_platform in platform_list:
env["verbose"] = True
env["warnings"] = "extra"
env["werror"] = True
+ if env["tools"]:
+ env["tests"] = True
if env["vsproj"]:
env.vs_incs = []
@@ -641,9 +648,6 @@ if selected_platform in platform_list:
}
)
- # enable test framework globally and inform it of configuration method
- env.Append(CPPDEFINES=["DOCTEST_CONFIG_IMPLEMENT"])
-
scons_cache_path = os.environ.get("SCONS_CACHE")
if scons_cache_path != None:
CacheDir(scons_cache_path)
@@ -651,8 +655,7 @@ if selected_platform in platform_list:
Export("env")
- # build subdirs, the build order is dependent on link order.
-
+ # Build subdirs, the build order is dependent on link order.
SConscript("core/SCsub")
SConscript("servers/SCsub")
SConscript("scene/SCsub")
@@ -661,9 +664,11 @@ if selected_platform in platform_list:
SConscript("platform/SCsub")
SConscript("modules/SCsub")
+ if env["tests"]:
+ SConscript("tests/SCsub")
SConscript("main/SCsub")
- SConscript("platform/" + selected_platform + "/SCsub") # build selected platform
+ SConscript("platform/" + selected_platform + "/SCsub") # Build selected platform.
# Microsoft Visual Studio Project Generation
if env["vsproj"]:
diff --git a/core/bind/core_bind.cpp b/core/bind/core_bind.cpp
index 2f8b11652b..045d7d5872 100644
--- a/core/bind/core_bind.cpp
+++ b/core/bind/core_bind.cpp
@@ -781,6 +781,7 @@ void _OS::_bind_methods() {
// Those default values need to be specified for the docs generator,
// to avoid using values from the documentation writer's own OS instance.
+ ADD_PROPERTY_DEFAULT("tablet_driver", "");
ADD_PROPERTY_DEFAULT("exit_code", 0);
ADD_PROPERTY_DEFAULT("low_processor_usage_mode", false);
ADD_PROPERTY_DEFAULT("low_processor_usage_mode_sleep_usec", 6900);
diff --git a/core/callable_method_pointer.h b/core/callable_method_pointer.h
index 22db7d1c82..1bb89e53e1 100644
--- a/core/callable_method_pointer.h
+++ b/core/callable_method_pointer.h
@@ -131,7 +131,7 @@ void call_with_variant_args_helper(T *p_instance, void (T::*p_method)(P...), con
#ifdef DEBUG_METHODS_ENABLED
(p_instance->*p_method)(VariantCasterAndValidate<P>::cast(p_args, Is, r_error)...);
#else
- (p_instance->*p_method)(VariantCaster<P>::cast(p_args[Is])...);
+ (p_instance->*p_method)(VariantCaster<P>::cast(*p_args[Is])...);
#endif
}
@@ -228,7 +228,7 @@ void call_with_variant_args_ret_helper(T *p_instance, R (T::*p_method)(P...), co
#ifdef DEBUG_METHODS_ENABLED
r_ret = (p_instance->*p_method)(VariantCasterAndValidate<P>::cast(p_args, Is, r_error)...);
#else
- (p_instance->*p_method)(VariantCaster<P>::cast(p_args[Is])...);
+ (p_instance->*p_method)(VariantCaster<P>::cast(*p_args[Is])...);
#endif
}
diff --git a/core/global_constants.cpp b/core/global_constants.cpp
index 6281e56395..b30685539a 100644
--- a/core/global_constants.cpp
+++ b/core/global_constants.cpp
@@ -38,6 +38,7 @@
struct _GlobalConstant {
#ifdef DEBUG_METHODS_ENABLED
StringName enum_name;
+ bool ignore_value_in_docs;
#endif
const char *name;
int value;
@@ -45,8 +46,9 @@ struct _GlobalConstant {
_GlobalConstant() {}
#ifdef DEBUG_METHODS_ENABLED
- _GlobalConstant(const StringName &p_enum_name, const char *p_name, int p_value) :
+ _GlobalConstant(const StringName &p_enum_name, const char *p_name, int p_value, bool p_ignore_value_in_docs = false) :
enum_name(p_enum_name),
+ ignore_value_in_docs(p_ignore_value_in_docs),
name(p_name),
value(p_value) {
}
@@ -71,6 +73,15 @@ static Vector<_GlobalConstant> _global_constants;
#define BIND_GLOBAL_ENUM_CONSTANT_CUSTOM(m_custom_name, m_constant) \
_global_constants.push_back(_GlobalConstant(__constant_get_enum_name(m_constant, #m_constant), m_custom_name, m_constant));
+#define BIND_GLOBAL_CONSTANT_NO_VAL(m_constant) \
+ _global_constants.push_back(_GlobalConstant(StringName(), #m_constant, m_constant, true));
+
+#define BIND_GLOBAL_ENUM_CONSTANT_NO_VAL(m_constant) \
+ _global_constants.push_back(_GlobalConstant(__constant_get_enum_name(m_constant, #m_constant), #m_constant, m_constant, true));
+
+#define BIND_GLOBAL_ENUM_CONSTANT_CUSTOM_NO_VAL(m_custom_name, m_constant) \
+ _global_constants.push_back(_GlobalConstant(__constant_get_enum_name(m_constant, #m_constant), m_custom_name, m_constant, true));
+
#else
#define BIND_GLOBAL_CONSTANT(m_constant) \
@@ -82,6 +93,15 @@ static Vector<_GlobalConstant> _global_constants;
#define BIND_GLOBAL_ENUM_CONSTANT_CUSTOM(m_custom_name, m_constant) \
_global_constants.push_back(_GlobalConstant(m_custom_name, m_constant));
+#define BIND_GLOBAL_CONSTANT_NO_VAL(m_constant) \
+ _global_constants.push_back(_GlobalConstant(#m_constant, m_constant));
+
+#define BIND_GLOBAL_ENUM_CONSTANT_NO_VAL(m_constant) \
+ _global_constants.push_back(_GlobalConstant(#m_constant, m_constant));
+
+#define BIND_GLOBAL_ENUM_CONSTANT_CUSTOM_NO_VAL(m_custom_name, m_constant) \
+ _global_constants.push_back(_GlobalConstant(m_custom_name, m_constant));
+
#endif
VARIANT_ENUM_CAST(KeyList);
@@ -368,7 +388,7 @@ void register_global_constants() {
BIND_GLOBAL_ENUM_CONSTANT(KEY_MASK_ALT);
BIND_GLOBAL_ENUM_CONSTANT(KEY_MASK_META);
BIND_GLOBAL_ENUM_CONSTANT(KEY_MASK_CTRL);
- BIND_GLOBAL_ENUM_CONSTANT(KEY_MASK_CMD);
+ BIND_GLOBAL_ENUM_CONSTANT_NO_VAL(KEY_MASK_CMD);
BIND_GLOBAL_ENUM_CONSTANT(KEY_MASK_KPAD);
BIND_GLOBAL_ENUM_CONSTANT(KEY_MASK_GROUP_SWITCH);
@@ -648,10 +668,18 @@ int GlobalConstants::get_global_constant_count() {
StringName GlobalConstants::get_global_constant_enum(int p_idx) {
return _global_constants[p_idx].enum_name;
}
+
+bool GlobalConstants::get_ignore_value_in_docs(int p_idx) {
+ return _global_constants[p_idx].ignore_value_in_docs;
+}
#else
StringName GlobalConstants::get_global_constant_enum(int p_idx) {
return StringName();
}
+
+bool GlobalConstants::get_ignore_value_in_docs(int p_idx) {
+ return false;
+}
#endif
const char *GlobalConstants::get_global_constant_name(int p_idx) {
diff --git a/core/global_constants.h b/core/global_constants.h
index a20b5ecd9a..989633a6fa 100644
--- a/core/global_constants.h
+++ b/core/global_constants.h
@@ -37,6 +37,7 @@ class GlobalConstants {
public:
static int get_global_constant_count();
static StringName get_global_constant_enum(int p_idx);
+ static bool get_ignore_value_in_docs(int p_idx);
static const char *get_global_constant_name(int p_idx);
static int get_global_constant_value(int p_idx);
};
diff --git a/core/object.cpp b/core/object.cpp
index ba002024e6..ff6d4a666f 100644
--- a/core/object.cpp
+++ b/core/object.cpp
@@ -680,84 +680,6 @@ Variant Object::_call_deferred_bind(const Variant **p_args, int p_argcount, Call
return Variant();
}
-#ifdef DEBUG_ENABLED
-static void _test_call_error(const StringName &p_func, const Callable::CallError &error) {
- switch (error.error) {
- case Callable::CallError::CALL_OK:
- case Callable::CallError::CALL_ERROR_INVALID_METHOD:
- break;
- case Callable::CallError::CALL_ERROR_INVALID_ARGUMENT: {
- ERR_FAIL_MSG("Error calling function: " + String(p_func) + " - Invalid type for argument " + itos(error.argument) + ", expected " + Variant::get_type_name(Variant::Type(error.expected)) + ".");
- break;
- }
- case Callable::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS: {
- ERR_FAIL_MSG("Error calling function: " + String(p_func) + " - Too many arguments, expected " + itos(error.argument) + ".");
- break;
- }
- case Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS: {
- ERR_FAIL_MSG("Error calling function: " + String(p_func) + " - Too few arguments, expected " + itos(error.argument) + ".");
- break;
- }
- case Callable::CallError::CALL_ERROR_INSTANCE_IS_NULL:
- break;
- }
-}
-#else
-
-#define _test_call_error(m_str, m_err)
-
-#endif
-
-void Object::call_multilevel(const StringName &p_method, const Variant **p_args, int p_argcount) {
- if (p_method == CoreStringNames::get_singleton()->_free) {
-#ifdef DEBUG_ENABLED
- ERR_FAIL_COND_MSG(Object::cast_to<Reference>(this), "Can't 'free' a reference.");
-
- ERR_FAIL_COND_MSG(_lock_index.get() > 1, "Object is locked and can't be freed.");
-#endif
-
- //must be here, must be before everything,
- memdelete(this);
- return;
- }
-
- //Variant ret;
- OBJ_DEBUG_LOCK
-
- Callable::CallError error;
-
- if (script_instance) {
- script_instance->call_multilevel(p_method, p_args, p_argcount);
- //_test_call_error(p_method,error);
- }
-
- MethodBind *method = ClassDB::get_method(get_class_name(), p_method);
-
- if (method) {
- method->call(this, p_args, p_argcount, error);
- _test_call_error(p_method, error);
- }
-}
-
-void Object::call_multilevel_reversed(const StringName &p_method, const Variant **p_args, int p_argcount) {
- MethodBind *method = ClassDB::get_method(get_class_name(), p_method);
-
- Callable::CallError error;
- OBJ_DEBUG_LOCK
-
- if (method) {
- method->call(this, p_args, p_argcount, error);
- _test_call_error(p_method, error);
- }
-
- //Variant ret;
-
- if (script_instance) {
- script_instance->call_multilevel_reversed(p_method, p_args, p_argcount);
- //_test_call_error(p_method,error);
- }
-}
-
bool Object::has_method(const StringName &p_method) const {
if (p_method == CoreStringNames::get_singleton()->_free) {
return true;
@@ -820,21 +742,6 @@ Variant Object::call(const StringName &p_name, VARIANT_ARG_DECLARE) {
return ret;
}
-void Object::call_multilevel(const StringName &p_name, VARIANT_ARG_DECLARE) {
- VARIANT_ARGPTRS;
-
- int argc = 0;
- for (int i = 0; i < VARIANT_ARG_MAX; i++) {
- if (argptr[i]->get_type() == Variant::NIL) {
- break;
- }
- argc++;
- }
-
- //Callable::CallError error;
- call_multilevel(p_name, argptr, argc);
-}
-
Variant Object::call(const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error) {
r_error.error = Callable::CallError::CALL_OK;
diff --git a/core/object.h b/core/object.h
index 954be5304c..d9847d10aa 100644
--- a/core/object.h
+++ b/core/object.h
@@ -655,10 +655,7 @@ public:
void get_method_list(List<MethodInfo> *p_list) const;
Variant callv(const StringName &p_method, const Array &p_args);
virtual Variant call(const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error);
- virtual void call_multilevel(const StringName &p_method, const Variant **p_args, int p_argcount);
- virtual void call_multilevel_reversed(const StringName &p_method, const Variant **p_args, int p_argcount);
Variant call(const StringName &p_name, VARIANT_ARG_LIST); // C++ helper
- void call_multilevel(const StringName &p_name, VARIANT_ARG_LIST); // C++ helper
void notification(int p_notification, bool p_reversed = false);
String to_string();
diff --git a/core/project_settings.cpp b/core/project_settings.cpp
index 638987bb2f..e08a44d0c1 100644
--- a/core/project_settings.cpp
+++ b/core/project_settings.cpp
@@ -122,6 +122,22 @@ void ProjectSettings::set_restart_if_changed(const String &p_name, bool p_restar
props[p_name].restart_if_changed = p_restart;
}
+void ProjectSettings::set_ignore_value_in_docs(const String &p_name, bool p_ignore) {
+ ERR_FAIL_COND_MSG(!props.has(p_name), "Request for nonexistent project setting: " + p_name + ".");
+#ifdef DEBUG_METHODS_ENABLED
+ props[p_name].ignore_value_in_docs = p_ignore;
+#endif
+}
+
+bool ProjectSettings::get_ignore_value_in_docs(const String &p_name) const {
+ ERR_FAIL_COND_V_MSG(!props.has(p_name), false, "Request for nonexistent project setting: " + p_name + ".");
+#ifdef DEBUG_METHODS_ENABLED
+ return props[p_name].ignore_value_in_docs;
+#else
+ return false;
+#endif
+}
+
String ProjectSettings::globalize_path(const String &p_path) const {
if (p_path.begins_with("res://")) {
if (resource_path != "") {
@@ -876,7 +892,7 @@ Error ProjectSettings::save_custom(const String &p_path, const CustomMap &p_cust
}
}
-Variant _GLOBAL_DEF(const String &p_var, const Variant &p_default, bool p_restart_if_changed) {
+Variant _GLOBAL_DEF(const String &p_var, const Variant &p_default, bool p_restart_if_changed, bool p_ignore_value_in_docs) {
Variant ret;
if (!ProjectSettings::get_singleton()->has_setting(p_var)) {
ProjectSettings::get_singleton()->set(p_var, p_default);
@@ -886,6 +902,7 @@ Variant _GLOBAL_DEF(const String &p_var, const Variant &p_default, bool p_restar
ProjectSettings::get_singleton()->set_initial_value(p_var, p_default);
ProjectSettings::get_singleton()->set_builtin_order(p_var);
ProjectSettings::get_singleton()->set_restart_if_changed(p_var, p_restart_if_changed);
+ ProjectSettings::get_singleton()->set_ignore_value_in_docs(p_var, p_ignore_value_in_docs);
return ret;
}
diff --git a/core/project_settings.h b/core/project_settings.h
index 4aceafe3c0..659ee402b3 100644
--- a/core/project_settings.h
+++ b/core/project_settings.h
@@ -62,6 +62,9 @@ protected:
bool hide_from_editor = false;
bool overridden = false;
bool restart_if_changed = false;
+#ifdef DEBUG_METHODS_ENABLED
+ bool ignore_value_in_docs = false;
+#endif
VariantContainer() {}
@@ -125,6 +128,9 @@ public:
void set_initial_value(const String &p_name, const Variant &p_value);
void set_restart_if_changed(const String &p_name, bool p_restart);
+ void set_ignore_value_in_docs(const String &p_name, bool p_ignore);
+ bool get_ignore_value_in_docs(const String &p_name) const;
+
bool property_can_revert(const String &p_name);
Variant property_get_revert(const String &p_name);
@@ -167,9 +173,11 @@ public:
};
//not a macro any longer
-Variant _GLOBAL_DEF(const String &p_var, const Variant &p_default, bool p_restart_if_changed = false);
+Variant _GLOBAL_DEF(const String &p_var, const Variant &p_default, bool p_restart_if_changed = false, bool p_ignore_value_in_docs = false);
#define GLOBAL_DEF(m_var, m_value) _GLOBAL_DEF(m_var, m_value)
#define GLOBAL_DEF_RST(m_var, m_value) _GLOBAL_DEF(m_var, m_value, true)
+#define GLOBAL_DEF_NOVAL(m_var, m_value) _GLOBAL_DEF(m_var, m_value, false, true)
+#define GLOBAL_DEF_RST_NOVAL(m_var, m_value) _GLOBAL_DEF(m_var, m_value, true, true)
#define GLOBAL_GET(m_var) ProjectSettings::get_singleton()->get(m_var)
#endif // PROJECT_SETTINGS_H
diff --git a/core/script_language.cpp b/core/script_language.cpp
index 420a560782..b63aeb952c 100644
--- a/core/script_language.cpp
+++ b/core/script_language.cpp
@@ -308,16 +308,6 @@ Variant ScriptInstance::call(const StringName &p_method, VARIANT_ARG_DECLARE) {
return call(p_method, argptr, argc, error);
}
-void ScriptInstance::call_multilevel(const StringName &p_method, const Variant **p_args, int p_argcount) {
- Callable::CallError ce;
- call(p_method, p_args, p_argcount, ce); // script may not support multilevel calls
-}
-
-void ScriptInstance::call_multilevel_reversed(const StringName &p_method, const Variant **p_args, int p_argcount) {
- Callable::CallError ce;
- call(p_method, p_args, p_argcount, ce); // script may not support multilevel calls
-}
-
void ScriptInstance::property_set_fallback(const StringName &, const Variant &, bool *r_valid) {
if (r_valid) {
*r_valid = false;
@@ -331,19 +321,6 @@ Variant ScriptInstance::property_get_fallback(const StringName &, bool *r_valid)
return Variant();
}
-void ScriptInstance::call_multilevel(const StringName &p_method, VARIANT_ARG_DECLARE) {
- VARIANT_ARGPTRS;
- int argc = 0;
- for (int i = 0; i < VARIANT_ARG_MAX; i++) {
- if (argptr[i]->get_type() == Variant::NIL) {
- break;
- }
- argc++;
- }
-
- call_multilevel(p_method, argptr, argc);
-}
-
ScriptInstance::~ScriptInstance() {
}
diff --git a/core/script_language.h b/core/script_language.h
index 6ba38399a1..aa7014ed3e 100644
--- a/core/script_language.h
+++ b/core/script_language.h
@@ -199,9 +199,6 @@ public:
virtual bool has_method(const StringName &p_method) const = 0;
virtual Variant call(const StringName &p_method, VARIANT_ARG_LIST);
virtual Variant call(const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error) = 0;
- virtual void call_multilevel(const StringName &p_method, VARIANT_ARG_LIST);
- virtual void call_multilevel(const StringName &p_method, const Variant **p_args, int p_argcount);
- virtual void call_multilevel_reversed(const StringName &p_method, const Variant **p_args, int p_argcount);
virtual void notification(int p_notification) = 0;
virtual String to_string(bool *r_valid) {
if (r_valid) {
@@ -428,8 +425,6 @@ public:
r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD;
return Variant();
}
- //virtual void call_multilevel(const StringName& p_method,VARIANT_ARG_LIST) { return Variant(); }
- //virtual void call_multilevel(const StringName& p_method,const Variant** p_args,int p_argcount,Callable::CallError &r_error) { return Variant(); }
virtual void notification(int p_notification) {}
virtual Ref<Script> get_script() const { return script; }
diff --git a/core/ustring.cpp b/core/ustring.cpp
index 5d3cf5f1a4..572ad1af59 100644
--- a/core/ustring.cpp
+++ b/core/ustring.cpp
@@ -1025,6 +1025,9 @@ String String::chr(CharType p_char) {
}
String String::num(double p_num, int p_decimals) {
+ if (Math::is_nan(p_num)) {
+ return "nan";
+ }
#ifndef NO_USE_STDLIB
if (p_decimals > 16) {
@@ -1313,6 +1316,9 @@ String String::num_real(double p_num) {
}
String String::num_scientific(double p_num) {
+ if (Math::is_nan(p_num)) {
+ return "nan";
+ }
#ifndef NO_USE_STDLIB
char buf[256];
diff --git a/core/variant.cpp b/core/variant.cpp
index f6b7e2821a..afd01b3359 100644
--- a/core/variant.cpp
+++ b/core/variant.cpp
@@ -1454,7 +1454,7 @@ Variant::operator signed long() const {
case INT:
return _data._int;
case FLOAT:
- return _data._real;
+ return _data._float;
case STRING:
return operator String().to_int();
default: {
@@ -1474,7 +1474,7 @@ Variant::operator unsigned long() const {
case INT:
return _data._int;
case FLOAT:
- return _data._real;
+ return _data._float;
case STRING:
return operator String().to_int();
default: {
diff --git a/doc/classes/@GlobalScope.xml b/doc/classes/@GlobalScope.xml
index 9a28a0d085..7f7df33471 100644
--- a/doc/classes/@GlobalScope.xml
+++ b/doc/classes/@GlobalScope.xml
@@ -898,7 +898,7 @@
<constant name="KEY_MASK_CTRL" value="268435456" enum="KeyModifierMask">
Ctrl key mask.
</constant>
- <constant name="KEY_MASK_CMD" value="268435456" enum="KeyModifierMask">
+ <constant name="KEY_MASK_CMD" value="platform-dependent" enum="KeyModifierMask">
Command key mask. On macOS, this is equivalent to [constant KEY_MASK_META]. On other platforms, this is equivalent to [constant KEY_MASK_CTRL]. This mask should be preferred to [constant KEY_MASK_META] or [constant KEY_MASK_CTRL] for system shortcuts as it handles all platforms correctly.
</constant>
<constant name="KEY_MASK_KPAD" value="536870912" enum="KeyModifierMask">
diff --git a/doc/classes/ProjectSettings.xml b/doc/classes/ProjectSettings.xml
index e255ce2e1a..733bb559cb 100644
--- a/doc/classes/ProjectSettings.xml
+++ b/doc/classes/ProjectSettings.xml
@@ -244,7 +244,7 @@
<member name="audio/default_bus_layout" type="String" setter="" getter="" default="&quot;res://default_bus_layout.tres&quot;">
Default [AudioBusLayout] resource file to use in the project, unless overridden by the scene.
</member>
- <member name="audio/driver" type="String" setter="" getter="" default="&quot;PulseAudio&quot;">
+ <member name="audio/driver" type="String" setter="" getter="">
Specifies the audio driver to use. This setting is platform-dependent as each platform supports different audio drivers. If left empty, the default audio driver will be used.
</member>
<member name="audio/enable_audio_input" type="bool" setter="" getter="" default="false">
@@ -453,7 +453,7 @@
<member name="display/window/size/width" type="int" setter="" getter="" default="1024">
Sets the game's main viewport width. On desktop platforms, this is the default window size. Stretch mode settings also use this as a reference when enabled.
</member>
- <member name="display/window/tablet_driver" type="String" setter="" getter="" default="&quot;&quot;">
+ <member name="display/window/tablet_driver" type="String" setter="" getter="">
Specifies the tablet driver to use. If left empty, the default driver will be used.
</member>
<member name="display/window/vsync/use_vsync" type="bool" setter="" getter="" default="true">
@@ -472,7 +472,7 @@
<member name="gui/common/default_scroll_deadzone" type="int" setter="" getter="" default="0">
Default value for [member ScrollContainer.scroll_deadzone], which will be used for all [ScrollContainer]s unless overridden.
</member>
- <member name="gui/common/swap_cancel_ok" type="bool" setter="" getter="" default="false">
+ <member name="gui/common/swap_cancel_ok" type="bool" setter="" getter="">
If [code]true[/code], swaps Cancel and OK buttons in dialogs on Windows and UWP to follow interface conventions.
</member>
<member name="gui/common/text_edit_undo_stack_max_size" type="int" setter="" getter="" default="1024">
diff --git a/doc/classes/TileSet.xml b/doc/classes/TileSet.xml
index 9a78e45d46..4991a1e58b 100644
--- a/doc/classes/TileSet.xml
+++ b/doc/classes/TileSet.xml
@@ -478,7 +478,17 @@
<argument index="0" name="id" type="int">
</argument>
<description>
- Returns an array of the tile's shapes.
+ Returns an array of dictionaries describing the tile's shapes.
+ [b]Dictionary structure in the array returned by this method:[/b]
+ [codeblock]
+ {
+ "autotile_coord": Vector2,
+ "one_way": bool,
+ "one_way_margin": int,
+ "shape": CollisionShape2D,
+ "shape_transform": Transform2D,
+ }
+ [/codeblock]
</description>
</method>
<method name="tile_get_texture" qualifiers="const">
diff --git a/drivers/vulkan/SCsub b/drivers/vulkan/SCsub
index 91d0e42f80..61d91711da 100644
--- a/drivers/vulkan/SCsub
+++ b/drivers/vulkan/SCsub
@@ -4,6 +4,7 @@ Import("env")
env.add_source_files(env.drivers_sources, "*.cpp")
+# FIXME: Refactor all this to reduce code duplication.
if env["platform"] == "android":
# Use NDK Vulkan headers
thirdparty_dir = env["ANDROID_NDK_ROOT"] + "/sources/third_party/vulkan/src"
@@ -22,6 +23,17 @@ if env["platform"] == "android":
thirdparty_dir = "#thirdparty/vulkan"
vma_sources = [thirdparty_dir + "/android/vk_mem_alloc.cpp"]
env_thirdparty.add_source_files(env.drivers_sources, vma_sources)
+elif env["platform"] == "iphone":
+ # Use bundled Vulkan headers
+ thirdparty_dir = "#thirdparty/vulkan"
+ env.Prepend(CPPPATH=[thirdparty_dir, thirdparty_dir + "/include", thirdparty_dir + "/loader"])
+
+ # Build Vulkan memory allocator
+ env_thirdparty = env.Clone()
+ env_thirdparty.disable_warnings()
+
+ vma_sources = [thirdparty_dir + "/vk_mem_alloc.cpp"]
+ env_thirdparty.add_source_files(env.drivers_sources, vma_sources)
elif env["builtin_vulkan"]:
# Use bundled Vulkan headers
thirdparty_dir = "#thirdparty/vulkan"
@@ -70,16 +82,6 @@ elif env["builtin_vulkan"]:
'FALLBACK_CONFIG_DIRS=\\"%s\\"' % "/etc/xdg",
]
)
- elif env["platform"] == "iphone":
- env_thirdparty.AppendUnique(
- CPPDEFINES=[
- "VK_USE_PLATFORM_IOS_MVK",
- "VULKAN_NON_CMAKE_BUILD",
- 'SYSCONFDIR=\\"%s\\"' % "/etc",
- 'FALLBACK_DATA_DIRS=\\"%s\\"' % "/usr/local/share:/usr/share",
- 'FALLBACK_CONFIG_DIRS=\\"%s\\"' % "/etc/xdg",
- ]
- )
elif env["platform"] == "linuxbsd":
env_thirdparty.AppendUnique(
CPPDEFINES=[
@@ -99,3 +101,13 @@ elif env["builtin_vulkan"]:
loader_sources = [thirdparty_dir + "/loader/" + file for file in loader_sources]
env_thirdparty.add_source_files(env.drivers_sources, loader_sources)
env_thirdparty.add_source_files(env.drivers_sources, vma_sources)
+else: # Always build VMA.
+ thirdparty_dir = "#thirdparty/vulkan"
+ env.Prepend(CPPPATH=[thirdparty_dir])
+
+ # Build Vulkan loader library
+ env_thirdparty = env.Clone()
+ env_thirdparty.disable_warnings()
+ vma_sources = [thirdparty_dir + "/vk_mem_alloc.cpp"]
+
+ env_thirdparty.add_source_files(env.drivers_sources, vma_sources)
diff --git a/editor/code_editor.cpp b/editor/code_editor.cpp
index 9a87d6d38c..37db3ba780 100644
--- a/editor/code_editor.cpp
+++ b/editor/code_editor.cpp
@@ -1456,8 +1456,6 @@ void CodeTextEditor::set_edit_state(const Variant &p_state) {
text_editor->set_line_as_bookmark(bookmarks[i], true);
}
}
-
- text_editor->grab_focus();
}
void CodeTextEditor::set_error(const String &p_error) {
diff --git a/editor/doc_data.cpp b/editor/doc_data.cpp
index 54acbe9559..75b16b4510 100644
--- a/editor/doc_data.cpp
+++ b/editor/doc_data.cpp
@@ -321,12 +321,13 @@ void DocData::generate(bool p_basic_types) {
continue;
}
if (E->get().usage & PROPERTY_USAGE_EDITOR) {
- default_value = ProjectSettings::get_singleton()->property_get_revert(E->get().name);
- default_value_valid = true;
+ if (!ProjectSettings::get_singleton()->get_ignore_value_in_docs(E->get().name)) {
+ default_value = ProjectSettings::get_singleton()->property_get_revert(E->get().name);
+ default_value_valid = true;
+ }
}
} else {
default_value = get_documentation_default_value(name, E->get().name, default_value_valid);
-
if (inherited) {
bool base_default_value_valid = false;
Variant base_default_value = get_documentation_default_value(ClassDB::get_parent_class(name), E->get().name, base_default_value_valid);
@@ -478,6 +479,7 @@ void DocData::generate(bool p_basic_types) {
ConstantDoc constant;
constant.name = E->get();
constant.value = itos(ClassDB::get_integer_constant(name, E->get()));
+ constant.is_value_valid = true;
constant.enumeration = ClassDB::get_integer_constant_enum(name, E->get());
c.constants.push_back(constant);
}
@@ -620,6 +622,7 @@ void DocData::generate(bool p_basic_types) {
constant.name = E->get();
Variant value = Variant::get_constant_value(Variant::Type(i), E->get());
constant.value = value.get_type() == Variant::INT ? itos(value) : value.get_construct_string();
+ constant.is_value_valid = true;
c.constants.push_back(constant);
}
}
@@ -635,7 +638,12 @@ void DocData::generate(bool p_basic_types) {
for (int i = 0; i < GlobalConstants::get_global_constant_count(); i++) {
ConstantDoc cd;
cd.name = GlobalConstants::get_global_constant_name(i);
- cd.value = itos(GlobalConstants::get_global_constant_value(i));
+ if (!GlobalConstants::get_ignore_value_in_docs(i)) {
+ cd.value = itos(GlobalConstants::get_global_constant_value(i));
+ cd.is_value_valid = true;
+ } else {
+ cd.is_value_valid = false;
+ }
cd.enumeration = GlobalConstants::get_global_constant_enum(i);
c.constants.push_back(cd);
}
@@ -715,6 +723,7 @@ void DocData::generate(bool p_basic_types) {
ConstantDoc cd;
cd.name = E->get().first;
cd.value = E->get().second;
+ cd.is_value_valid = true;
c.constants.push_back(cd);
}
@@ -989,6 +998,7 @@ Error DocData::_load(Ref<XMLParser> parser) {
constant2.name = parser->get_attribute_value("name");
ERR_FAIL_COND_V(!parser->has_attribute("value"), ERR_FILE_CORRUPT);
constant2.value = parser->get_attribute_value("value");
+ constant2.is_value_valid = true;
if (parser->has_attribute("enum")) {
constant2.enumeration = parser->get_attribute_value("enum");
}
@@ -1178,10 +1188,18 @@ Error DocData::save_classes(const String &p_default_path, const Map<String, Stri
for (int i = 0; i < c.constants.size(); i++) {
const ConstantDoc &k = c.constants[i];
- if (k.enumeration != String()) {
- _write_string(f, 2, "<constant name=\"" + k.name + "\" value=\"" + k.value + "\" enum=\"" + k.enumeration + "\">");
+ if (k.is_value_valid) {
+ if (k.enumeration != String()) {
+ _write_string(f, 2, "<constant name=\"" + k.name + "\" value=\"" + k.value + "\" enum=\"" + k.enumeration + "\">");
+ } else {
+ _write_string(f, 2, "<constant name=\"" + k.name + "\" value=\"" + k.value + "\">");
+ }
} else {
- _write_string(f, 2, "<constant name=\"" + k.name + "\" value=\"" + k.value + "\">");
+ if (k.enumeration != String()) {
+ _write_string(f, 2, "<constant name=\"" + k.name + "\" value=\"platform-dependent\" enum=\"" + k.enumeration + "\">");
+ } else {
+ _write_string(f, 2, "<constant name=\"" + k.name + "\" value=\"platform-dependent\">");
+ }
}
_write_string(f, 3, k.description.strip_edges().xml_escape());
_write_string(f, 2, "</constant>");
diff --git a/editor/doc_data.h b/editor/doc_data.h
index 1880be81ed..a35cfb59c7 100644
--- a/editor/doc_data.h
+++ b/editor/doc_data.h
@@ -62,6 +62,7 @@ public:
struct ConstantDoc {
String name;
String value;
+ bool is_value_valid;
String enumeration;
String description;
bool operator<(const ConstantDoc &p_const) const {
diff --git a/editor/plugins/script_editor_plugin.cpp b/editor/plugins/script_editor_plugin.cpp
index 00fee90841..756444097d 100644
--- a/editor/plugins/script_editor_plugin.cpp
+++ b/editor/plugins/script_editor_plugin.cpp
@@ -696,18 +696,21 @@ void ScriptEditor::_close_tab(int p_idx, bool p_save, bool p_history_back) {
Node *tselected = tab_container->get_child(selected);
- ScriptEditorBase *current = Object::cast_to<ScriptEditorBase>(tab_container->get_child(selected));
+ ScriptEditorBase *current = Object::cast_to<ScriptEditorBase>(tselected);
if (current) {
Ref<Script> script = current->get_edited_resource();
- if (p_save) {
- // Do not try to save internal scripts
- if (!script.is_valid() || !(script->get_path() == "" || script->get_path().find("local://") != -1 || script->get_path().find("::") != -1)) {
+ if (p_save && script.is_valid()) {
+ // Do not try to save internal scripts, but prompt to save in-memory
+ // scripts which are not saved to disk yet (have empty path).
+ if (script->get_path().find("local://") == -1 && script->get_path().find("::") == -1) {
_menu_option(FILE_SAVE);
}
}
-
- if (script != nullptr) {
- previous_scripts.push_back(script->get_path());
+ if (script.is_valid()) {
+ if (!script->get_path().empty()) {
+ // Only saved scripts can be restored.
+ previous_scripts.push_back(script->get_path());
+ }
notify_script_close(script);
}
}
@@ -779,8 +782,10 @@ void ScriptEditor::_close_docs_tab() {
void ScriptEditor::_copy_script_path() {
ScriptEditorBase *se = _get_current_editor();
- RES script = se->get_edited_resource();
- DisplayServer::get_singleton()->clipboard_set(script->get_path());
+ if (se) {
+ RES script = se->get_edited_resource();
+ DisplayServer::get_singleton()->clipboard_set(script->get_path());
+ }
}
void ScriptEditor::_close_other_tabs() {
@@ -1038,17 +1043,19 @@ void ScriptEditor::_file_dialog_action(String p_file) {
} break;
case FILE_SAVE_AS: {
ScriptEditorBase *current = _get_current_editor();
+ if (current) {
+ RES resource = current->get_edited_resource();
+ String path = ProjectSettings::get_singleton()->localize_path(p_file);
+ Error err = _save_text_file(resource, path);
- String path = ProjectSettings::get_singleton()->localize_path(p_file);
- Error err = _save_text_file(current->get_edited_resource(), path);
+ if (err != OK) {
+ editor->show_accept(TTR("Error saving file!"), TTR("OK"));
+ return;
+ }
- if (err != OK) {
- editor->show_accept(TTR("Error saving file!"), TTR("OK"));
- return;
+ resource->set_path(path);
+ _update_script_names();
}
-
- ((Resource *)current->get_edited_resource().ptr())->set_path(path);
- _update_script_names();
} break;
case THEME_SAVE_AS: {
if (!EditorSettings::get_singleton()->save_text_editor_theme_as(p_file)) {
@@ -1240,13 +1247,14 @@ void ScriptEditor::_menu_option(int p_option) {
}
}
- Ref<TextFile> text_file = current->get_edited_resource();
+ RES resource = current->get_edited_resource();
+ Ref<TextFile> text_file = resource;
if (text_file != nullptr) {
current->apply_code();
_save_text_file(text_file, text_file->get_path());
break;
}
- editor->save_resource(current->get_edited_resource());
+ editor->save_resource(resource);
} break;
case FILE_SAVE_AS: {
@@ -1264,7 +1272,8 @@ void ScriptEditor::_menu_option(int p_option) {
}
}
- Ref<TextFile> text_file = current->get_edited_resource();
+ RES resource = current->get_edited_resource();
+ Ref<TextFile> text_file = resource;
if (text_file != nullptr) {
file_dialog->set_file_mode(EditorFileDialog::FILE_MODE_SAVE_FILE);
file_dialog->set_access(EditorFileDialog::ACCESS_FILESYSTEM);
@@ -1280,8 +1289,8 @@ void ScriptEditor::_menu_option(int p_option) {
break;
}
- editor->push_item(Object::cast_to<Object>(current->get_edited_resource().ptr()));
- editor->save_resource_as(current->get_edited_resource());
+ editor->push_item(resource.ptr());
+ editor->save_resource_as(resource);
} break;
@@ -1465,6 +1474,7 @@ void ScriptEditor::_notification(int p_what) {
editor->connect("stop_pressed", callable_mp(this, &ScriptEditor::_editor_stop));
editor->connect("script_add_function_request", callable_mp(this, &ScriptEditor::_add_callback));
editor->connect("resource_saved", callable_mp(this, &ScriptEditor::_res_saved_callback));
+ editor->get_filesystem_dock()->connect("file_removed", callable_mp(this, &ScriptEditor::_file_removed));
script_list->connect("item_selected", callable_mp(this, &ScriptEditor::_script_selected));
members_overview->connect("item_selected", callable_mp(this, &ScriptEditor::_members_overview_selected));
@@ -1472,6 +1482,7 @@ void ScriptEditor::_notification(int p_what) {
script_split->connect("dragged", callable_mp(this, &ScriptEditor::_script_split_dragged));
EditorSettings::get_singleton()->connect("settings_changed", callable_mp(this, &ScriptEditor::_editor_settings_changed));
+ EditorFileSystem::get_singleton()->connect("filesystem_changed", callable_mp(this, &ScriptEditor::_filesystem_changed));
[[fallthrough]];
}
case NOTIFICATION_THEME_CHANGED: {
@@ -1632,6 +1643,8 @@ void ScriptEditor::ensure_select_current() {
if (tab_container->get_child_count() && tab_container->get_current_tab() >= 0) {
ScriptEditorBase *se = _get_current_editor();
if (se) {
+ se->enable_editor();
+
if (!grab_focus_block && is_visible_in_tree()) {
se->ensure_focus();
}
@@ -1842,6 +1855,12 @@ void ScriptEditor::_update_script_names() {
if (se) {
Ref<Texture2D> icon = se->get_theme_icon();
String path = se->get_edited_resource()->get_path();
+ bool saved = !path.empty();
+ if (saved) {
+ // The script might be deleted, moved, or renamed, so make sure
+ // to update original path to previously edited resource.
+ se->set_meta("_edit_res_path", path);
+ }
bool built_in = !path.is_resource_file();
String name;
@@ -1860,7 +1879,7 @@ void ScriptEditor::_update_script_names() {
_ScriptEditorItemData sd;
sd.icon = icon;
sd.name = name;
- sd.tooltip = path;
+ sd.tooltip = saved ? path : TTR("Unsaved file.");
sd.index = i;
sd.used = used.has(se->get_edited_resource());
sd.category = 0;
@@ -1893,6 +1912,9 @@ void ScriptEditor::_update_script_names() {
sd.name = path;
} break;
}
+ if (!saved) {
+ sd.name = se->get_name();
+ }
sedata.push_back(sd);
}
@@ -1976,6 +1998,11 @@ void ScriptEditor::_update_script_names() {
script_list->select(index);
script_name_label->set_text(sedata_filtered[i].name);
script_icon->set_texture(sedata_filtered[i].icon);
+ ScriptEditorBase *se = _get_current_editor();
+ if (se) {
+ se->enable_editor();
+ _update_selected_editor_menu();
+ }
}
}
@@ -2150,6 +2177,8 @@ bool ScriptEditor::edit(const RES &p_resource, int p_line, int p_col, bool p_gra
if ((script != nullptr && se->get_edited_resource() == p_resource) || se->get_edited_resource()->get_path() == p_resource->get_path()) {
if (should_open) {
+ se->enable_editor();
+
if (tab_container->get_current_tab() != i) {
_go_to_tab(i);
_update_script_names();
@@ -2180,6 +2209,8 @@ bool ScriptEditor::edit(const RES &p_resource, int p_line, int p_col, bool p_gra
}
ERR_FAIL_COND_V(!se, false);
+ se->set_edited_resource(p_resource);
+
if (p_resource->get_class_name() != StringName("VisualScript")) {
bool highlighter_set = false;
for (int i = 0; i < syntax_highlighters.size(); i++) {
@@ -2200,7 +2231,14 @@ bool ScriptEditor::edit(const RES &p_resource, int p_line, int p_col, bool p_gra
}
tab_container->add_child(se);
- se->set_edited_resource(p_resource);
+
+ if (p_grab_focus) {
+ se->enable_editor();
+ }
+
+ // If we delete a script within the filesystem, the original resource path
+ // is lost, so keep it as metadata to figure out the exact tab to delete.
+ se->set_meta("_edit_res_path", p_resource->get_path());
se->set_tooltip_request_func("_get_debug_tooltip", this);
if (se->get_edit_menu()) {
se->get_edit_menu()->hide();
@@ -2210,6 +2248,7 @@ bool ScriptEditor::edit(const RES &p_resource, int p_line, int p_col, bool p_gra
if (p_grab_focus) {
_go_to_tab(tab_container->get_tab_count() - 1);
+ _add_recent_script(p_resource->get_path());
}
_sort_list_on_update = true;
@@ -2234,7 +2273,6 @@ bool ScriptEditor::edit(const RES &p_resource, int p_line, int p_col, bool p_gra
}
notify_script_changed(p_resource);
- _add_recent_script(p_resource->get_path());
return true;
}
@@ -2375,6 +2413,23 @@ void ScriptEditor::_editor_settings_changed() {
ScriptServer::set_reload_scripts_on_save(EDITOR_DEF("text_editor/files/auto_reload_and_parse_scripts_on_save", true));
}
+void ScriptEditor::_filesystem_changed() {
+ _update_script_names();
+}
+
+void ScriptEditor::_file_removed(const String &p_removed_file) {
+ for (int i = 0; i < tab_container->get_child_count(); i++) {
+ ScriptEditorBase *se = Object::cast_to<ScriptEditorBase>(tab_container->get_child(i));
+ if (!se) {
+ continue;
+ }
+ if (se->get_meta("_edit_res_path") == p_removed_file) {
+ // The script is deleted with no undo, so just close the tab.
+ _close_tab(i, false, false);
+ }
+ }
+}
+
void ScriptEditor::_autosave_scripts() {
save_all_scripts();
}
@@ -2706,7 +2761,7 @@ void ScriptEditor::set_window_layout(Ref<ConfigFile> p_layout) {
if (!scr.is_valid()) {
continue;
}
- if (!edit(scr)) {
+ if (!edit(scr, false)) {
continue;
}
} else {
@@ -2715,7 +2770,7 @@ void ScriptEditor::set_window_layout(Ref<ConfigFile> p_layout) {
if (error != OK || !text_file.is_valid()) {
continue;
}
- if (!edit(text_file)) {
+ if (!edit(text_file, false)) {
continue;
}
}
diff --git a/editor/plugins/script_editor_plugin.h b/editor/plugins/script_editor_plugin.h
index 3891af4091..1234ebd267 100644
--- a/editor/plugins/script_editor_plugin.h
+++ b/editor/plugins/script_editor_plugin.h
@@ -134,6 +134,7 @@ public:
virtual RES get_edited_resource() const = 0;
virtual Vector<String> get_functions() = 0;
virtual void set_edited_resource(const RES &p_res) = 0;
+ virtual void enable_editor() = 0;
virtual void reload_text() = 0;
virtual String get_name() = 0;
virtual Ref<Texture2D> get_theme_icon() = 0;
@@ -370,6 +371,8 @@ class ScriptEditor : public PanelContainer {
void _save_layout();
void _editor_settings_changed();
+ void _filesystem_changed();
+ void _file_removed(const String &p_file);
void _autosave_scripts();
void _update_autosave_timer();
diff --git a/editor/plugins/script_text_editor.cpp b/editor/plugins/script_text_editor.cpp
index f4fdf8ccb0..1a88562c13 100644
--- a/editor/plugins/script_text_editor.cpp
+++ b/editor/plugins/script_text_editor.cpp
@@ -140,10 +140,10 @@ RES ScriptTextEditor::get_edited_resource() const {
}
void ScriptTextEditor::set_edited_resource(const RES &p_res) {
- ERR_FAIL_COND(!script.is_null());
+ ERR_FAIL_COND(script.is_valid());
+ ERR_FAIL_COND(p_res.is_null());
script = p_res;
- _set_theme_for_script();
code_editor->get_text_edit()->set_text(script->get_source_code());
code_editor->get_text_edit()->clear_undo_history();
@@ -151,6 +151,17 @@ void ScriptTextEditor::set_edited_resource(const RES &p_res) {
emit_signal("name_changed");
code_editor->update_line_and_column();
+}
+
+void ScriptTextEditor::enable_editor() {
+ if (editor_enabled) {
+ return;
+ }
+
+ editor_enabled = true;
+
+ _enable_code_editor();
+ _set_theme_for_script();
_validate_script();
}
@@ -301,14 +312,6 @@ void ScriptTextEditor::reload_text() {
code_editor->update_line_and_column();
}
-void ScriptTextEditor::_notification(int p_what) {
- switch (p_what) {
- case NOTIFICATION_READY:
- _load_theme_settings();
- break;
- }
-}
-
void ScriptTextEditor::add_callback(const String &p_function, PackedStringArray p_args) {
String code = code_editor->get_text_edit()->get_text();
int pos = script->get_language()->find_function(p_function, code);
@@ -335,7 +338,10 @@ void ScriptTextEditor::update_settings() {
}
bool ScriptTextEditor::is_unsaved() {
- return code_editor->get_text_edit()->get_version() != code_editor->get_text_edit()->get_saved_version();
+ const bool unsaved =
+ code_editor->get_text_edit()->get_version() != code_editor->get_text_edit()->get_saved_version() ||
+ script->get_path().empty(); // In memory.
+ return unsaved;
}
Variant ScriptTextEditor::get_edit_state() {
@@ -352,6 +358,10 @@ void ScriptTextEditor::set_edit_state(const Variant &p_state) {
_change_syntax_highlighter(idx);
}
}
+
+ if (editor_enabled) {
+ ensure_focus();
+ }
}
void ScriptTextEditor::_convert_case(CodeTextEditor::CaseStyle p_case) {
@@ -408,6 +418,9 @@ String ScriptTextEditor::get_name() {
if (script->get_path().find("local://") == -1 && script->get_path().find("::") == -1) {
name = script->get_path().get_file();
if (is_unsaved()) {
+ if (script->get_path().empty()) {
+ name = TTR("[unsaved]");
+ }
name += "(*)";
}
} else if (script->get_name() != "") {
@@ -1274,23 +1287,28 @@ void ScriptTextEditor::_edit_option_toggle_inline_comment() {
}
void ScriptTextEditor::add_syntax_highlighter(Ref<EditorSyntaxHighlighter> p_highlighter) {
+ ERR_FAIL_COND(p_highlighter.is_null());
+
highlighters[p_highlighter->_get_name()] = p_highlighter;
highlighter_menu->add_radio_check_item(p_highlighter->_get_name());
}
void ScriptTextEditor::set_syntax_highlighter(Ref<EditorSyntaxHighlighter> p_highlighter) {
+ ERR_FAIL_COND(p_highlighter.is_null());
+
+ Map<String, Ref<EditorSyntaxHighlighter>>::Element *el = highlighters.front();
+ while (el != nullptr) {
+ int highlighter_index = highlighter_menu->get_item_idx_from_text(el->key());
+ highlighter_menu->set_item_checked(highlighter_index, el->value() == p_highlighter);
+ el = el->next();
+ }
+
TextEdit *te = code_editor->get_text_edit();
p_highlighter->_set_edited_resource(script);
te->set_syntax_highlighter(p_highlighter);
- highlighter_menu->set_item_checked(highlighter_menu->get_item_idx_from_text(p_highlighter->_get_name()), true);
}
void ScriptTextEditor::_change_syntax_highlighter(int p_idx) {
- Map<String, Ref<EditorSyntaxHighlighter>>::Element *el = highlighters.front();
- while (el != nullptr) {
- highlighter_menu->set_item_checked(highlighter_menu->get_item_idx_from_text(el->key()), false);
- el = el->next();
- }
set_syntax_highlighter(highlighters[highlighter_menu->get_item_text(p_idx)]);
}
@@ -1606,64 +1624,40 @@ void ScriptTextEditor::_make_context_menu(bool p_selection, bool p_color, bool p
context_menu->popup();
}
-ScriptTextEditor::ScriptTextEditor() {
- theme_loaded = false;
- script_is_valid = false;
+void ScriptTextEditor::_enable_code_editor() {
+ ERR_FAIL_COND(code_editor->get_parent());
VSplitContainer *editor_box = memnew(VSplitContainer);
add_child(editor_box);
editor_box->set_anchors_and_margins_preset(Control::PRESET_WIDE);
editor_box->set_v_size_flags(SIZE_EXPAND_FILL);
- code_editor = memnew(CodeTextEditor);
editor_box->add_child(code_editor);
- code_editor->add_theme_constant_override("separation", 2);
- code_editor->set_anchors_and_margins_preset(Control::PRESET_WIDE);
+ code_editor->connect("show_warnings_panel", callable_mp(this, &ScriptTextEditor::_show_warnings_panel));
code_editor->connect("validate_script", callable_mp(this, &ScriptTextEditor::_validate_script));
code_editor->connect("load_theme_settings", callable_mp(this, &ScriptTextEditor::_load_theme_settings));
- code_editor->set_code_complete_func(_code_complete_scripts, this);
code_editor->get_text_edit()->connect("breakpoint_toggled", callable_mp(this, &ScriptTextEditor::_breakpoint_toggled));
code_editor->get_text_edit()->connect("symbol_lookup", callable_mp(this, &ScriptTextEditor::_lookup_symbol));
code_editor->get_text_edit()->connect("symbol_validate", callable_mp(this, &ScriptTextEditor::_validate_symbol));
- code_editor->get_text_edit()->connect("info_clicked", callable_mp(this, &ScriptTextEditor::_lookup_connections));
- code_editor->set_v_size_flags(SIZE_EXPAND_FILL);
+ code_editor->get_text_edit()->connect("gui_input", callable_mp(this, &ScriptTextEditor::_text_edit_gui_input));
code_editor->show_toggle_scripts_button();
- warnings_panel = memnew(RichTextLabel);
editor_box->add_child(warnings_panel);
warnings_panel->add_theme_font_override(
"normal_font", EditorNode::get_singleton()->get_gui_base()->get_theme_font("main", "EditorFonts"));
- warnings_panel->set_custom_minimum_size(Size2(0, 100 * EDSCALE));
- warnings_panel->set_h_size_flags(SIZE_EXPAND_FILL);
- warnings_panel->set_meta_underline(true);
- warnings_panel->set_selection_enabled(true);
- warnings_panel->set_focus_mode(FOCUS_CLICK);
- warnings_panel->hide();
-
- code_editor->connect("show_warnings_panel", callable_mp(this, &ScriptTextEditor::_show_warnings_panel));
warnings_panel->connect("meta_clicked", callable_mp(this, &ScriptTextEditor::_warning_clicked));
- update_settings();
-
- code_editor->get_text_edit()->set_callhint_settings(
- EditorSettings::get_singleton()->get("text_editor/completion/put_callhint_tooltip_below_current_line"),
- EditorSettings::get_singleton()->get("text_editor/completion/callhint_tooltip_offset"));
-
- code_editor->get_text_edit()->set_select_identifiers_on_hover(true);
- code_editor->get_text_edit()->set_context_menu_enabled(false);
- code_editor->get_text_edit()->connect("gui_input", callable_mp(this, &ScriptTextEditor::_text_edit_gui_input));
-
- context_menu = memnew(PopupMenu);
add_child(context_menu);
context_menu->connect("id_pressed", callable_mp(this, &ScriptTextEditor::_edit_option));
- color_panel = memnew(PopupPanel);
add_child(color_panel);
+
color_picker = memnew(ColorPicker);
color_picker->set_deferred_mode(true);
- color_panel->add_child(color_picker);
color_picker->connect("color_changed", callable_mp(this, &ScriptTextEditor::_color_changed));
+ color_panel->add_child(color_picker);
+
// get default color picker mode from editor settings
int default_color_mode = EDITOR_GET("interface/inspector/default_color_picker_mode");
if (default_color_mode == 1) {
@@ -1672,12 +1666,27 @@ ScriptTextEditor::ScriptTextEditor() {
color_picker->set_raw_mode(true);
}
- edit_hb = memnew(HBoxContainer);
+ quick_open = memnew(ScriptEditorQuickOpen);
+ quick_open->connect("goto_line", callable_mp(this, &ScriptTextEditor::_goto_line));
+ add_child(quick_open);
- edit_menu = memnew(MenuButton);
- edit_menu->set_text(TTR("Edit"));
- edit_menu->set_switch_on_hover(true);
+ goto_line_dialog = memnew(GotoLineDialog);
+ add_child(goto_line_dialog);
+
+ add_child(connection_info_dialog);
+ edit_hb->add_child(search_menu);
+ search_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/find"), SEARCH_FIND);
+ search_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/find_next"), SEARCH_FIND_NEXT);
+ search_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/find_previous"), SEARCH_FIND_PREV);
+ search_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/replace"), SEARCH_REPLACE);
+ search_menu->get_popup()->add_separator();
+ search_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/find_in_files"), SEARCH_IN_FILES);
+ search_menu->get_popup()->add_separator();
+ search_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/contextual_help"), HELP_CONTEXTUAL);
+ search_menu->get_popup()->connect("id_pressed", callable_mp(this, &ScriptTextEditor::_edit_option));
+
+ edit_hb->add_child(edit_menu);
edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/undo"), EDIT_UNDO);
edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/redo"), EDIT_REDO);
edit_menu->get_popup()->add_separator();
@@ -1707,8 +1716,6 @@ ScriptTextEditor::ScriptTextEditor() {
edit_menu->get_popup()->connect("id_pressed", callable_mp(this, &ScriptTextEditor::_edit_option));
edit_menu->get_popup()->add_separator();
- PopupMenu *convert_case = memnew(PopupMenu);
- convert_case->set_name("convert_case");
edit_menu->get_popup()->add_child(convert_case);
edit_menu->get_popup()->add_submenu_item(TTR("Convert Case"), "convert_case");
convert_case->add_shortcut(ED_SHORTCUT("script_text_editor/convert_to_uppercase", TTR("Uppercase"), KEY_MASK_SHIFT | KEY_F4), EDIT_TO_UPPERCASE);
@@ -1716,12 +1723,73 @@ ScriptTextEditor::ScriptTextEditor() {
convert_case->add_shortcut(ED_SHORTCUT("script_text_editor/capitalize", TTR("Capitalize"), KEY_MASK_SHIFT | KEY_F6), EDIT_CAPITALIZE);
convert_case->connect("id_pressed", callable_mp(this, &ScriptTextEditor::_edit_option));
- highlighter_menu = memnew(PopupMenu);
- highlighter_menu->set_name("highlighter_menu");
edit_menu->get_popup()->add_child(highlighter_menu);
edit_menu->get_popup()->add_submenu_item(TTR("Syntax Highlighter"), "highlighter_menu");
highlighter_menu->connect("id_pressed", callable_mp(this, &ScriptTextEditor::_change_syntax_highlighter));
+ _load_theme_settings();
+
+ search_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/replace_in_files"), REPLACE_IN_FILES);
+ edit_hb->add_child(goto_menu);
+ goto_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/goto_function"), SEARCH_LOCATE_FUNCTION);
+ goto_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/goto_line"), SEARCH_GOTO_LINE);
+ goto_menu->get_popup()->add_separator();
+
+ goto_menu->get_popup()->add_child(bookmarks_menu);
+ goto_menu->get_popup()->add_submenu_item(TTR("Bookmarks"), "Bookmarks");
+ _update_bookmark_list();
+ bookmarks_menu->connect("about_to_popup", callable_mp(this, &ScriptTextEditor::_update_bookmark_list));
+ bookmarks_menu->connect("index_pressed", callable_mp(this, &ScriptTextEditor::_bookmark_item_pressed));
+
+ goto_menu->get_popup()->add_child(breakpoints_menu);
+ goto_menu->get_popup()->add_submenu_item(TTR("Breakpoints"), "Breakpoints");
+ _update_breakpoint_list();
+ breakpoints_menu->connect("about_to_popup", callable_mp(this, &ScriptTextEditor::_update_breakpoint_list));
+ breakpoints_menu->connect("index_pressed", callable_mp(this, &ScriptTextEditor::_breakpoint_item_pressed));
+
+ goto_menu->get_popup()->connect("id_pressed", callable_mp(this, &ScriptTextEditor::_edit_option));
+}
+
+ScriptTextEditor::ScriptTextEditor() {
+ code_editor = memnew(CodeTextEditor);
+ code_editor->add_theme_constant_override("separation", 2);
+ code_editor->set_anchors_and_margins_preset(Control::PRESET_WIDE);
+ code_editor->set_code_complete_func(_code_complete_scripts, this);
+ code_editor->set_v_size_flags(SIZE_EXPAND_FILL);
+
+ warnings_panel = memnew(RichTextLabel);
+ warnings_panel->set_custom_minimum_size(Size2(0, 100 * EDSCALE));
+ warnings_panel->set_h_size_flags(SIZE_EXPAND_FILL);
+ warnings_panel->set_meta_underline(true);
+ warnings_panel->set_selection_enabled(true);
+ warnings_panel->set_focus_mode(FOCUS_CLICK);
+ warnings_panel->hide();
+
+ update_settings();
+
+ code_editor->get_text_edit()->set_callhint_settings(
+ EditorSettings::get_singleton()->get("text_editor/completion/put_callhint_tooltip_below_current_line"),
+ EditorSettings::get_singleton()->get("text_editor/completion/callhint_tooltip_offset"));
+
+ code_editor->get_text_edit()->set_select_identifiers_on_hover(true);
+ code_editor->get_text_edit()->set_context_menu_enabled(false);
+
+ context_menu = memnew(PopupMenu);
+
+ color_panel = memnew(PopupPanel);
+
+ edit_hb = memnew(HBoxContainer);
+
+ edit_menu = memnew(MenuButton);
+ edit_menu->set_text(TTR("Edit"));
+ edit_menu->set_switch_on_hover(true);
+
+ convert_case = memnew(PopupMenu);
+ convert_case->set_name("convert_case");
+
+ highlighter_menu = memnew(PopupMenu);
+ highlighter_menu->set_name("highlighter_menu");
+
Ref<EditorPlainTextSyntaxHighlighter> plain_highlighter;
plain_highlighter.instance();
add_syntax_highlighter(plain_highlighter);
@@ -1732,64 +1800,42 @@ ScriptTextEditor::ScriptTextEditor() {
set_syntax_highlighter(highlighter);
search_menu = memnew(MenuButton);
- edit_hb->add_child(search_menu);
search_menu->set_text(TTR("Search"));
search_menu->set_switch_on_hover(true);
- search_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/find"), SEARCH_FIND);
- search_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/find_next"), SEARCH_FIND_NEXT);
- search_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/find_previous"), SEARCH_FIND_PREV);
- search_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/replace"), SEARCH_REPLACE);
- search_menu->get_popup()->add_separator();
- search_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/find_in_files"), SEARCH_IN_FILES);
- search_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/replace_in_files"), REPLACE_IN_FILES);
- search_menu->get_popup()->add_separator();
- search_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/contextual_help"), HELP_CONTEXTUAL);
- search_menu->get_popup()->connect("id_pressed", callable_mp(this, &ScriptTextEditor::_edit_option));
-
- edit_hb->add_child(edit_menu);
-
- MenuButton *goto_menu = memnew(MenuButton);
- edit_hb->add_child(goto_menu);
+ goto_menu = memnew(MenuButton);
goto_menu->set_text(TTR("Go To"));
goto_menu->set_switch_on_hover(true);
- goto_menu->get_popup()->connect("id_pressed", callable_mp(this, &ScriptTextEditor::_edit_option));
-
- goto_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/goto_function"), SEARCH_LOCATE_FUNCTION);
- goto_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/goto_line"), SEARCH_GOTO_LINE);
- goto_menu->get_popup()->add_separator();
bookmarks_menu = memnew(PopupMenu);
bookmarks_menu->set_name("Bookmarks");
- goto_menu->get_popup()->add_child(bookmarks_menu);
- goto_menu->get_popup()->add_submenu_item(TTR("Bookmarks"), "Bookmarks");
- _update_bookmark_list();
- bookmarks_menu->connect("about_to_popup", callable_mp(this, &ScriptTextEditor::_update_bookmark_list));
- bookmarks_menu->connect("index_pressed", callable_mp(this, &ScriptTextEditor::_bookmark_item_pressed));
breakpoints_menu = memnew(PopupMenu);
breakpoints_menu->set_name("Breakpoints");
- goto_menu->get_popup()->add_child(breakpoints_menu);
- goto_menu->get_popup()->add_submenu_item(TTR("Breakpoints"), "Breakpoints");
- _update_breakpoint_list();
- breakpoints_menu->connect("about_to_popup", callable_mp(this, &ScriptTextEditor::_update_breakpoint_list));
- breakpoints_menu->connect("index_pressed", callable_mp(this, &ScriptTextEditor::_breakpoint_item_pressed));
-
- quick_open = memnew(ScriptEditorQuickOpen);
- add_child(quick_open);
- quick_open->connect("goto_line", callable_mp(this, &ScriptTextEditor::_goto_line));
-
- goto_line_dialog = memnew(GotoLineDialog);
- add_child(goto_line_dialog);
connection_info_dialog = memnew(ConnectionInfoDialog);
- add_child(connection_info_dialog);
code_editor->get_text_edit()->set_drag_forwarding(this);
}
ScriptTextEditor::~ScriptTextEditor() {
highlighters.clear();
+
+ if (!editor_enabled) {
+ memdelete(code_editor);
+ memdelete(warnings_panel);
+ memdelete(context_menu);
+ memdelete(color_panel);
+ memdelete(edit_hb);
+ memdelete(edit_menu);
+ memdelete(convert_case);
+ memdelete(highlighter_menu);
+ memdelete(search_menu);
+ memdelete(goto_menu);
+ memdelete(bookmarks_menu);
+ memdelete(breakpoints_menu);
+ memdelete(connection_info_dialog);
+ }
}
static ScriptEditorBase *create_editor(const RES &p_resource) {
diff --git a/editor/plugins/script_text_editor.h b/editor/plugins/script_text_editor.h
index 6d7f84d746..e931c9fdc6 100644
--- a/editor/plugins/script_text_editor.h
+++ b/editor/plugins/script_text_editor.h
@@ -39,8 +39,8 @@
class ConnectionInfoDialog : public AcceptDialog {
GDCLASS(ConnectionInfoDialog, AcceptDialog);
- Label *method;
- Tree *tree;
+ Label *method = nullptr;
+ Tree *tree = nullptr;
virtual void ok_pressed() override;
@@ -53,11 +53,12 @@ public:
class ScriptTextEditor : public ScriptEditorBase {
GDCLASS(ScriptTextEditor, ScriptEditorBase);
- CodeTextEditor *code_editor;
- RichTextLabel *warnings_panel;
+ CodeTextEditor *code_editor = nullptr;
+ RichTextLabel *warnings_panel = nullptr;
Ref<Script> script;
- bool script_is_valid;
+ bool script_is_valid = false;
+ bool editor_enabled = false;
Vector<String> functions;
@@ -65,25 +66,27 @@ class ScriptTextEditor : public ScriptEditorBase {
Vector<String> member_keywords;
- HBoxContainer *edit_hb;
+ HBoxContainer *edit_hb = nullptr;
- MenuButton *edit_menu;
- MenuButton *search_menu;
- PopupMenu *bookmarks_menu;
- PopupMenu *breakpoints_menu;
- PopupMenu *highlighter_menu;
- PopupMenu *context_menu;
+ MenuButton *edit_menu = nullptr;
+ MenuButton *search_menu = nullptr;
+ MenuButton *goto_menu = nullptr;
+ PopupMenu *bookmarks_menu = nullptr;
+ PopupMenu *breakpoints_menu = nullptr;
+ PopupMenu *highlighter_menu = nullptr;
+ PopupMenu *context_menu = nullptr;
+ PopupMenu *convert_case = nullptr;
- GotoLineDialog *goto_line_dialog;
- ScriptEditorQuickOpen *quick_open;
- ConnectionInfoDialog *connection_info_dialog;
+ GotoLineDialog *goto_line_dialog = nullptr;
+ ScriptEditorQuickOpen *quick_open = nullptr;
+ ConnectionInfoDialog *connection_info_dialog = nullptr;
- PopupPanel *color_panel;
- ColorPicker *color_picker;
+ PopupPanel *color_panel = nullptr;
+ ColorPicker *color_picker = nullptr;
Vector2 color_position;
String color_args;
- bool theme_loaded;
+ bool theme_loaded = false;
enum {
EDIT_UNDO,
@@ -132,6 +135,8 @@ class ScriptTextEditor : public ScriptEditorBase {
LOOKUP_SYMBOL,
};
+ void _enable_code_editor();
+
protected:
void _update_breakpoint_list();
void _breakpoint_item_pressed(int p_idx);
@@ -149,7 +154,6 @@ protected:
void _show_warnings_panel(bool p_show);
void _warning_clicked(Variant p_line);
- void _notification(int p_what);
static void _bind_methods();
Map<String, Ref<EditorSyntaxHighlighter>> highlighters;
@@ -185,6 +189,7 @@ public:
virtual void apply_code() override;
virtual RES get_edited_resource() const override;
virtual void set_edited_resource(const RES &p_res) override;
+ virtual void enable_editor() override;
virtual Vector<String> get_functions() override;
virtual void reload_text() override;
virtual String get_name() override;
diff --git a/editor/plugins/shader_editor_plugin.cpp b/editor/plugins/shader_editor_plugin.cpp
index 60ba3802fb..dc2abe15ee 100644
--- a/editor/plugins/shader_editor_plugin.cpp
+++ b/editor/plugins/shader_editor_plugin.cpp
@@ -136,28 +136,39 @@ void ShaderTextEditor::_load_theme_settings() {
syntax_highlighter->set_function_color(EDITOR_GET("text_editor/highlighting/function_color"));
syntax_highlighter->set_member_variable_color(EDITOR_GET("text_editor/highlighting/member_variable_color"));
+ syntax_highlighter->clear_keyword_colors();
+
List<String> keywords;
ShaderLanguage::get_keyword_list(&keywords);
+ const Color keyword_color = EDITOR_GET("text_editor/highlighting/keyword_color");
+
+ for (List<String>::Element *E = keywords.front(); E; E = E->next()) {
+ syntax_highlighter->add_keyword_color(E->get(), keyword_color);
+ }
+ // Colorize built-ins like `COLOR` differently to make them easier
+ // to distinguish from keywords at a quick glance.
+
+ List<String> built_ins;
if (shader.is_valid()) {
for (const Map<StringName, ShaderLanguage::FunctionInfo>::Element *E = ShaderTypes::get_singleton()->get_functions(RenderingServer::ShaderMode(shader->get_mode())).front(); E; E = E->next()) {
for (const Map<StringName, ShaderLanguage::BuiltInInfo>::Element *F = E->get().built_ins.front(); F; F = F->next()) {
- keywords.push_back(F->key());
+ built_ins.push_back(F->key());
}
}
for (int i = 0; i < ShaderTypes::get_singleton()->get_modes(RenderingServer::ShaderMode(shader->get_mode())).size(); i++) {
- keywords.push_back(ShaderTypes::get_singleton()->get_modes(RenderingServer::ShaderMode(shader->get_mode()))[i]);
+ built_ins.push_back(ShaderTypes::get_singleton()->get_modes(RenderingServer::ShaderMode(shader->get_mode()))[i]);
}
}
- const Color keyword_color = EDITOR_GET("text_editor/highlighting/keyword_color");
- syntax_highlighter->clear_keyword_colors();
- for (List<String>::Element *E = keywords.front(); E; E = E->next()) {
- syntax_highlighter->add_keyword_color(E->get(), keyword_color);
+ const Color member_variable_color = EDITOR_GET("text_editor/highlighting/member_variable_color");
+
+ for (List<String>::Element *E = built_ins.front(); E; E = E->next()) {
+ syntax_highlighter->add_keyword_color(E->get(), member_variable_color);
}
- //colorize comments
+ // Colorize comments.
const Color comment_color = EDITOR_GET("text_editor/highlighting/comment_color");
syntax_highlighter->clear_color_regions();
syntax_highlighter->add_color_region("/*", "*/", comment_color, false);
diff --git a/editor/plugins/text_editor.cpp b/editor/plugins/text_editor.cpp
index d602d152fe..82e231e396 100644
--- a/editor/plugins/text_editor.cpp
+++ b/editor/plugins/text_editor.cpp
@@ -34,22 +34,27 @@
#include "editor/editor_node.h"
void TextEditor::add_syntax_highlighter(Ref<EditorSyntaxHighlighter> p_highlighter) {
+ ERR_FAIL_COND(p_highlighter.is_null());
+
highlighters[p_highlighter->_get_name()] = p_highlighter;
highlighter_menu->add_radio_check_item(p_highlighter->_get_name());
}
void TextEditor::set_syntax_highlighter(Ref<EditorSyntaxHighlighter> p_highlighter) {
- TextEdit *te = code_editor->get_text_edit();
- te->set_syntax_highlighter(p_highlighter);
- highlighter_menu->set_item_checked(highlighter_menu->get_item_idx_from_text(p_highlighter->_get_name()), true);
-}
+ ERR_FAIL_COND(p_highlighter.is_null());
-void TextEditor::_change_syntax_highlighter(int p_idx) {
Map<String, Ref<EditorSyntaxHighlighter>>::Element *el = highlighters.front();
while (el != nullptr) {
- highlighter_menu->set_item_checked(highlighter_menu->get_item_idx_from_text(el->key()), false);
+ int highlighter_index = highlighter_menu->get_item_idx_from_text(el->key());
+ highlighter_menu->set_item_checked(highlighter_index, el->value() == p_highlighter);
el = el->next();
}
+
+ TextEdit *te = code_editor->get_text_edit();
+ te->set_syntax_highlighter(p_highlighter);
+}
+
+void TextEditor::_change_syntax_highlighter(int p_idx) {
set_syntax_highlighter(highlighters[highlighter_menu->get_item_text(p_idx)]);
}
@@ -114,6 +119,9 @@ String TextEditor::get_name() {
if (text_file->get_path().find("local://") == -1 && text_file->get_path().find("::") == -1) {
name = text_file->get_path().get_file();
if (is_unsaved()) {
+ if (text_file->get_path().empty()) {
+ name = TTR("[unsaved]");
+ }
name += "(*)";
}
} else if (text_file->get_name() != "") {
@@ -126,7 +134,7 @@ String TextEditor::get_name() {
}
Ref<Texture2D> TextEditor::get_theme_icon() {
- return EditorNode::get_singleton()->get_object_icon(text_file.operator->(), "");
+ return EditorNode::get_singleton()->get_object_icon(text_file.ptr(), "");
}
RES TextEditor::get_edited_resource() const {
@@ -134,7 +142,8 @@ RES TextEditor::get_edited_resource() const {
}
void TextEditor::set_edited_resource(const RES &p_res) {
- ERR_FAIL_COND(!text_file.is_null());
+ ERR_FAIL_COND(text_file.is_valid());
+ ERR_FAIL_COND(p_res.is_null());
text_file = p_res;
@@ -146,6 +155,16 @@ void TextEditor::set_edited_resource(const RES &p_res) {
code_editor->update_line_and_column();
}
+void TextEditor::enable_editor() {
+ if (editor_enabled) {
+ return;
+ }
+
+ editor_enabled = true;
+
+ _load_theme_settings();
+}
+
void TextEditor::add_callback(const String &p_function, PackedStringArray p_args) {
}
@@ -220,7 +239,10 @@ void TextEditor::apply_code() {
}
bool TextEditor::is_unsaved() {
- return code_editor->get_text_edit()->get_version() != code_editor->get_text_edit()->get_saved_version();
+ const bool unsaved =
+ code_editor->get_text_edit()->get_version() != code_editor->get_text_edit()->get_saved_version() ||
+ text_file->get_path().empty(); // In memory.
+ return unsaved;
}
Variant TextEditor::get_edit_state() {
@@ -237,6 +259,8 @@ void TextEditor::set_edit_state(const Variant &p_state) {
_change_syntax_highlighter(idx);
}
}
+
+ ensure_focus();
}
void TextEditor::trim_trailing_whitespace() {
@@ -303,14 +327,6 @@ void TextEditor::clear_edit_menu() {
memdelete(edit_hb);
}
-void TextEditor::_notification(int p_what) {
- switch (p_what) {
- case NOTIFICATION_READY:
- _load_theme_settings();
- break;
- }
-}
-
void TextEditor::_edit_option(int p_op) {
TextEdit *tx = code_editor->get_text_edit();
diff --git a/editor/plugins/text_editor.h b/editor/plugins/text_editor.h
index 5299776b56..f3e9e599cf 100644
--- a/editor/plugins/text_editor.h
+++ b/editor/plugins/text_editor.h
@@ -37,18 +37,19 @@ class TextEditor : public ScriptEditorBase {
GDCLASS(TextEditor, ScriptEditorBase);
private:
- CodeTextEditor *code_editor;
+ CodeTextEditor *code_editor = nullptr;
Ref<TextFile> text_file;
+ bool editor_enabled = false;
- HBoxContainer *edit_hb;
- MenuButton *edit_menu;
- PopupMenu *highlighter_menu;
- MenuButton *search_menu;
- PopupMenu *bookmarks_menu;
- PopupMenu *context_menu;
+ HBoxContainer *edit_hb = nullptr;
+ MenuButton *edit_menu = nullptr;
+ PopupMenu *highlighter_menu = nullptr;
+ MenuButton *search_menu = nullptr;
+ PopupMenu *bookmarks_menu = nullptr;
+ PopupMenu *context_menu = nullptr;
- GotoLineDialog *goto_line_dialog;
+ GotoLineDialog *goto_line_dialog = nullptr;
enum {
EDIT_UNDO,
@@ -88,8 +89,6 @@ private:
protected:
static void _bind_methods();
- void _notification(int p_what);
-
void _edit_option(int p_op);
void _make_context_menu(bool p_selection, bool p_can_fold, bool p_is_folded, Vector2 p_position);
void _text_edit_gui_input(const Ref<InputEvent> &ev);
@@ -113,7 +112,7 @@ public:
virtual Ref<Texture2D> get_theme_icon() override;
virtual RES get_edited_resource() const override;
virtual void set_edited_resource(const RES &p_res) override;
- void set_edited_file(const Ref<TextFile> &p_file);
+ virtual void enable_editor() override;
virtual void reload_text() override;
virtual void apply_code() override;
virtual bool is_unsaved() override;
diff --git a/main/SCsub b/main/SCsub
index bf188d7328..97f77840f1 100644
--- a/main/SCsub
+++ b/main/SCsub
@@ -7,21 +7,23 @@ import main_builders
env.main_sources = []
-env.add_source_files(env.main_sources, "*.cpp")
+env_main = env.Clone()
-env.Depends("#main/splash.gen.h", "#main/splash.png")
-env.CommandNoCache("#main/splash.gen.h", "#main/splash.png", run_in_subprocess(main_builders.make_splash))
+env_main.add_source_files(env.main_sources, "*.cpp")
-env.Depends("#main/splash_editor.gen.h", "#main/splash_editor.png")
-env.CommandNoCache(
+if env["tests"]:
+ env_main.Append(CPPDEFINES=["TESTS_ENABLED"])
+
+env_main.Depends("#main/splash.gen.h", "#main/splash.png")
+env_main.CommandNoCache("#main/splash.gen.h", "#main/splash.png", run_in_subprocess(main_builders.make_splash))
+
+env_main.Depends("#main/splash_editor.gen.h", "#main/splash_editor.png")
+env_main.CommandNoCache(
"#main/splash_editor.gen.h", "#main/splash_editor.png", run_in_subprocess(main_builders.make_splash_editor)
)
-env.Depends("#main/app_icon.gen.h", "#main/app_icon.png")
-env.CommandNoCache("#main/app_icon.gen.h", "#main/app_icon.png", run_in_subprocess(main_builders.make_app_icon))
-
-if env["tools"]:
- SConscript("tests/SCsub")
+env_main.Depends("#main/app_icon.gen.h", "#main/app_icon.png")
+env_main.CommandNoCache("#main/app_icon.gen.h", "#main/app_icon.png", run_in_subprocess(main_builders.make_app_icon))
-lib = env.add_library("main", env.main_sources)
+lib = env_main.add_library("main", env.main_sources)
env.Prepend(LIBS=[lib])
diff --git a/main/main.cpp b/main/main.cpp
index 35aa99c720..6965e5415a 100644
--- a/main/main.cpp
+++ b/main/main.cpp
@@ -55,7 +55,6 @@
#include "main/performance.h"
#include "main/splash.gen.h"
#include "main/splash_editor.gen.h"
-#include "main/tests/test_main.h"
#include "modules/modules_enabled.gen.h"
#include "modules/register_module_types.h"
#include "platform/register_platform_apis.h"
@@ -75,6 +74,10 @@
#include "servers/rendering/rendering_server_wrap_mt.h"
#include "servers/xr_server.h"
+#ifdef TESTS_ENABLED
+#include "tests/test_main.h"
+#endif
+
#ifdef TOOLS_ENABLED
#include "editor/doc_data.h"
@@ -383,6 +386,7 @@ void Main::print_help(const char *p_binary) {
OS::get_singleton()->print(
" --gdnative-generate-json-api Generate JSON dump of the Godot API for GDNative bindings.\n");
#endif
+#ifdef TESTS_ENABLED
OS::get_singleton()->print(" --test <test> Run a unit test [");
const char **test_names = tests_get_names();
const char *comma = "";
@@ -393,10 +397,12 @@ void Main::print_help(const char *p_binary) {
}
OS::get_singleton()->print("].\n");
#endif
+ OS::get_singleton()->print("\n");
+#endif
}
int Main::test_entrypoint(int argc, char *argv[], bool &tests_need_run) {
-#ifdef TOOLS_ENABLED // templates can't run unit test tool
+#ifdef TESTS_ENABLED
for (int x = 0; x < argc; x++) {
if (strncmp(argv[x], "--test", 6) == 0) {
tests_need_run = true;
@@ -1191,7 +1197,7 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph
OS::get_singleton()->_vsync_via_compositor = window_vsync_via_compositor;
if (tablet_driver == "") { // specified in project.godot
- tablet_driver = GLOBAL_DEF_RST("display/window/tablet_driver", OS::get_singleton()->get_tablet_driver_name(0));
+ tablet_driver = GLOBAL_DEF_RST_NOVAL("display/window/tablet_driver", OS::get_singleton()->get_tablet_driver_name(0));
}
for (int i = 0; i < OS::get_singleton()->get_tablet_driver_count(); i++) {
@@ -1245,7 +1251,7 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph
}
if (audio_driver == "") { // specified in project.godot
- audio_driver = GLOBAL_DEF_RST("audio/driver", AudioDriverManager::get_driver(0)->get_name());
+ audio_driver = GLOBAL_DEF_RST_NOVAL("audio/driver", AudioDriverManager::get_driver(0)->get_name());
}
for (int i = 0; i < AudioDriverManager::get_driver_count(); i++) {
diff --git a/main/tests/SCsub b/main/tests/SCsub
deleted file mode 100644
index cb1d35b12f..0000000000
--- a/main/tests/SCsub
+++ /dev/null
@@ -1,9 +0,0 @@
-#!/usr/bin/python
-
-Import("env")
-
-env.tests_sources = []
-env.add_source_files(env.tests_sources, "*.cpp")
-
-lib = env.add_library("tests", env.tests_sources)
-env.Prepend(LIBS=[lib])
diff --git a/misc/dist/ios_xcode/godot_ios.xcodeproj/project.pbxproj b/misc/dist/ios_xcode/godot_ios.xcodeproj/project.pbxproj
index 54dda2563d..9a1143a21a 100644
--- a/misc/dist/ios_xcode/godot_ios.xcodeproj/project.pbxproj
+++ b/misc/dist/ios_xcode/godot_ios.xcodeproj/project.pbxproj
@@ -12,6 +12,8 @@
$modules_buildfile
1FF8DBB11FBA9DE1009DE660 /* dummy.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1FF8DBB01FBA9DE1009DE660 /* dummy.cpp */; };
D07CD44E1C5D589C00B7FB28 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = D07CD44D1C5D589C00B7FB28 /* Images.xcassets */; };
+ 9039D3BE24C093AC0020482C /* MoltenVK.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 9039D3BD24C093AC0020482C /* MoltenVK.a */; };
+ 9073252C24BF980B0063BCD4 /* vulkan in Resources */ = {isa = PBXBuildFile; fileRef = 905036DC24BF932E00301046 /* vulkan */; };
D0BCFE4618AEBDA2004A7AAE /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = D0BCFE4418AEBDA2004A7AAE /* InfoPlist.strings */; };
D0BCFE7818AEBFEB004A7AAE /* $binary.pck in Resources */ = {isa = PBXBuildFile; fileRef = D0BCFE7718AEBFEB004A7AAE /* $binary.pck */; };
$pbx_launch_screen_build_reference
@@ -37,6 +39,8 @@
$modules_fileref
1FF4C1881F584E6300A41E41 /* $binary.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = "$binary.entitlements"; sourceTree = "<group>"; };
1FF8DBB01FBA9DE1009DE660 /* dummy.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = dummy.cpp; sourceTree = "<group>"; };
+ 9039D3BD24C093AC0020482C /* MoltenVK.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = MoltenVK; path = MoltenVK.a; sourceTree = "<group>"; };
+ 905036DC24BF932E00301046 /* vulkan */ = {isa = PBXFileReference; lastKnownFileType = folder; name = vulkan; path = "$binary/vulkan"; sourceTree = "<group>"; };
D07CD44D1C5D589C00B7FB28 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = "<group>"; };
D0BCFE3418AEBDA2004A7AAE /* $binary.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "$binary.app"; sourceTree = BUILT_PRODUCTS_DIR; };
D0BCFE4318AEBDA2004A7AAE /* $binary-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "$binary-Info.plist"; sourceTree = "<group>"; };
@@ -52,6 +56,7 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
+ 9039D3BE24C093AC0020482C /* MoltenVK.a in Frameworks */,
DEADBEEF2F582BE20003B888 /* $binary.a */,
$modules_buildphase
$additional_pbx_frameworks_build
@@ -64,6 +69,7 @@
D0BCFE2B18AEBDA2004A7AAE = {
isa = PBXGroup;
children = (
+ 905036DC24BF932E00301046 /* vulkan */,
1F1575711F582BE20003B888 /* dylibs */,
D0BCFE7718AEBFEB004A7AAE /* $binary.pck */,
D0BCFE4118AEBDA2004A7AAE /* $binary */,
@@ -84,6 +90,7 @@
D0BCFE3618AEBDA2004A7AAE /* Frameworks */ = {
isa = PBXGroup;
children = (
+ 9039D3BD24C093AC0020482C /* MoltenVK.a */,
DEADBEEF1F582BE20003B888 /* $binary.a */,
$modules_buildgrp
$additional_pbx_frameworks_refs
@@ -248,6 +255,7 @@
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
+ 9073252C24BF980B0063BCD4 /* vulkan in Resources */,
D07CD44E1C5D589C00B7FB28 /* Images.xcassets in Resources */,
D0BCFE7818AEBFEB004A7AAE /* $binary.pck in Resources */,
$pbx_launch_screen_build_phase
@@ -319,7 +327,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
- IPHONEOS_DEPLOYMENT_TARGET = 9.0;
+ IPHONEOS_DEPLOYMENT_TARGET = 11.0;
OTHER_LDFLAGS = "$linker_flags";
SDKROOT = iphoneos;
TARGETED_DEVICE_FAMILY = "1,2";
@@ -358,7 +366,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
- IPHONEOS_DEPLOYMENT_TARGET = 9.0;
+ IPHONEOS_DEPLOYMENT_TARGET = 11.0;
OTHER_LDFLAGS = "$linker_flags";
SDKROOT = iphoneos;
TARGETED_DEVICE_FAMILY = "1,2";
diff --git a/misc/dist/ios_xcode/godot_ios/vulkan/icd.d/MoltenVK_icd.json b/misc/dist/ios_xcode/godot_ios/vulkan/icd.d/MoltenVK_icd.json
new file mode 100644
index 0000000000..7501cb548c
--- /dev/null
+++ b/misc/dist/ios_xcode/godot_ios/vulkan/icd.d/MoltenVK_icd.json
@@ -0,0 +1,7 @@
+{
+ "file_format_version" : "1.0.0",
+ "ICD": {
+ "library_path": "./../../Frameworks/MoltenVK.framework/MoltenVK",
+ "api_version" : "1.0.0"
+ }
+}
diff --git a/modules/SCsub b/modules/SCsub
index 9155a53eaf..2d774306e4 100644
--- a/modules/SCsub
+++ b/modules/SCsub
@@ -1,10 +1,10 @@
#!/usr/bin/env python
-Import("env")
-
import modules_builders
import os
+Import("env")
+
env_modules = env.Clone()
Export("env_modules")
@@ -12,6 +12,15 @@ Export("env_modules")
# Header with MODULE_*_ENABLED defines.
env.CommandNoCache("modules_enabled.gen.h", Value(env.module_list), modules_builders.generate_modules_enabled)
+# Header to be included in `tests/test_main.cpp` to run module-specific tests.
+if env["tests"]:
+ env.CommandNoCache(
+ "modules_tests.gen.h",
+ Value(env.module_list),
+ Action(modules_builders.generate_modules_tests, "Generating modules tests header."),
+ )
+ env.AlwaysBuild("modules_tests.gen.h")
+
vs_sources = []
# libmodule_<name>.a for each active module.
for name, path in env.module_list.items():
diff --git a/modules/arkit/arkit_interface.h b/modules/arkit/arkit_interface.h
index 1044f3cf6f..5a2c50e213 100644
--- a/modules/arkit/arkit_interface.h
+++ b/modules/arkit/arkit_interface.h
@@ -60,8 +60,8 @@ private:
float eye_height, z_near, z_far;
Ref<CameraFeed> feed;
- int image_width[2];
- int image_height[2];
+ size_t image_width[2];
+ size_t image_height[2];
Vector<uint8_t> img_data[2];
struct anchor_map {
@@ -84,9 +84,9 @@ public:
void start_session();
void stop_session();
- bool get_anchor_detection_is_enabled() const;
- void set_anchor_detection_is_enabled(bool p_enable);
- virtual int get_camera_feed_id();
+ bool get_anchor_detection_is_enabled() const override;
+ void set_anchor_detection_is_enabled(bool p_enable) override;
+ virtual int get_camera_feed_id() override;
bool get_light_estimation_is_enabled() const;
void set_light_estimation_is_enabled(bool p_enable);
@@ -97,22 +97,22 @@ public:
/* while Godot has its own raycast logic this takes ARKits camera into account and hits on any ARAnchor */
Array raycast(Vector2 p_screen_coord);
- void notification(int p_what);
+ virtual void notification(int p_what) override;
- virtual StringName get_name() const;
- virtual int get_capabilities() const;
+ virtual StringName get_name() const override;
+ virtual int get_capabilities() const override;
- virtual bool is_initialized() const;
- virtual bool initialize();
- virtual void uninitialize();
+ virtual bool is_initialized() const override;
+ virtual bool initialize() override;
+ virtual void uninitialize() override;
- virtual Size2 get_render_targetsize();
- virtual bool is_stereo();
- virtual Transform get_transform_for_eye(XRInterface::Eyes p_eye, const Transform &p_cam_transform);
- virtual CameraMatrix get_projection_for_eye(XRInterface::Eyes p_eye, real_t p_aspect, real_t p_z_near, real_t p_z_far);
- virtual void commit_for_eye(XRInterface::Eyes p_eye, RID p_render_target, const Rect2 &p_screen_rect);
+ virtual Size2 get_render_targetsize() override;
+ virtual bool is_stereo() override;
+ virtual Transform get_transform_for_eye(XRInterface::Eyes p_eye, const Transform &p_cam_transform) override;
+ virtual CameraMatrix get_projection_for_eye(XRInterface::Eyes p_eye, real_t p_aspect, real_t p_z_near, real_t p_z_far) override;
+ virtual void commit_for_eye(XRInterface::Eyes p_eye, RID p_render_target, const Rect2 &p_screen_rect) override;
- virtual void process();
+ virtual void process() override;
// called by delegate (void * because C++ and Obj-C don't always mix, should really change all platform/iphone/*.cpp files to .mm)
void _add_or_update_anchor(void *p_anchor);
diff --git a/modules/arkit/arkit_interface.mm b/modules/arkit/arkit_interface.mm
index 7de824815a..3fb2cc933d 100644
--- a/modules/arkit/arkit_interface.mm
+++ b/modules/arkit/arkit_interface.mm
@@ -42,7 +42,9 @@
#include "arkit_session_delegate.h"
// just a dirty workaround for now, declare these as globals. I'll probably encapsulate ARSession and associated logic into an mm object and change ARKitInterface to a normal cpp object that consumes it.
+API_AVAILABLE(ios(11.0))
ARSession *ar_session;
+
ARKitSessionDelegate *ar_delegate;
NSTimeInterval last_timestamp;
@@ -55,22 +57,28 @@ void ARKitInterface::start_session() {
if (initialized) {
print_line("Starting ARKit session");
- Class ARWorldTrackingConfigurationClass = NSClassFromString(@"ARWorldTrackingConfiguration");
- ARWorldTrackingConfiguration *configuration = [ARWorldTrackingConfigurationClass new];
+ if (@available(iOS 11, *)) {
+ Class ARWorldTrackingConfigurationClass = NSClassFromString(@"ARWorldTrackingConfiguration");
+ ARWorldTrackingConfiguration *configuration = [ARWorldTrackingConfigurationClass new];
- configuration.lightEstimationEnabled = light_estimation_is_enabled;
- if (plane_detection_is_enabled) {
- configuration.planeDetection = ARPlaneDetectionVertical | ARPlaneDetectionHorizontal;
- } else {
- configuration.planeDetection = 0;
- }
+ configuration.lightEstimationEnabled = light_estimation_is_enabled;
+ if (plane_detection_is_enabled) {
+ if (@available(iOS 11.3, *)) {
+ configuration.planeDetection = ARPlaneDetectionVertical | ARPlaneDetectionHorizontal;
+ } else {
+ configuration.planeDetection = ARPlaneDetectionHorizontal;
+ }
+ } else {
+ configuration.planeDetection = 0;
+ }
- // make sure our camera is on
- if (feed.is_valid()) {
- feed->set_active(true);
- }
+ // make sure our camera is on
+ if (feed.is_valid()) {
+ feed->set_active(true);
+ }
- [ar_session runWithConfiguration:configuration];
+ [ar_session runWithConfiguration:configuration];
+ }
}
}
@@ -84,7 +92,9 @@ void ARKitInterface::stop_session() {
feed->set_active(false);
}
- [ar_session pause];
+ if (@available(iOS 11.0, *)) {
+ [ar_session pause];
+ }
}
}
@@ -92,12 +102,12 @@ void ARKitInterface::notification(int p_what) {
// TODO, this is not being called, need to find out why, possibly because this is not a node.
// in that case we need to find a way to get these notifications!
switch (p_what) {
- case MainLoop::NOTIFICATION_WM_FOCUS_IN: {
+ case DisplayServer::WINDOW_EVENT_FOCUS_IN: {
print_line("Focus in");
start_session();
}; break;
- case MainLoop::NOTIFICATION_WM_FOCUS_OUT: {
+ case DisplayServer::WINDOW_EVENT_FOCUS_OUT: {
print_line("Focus out");
stop_session();
@@ -162,37 +172,42 @@ int ARKitInterface::get_capabilities() const {
}
Array ARKitInterface::raycast(Vector2 p_screen_coord) {
- Array arr;
- Size2 screen_size = OS::get_singleton()->get_window_size();
- CGPoint point;
- point.x = p_screen_coord.x / screen_size.x;
- point.y = p_screen_coord.y / screen_size.y;
-
- ///@TODO maybe give more options here, for now we're taking just ARAchors into account that were found during plane detection keeping their size into account
- NSArray<ARHitTestResult *> *results = [ar_session.currentFrame hittest:point types:ARHitTestResultTypeExistingPlaneUsingExtent];
-
- for (ARHitTestResult *result in results) {
- Transform transform;
-
- matrix_float4x4 m44 = result.worldTransform;
- transform.basis.elements[0].x = m44.columns[0][0];
- transform.basis.elements[1].x = m44.columns[0][1];
- transform.basis.elements[2].x = m44.columns[0][2];
- transform.basis.elements[0].y = m44.columns[1][0];
- transform.basis.elements[1].y = m44.columns[1][1];
- transform.basis.elements[2].y = m44.columns[1][2];
- transform.basis.elements[0].z = m44.columns[2][0];
- transform.basis.elements[1].z = m44.columns[2][1];
- transform.basis.elements[2].z = m44.columns[2][2];
- transform.origin.x = m44.columns[3][0];
- transform.origin.y = m44.columns[3][1];
- transform.origin.z = m44.columns[3][2];
-
- /* important, NOT scaled to world_scale !! */
- arr.push_back(transform);
- }
+ if (@available(iOS 11, *)) {
+ Array arr;
+ Size2 screen_size = DisplayServer::get_singleton()->screen_get_size();
+ CGPoint point;
+ point.x = p_screen_coord.x / screen_size.x;
+ point.y = p_screen_coord.y / screen_size.y;
+
+ ///@TODO maybe give more options here, for now we're taking just ARAchors into account that were found during plane detection keeping their size into account
+
+ NSArray<ARHitTestResult *> *results = [ar_session.currentFrame hitTest:point types:ARHitTestResultTypeExistingPlaneUsingExtent];
+
+ for (ARHitTestResult *result in results) {
+ Transform transform;
+
+ matrix_float4x4 m44 = result.worldTransform;
+ transform.basis.elements[0].x = m44.columns[0][0];
+ transform.basis.elements[1].x = m44.columns[0][1];
+ transform.basis.elements[2].x = m44.columns[0][2];
+ transform.basis.elements[0].y = m44.columns[1][0];
+ transform.basis.elements[1].y = m44.columns[1][1];
+ transform.basis.elements[2].y = m44.columns[1][2];
+ transform.basis.elements[0].z = m44.columns[2][0];
+ transform.basis.elements[1].z = m44.columns[2][1];
+ transform.basis.elements[2].z = m44.columns[2][2];
+ transform.origin.x = m44.columns[3][0];
+ transform.origin.y = m44.columns[3][1];
+ transform.origin.z = m44.columns[3][2];
+
+ /* important, NOT scaled to world_scale !! */
+ arr.push_back(transform);
+ }
- return arr;
+ return arr;
+ } else {
+ return Array();
+ }
}
void ARKitInterface::_bind_methods() {
@@ -221,51 +236,55 @@ bool ARKitInterface::initialize() {
XRServer *xr_server = XRServer::get_singleton();
ERR_FAIL_NULL_V(xr_server, false);
- if (!initialized) {
- print_line("initializing ARKit");
+ if (@available(iOS 11, *)) {
+ if (!initialized) {
+ print_line("initializing ARKit");
- // create our ar session and delegate
- Class ARSessionClass = NSClassFromString(@"ARSession");
- if (ARSessionClass == Nil) {
- void *arkit_handle = dlopen("/System/Library/Frameworks/ARKit.framework/ARKit", RTLD_NOW);
- if (arkit_handle) {
- ARSessionClass = NSClassFromString(@"ARSession");
- } else {
- print_line("ARKit init failed");
- return false;
+ // create our ar session and delegate
+ Class ARSessionClass = NSClassFromString(@"ARSession");
+ if (ARSessionClass == Nil) {
+ void *arkit_handle = dlopen("/System/Library/Frameworks/ARKit.framework/ARKit", RTLD_NOW);
+ if (arkit_handle) {
+ ARSessionClass = NSClassFromString(@"ARSession");
+ } else {
+ print_line("ARKit init failed");
+ return false;
+ }
}
- }
- ar_session = [ARSessionClass new];
- ar_delegate = [ARKitSessionDelegate new];
- ar_delegate.arkit_interface = this;
- ar_session.delegate = ar_delegate;
+ ar_session = [ARSessionClass new];
+ ar_delegate = [ARKitSessionDelegate new];
+ ar_delegate.arkit_interface = this;
+ ar_session.delegate = ar_delegate;
- // reset our transform
- transform = Transform();
+ // reset our transform
+ transform = Transform();
- // make this our primary interface
- xr_server->set_primary_interface(this);
+ // make this our primary interface
+ xr_server->set_primary_interface(this);
- // make sure we have our feed setup
- if (feed.is_null()) {
- feed.instance();
- feed->set_name("ARKit");
+ // make sure we have our feed setup
+ if (feed.is_null()) {
+ feed.instance();
+ feed->set_name("ARKit");
- CameraServer *cs = CameraServer::get_singleton();
- if (cs != NULL) {
- cs->add_feed(feed);
+ CameraServer *cs = CameraServer::get_singleton();
+ if (cs != NULL) {
+ cs->add_feed(feed);
+ }
}
- }
- feed->set_active(true);
+ feed->set_active(true);
- // yeah!
- initialized = true;
+ // yeah!
+ initialized = true;
- // Start our session...
- start_session();
- }
+ // Start our session...
+ start_session();
+ }
- return true;
+ return true;
+ } else {
+ return false;
+ }
}
void ARKitInterface::uninitialize() {
@@ -286,9 +305,12 @@ void ARKitInterface::uninitialize() {
remove_all_anchors();
- [ar_session release];
+ if (@available(iOS 11.0, *)) {
+ [ar_session release];
+ ar_session = NULL;
+ }
[ar_delegate release];
- ar_session = NULL;
+
ar_delegate = NULL;
initialized = false;
session_was_started = false;
@@ -296,15 +318,15 @@ void ARKitInterface::uninitialize() {
}
Size2 ARKitInterface::get_render_targetsize() {
- _THREAD_SAFE_METHOD_
+ // _THREAD_SAFE_METHOD_
- Size2 target_size = OS::get_singleton()->get_window_size();
+ Size2 target_size = DisplayServer::get_singleton()->screen_get_size();
return target_size;
}
Transform ARKitInterface::get_transform_for_eye(XRInterface::Eyes p_eye, const Transform &p_cam_transform) {
- _THREAD_SAFE_METHOD_
+ // _THREAD_SAFE_METHOD_
Transform transform_for_eye;
@@ -336,7 +358,7 @@ CameraMatrix ARKitInterface::get_projection_for_eye(XRInterface::Eyes p_eye, rea
}
void ARKitInterface::commit_for_eye(XRInterface::Eyes p_eye, RID p_render_target, const Rect2 &p_screen_rect) {
- _THREAD_SAFE_METHOD_
+ // _THREAD_SAFE_METHOD_
// We must have a valid render target
ERR_FAIL_COND(!p_render_target.is_valid());
@@ -345,15 +367,15 @@ void ARKitInterface::commit_for_eye(XRInterface::Eyes p_eye, RID p_render_target
ERR_FAIL_COND(p_screen_rect == Rect2());
// get the size of our screen
- Rect2 screen_rect = p_screen_rect;
+ // Rect2 screen_rect = p_screen_rect;
// screen_rect.position.x += screen_rect.size.x;
// screen_rect.size.x = -screen_rect.size.x;
// screen_rect.position.y += screen_rect.size.y;
// screen_rect.size.y = -screen_rect.size.y;
- VSG::rasterizer->set_current_render_target(RID());
- VSG::rasterizer->blit_render_target_to_screen(p_render_target, screen_rect, 0);
+ // VSG::rasterizer->set_current_render_target(RID());
+ // VSG::rasterizer->blit_render_target_to_screen(p_render_target, screen_rect, 0);
}
XRPositionalTracker *ARKitInterface::get_anchor_for_uuid(const unsigned char *p_uuid) {
@@ -432,7 +454,7 @@ void ARKitInterface::remove_all_anchors() {
}
void ARKitInterface::process() {
- _THREAD_SAFE_METHOD_
+ // _THREAD_SAFE_METHOD_
if (@available(iOS 11.0, *)) {
if (initialized) {
@@ -443,8 +465,16 @@ void ARKitInterface::process() {
last_timestamp = current_frame.timestamp;
// get some info about our screen and orientation
- Size2 screen_size = OS::get_singleton()->get_window_size();
- UIInterfaceOrientation orientation = [[UIApplication sharedApplication] statusBarOrientation];
+ Size2 screen_size = DisplayServer::get_singleton()->screen_get_size();
+ UIInterfaceOrientation orientation = UIInterfaceOrientationUnknown;
+
+ if (@available(iOS 13, *)) {
+ orientation = [UIApplication sharedApplication].delegate.window.windowScene.interfaceOrientation;
+#if !defined(TARGET_OS_SIMULATOR) || !TARGET_OS_SIMULATOR
+ } else {
+ orientation = [[UIApplication sharedApplication] statusBarOrientation];
+#endif
+ }
// Grab our camera image for our backbuffer
CVPixelBufferRef pixelBuffer = current_frame.capturedImage;
@@ -475,27 +505,27 @@ void ARKitInterface::process() {
{
// do Y
- int new_width = CVPixelBufferGetWidthOfPlane(pixelBuffer, 0);
- int new_height = CVPixelBufferGetHeightOfPlane(pixelBuffer, 0);
- int bytes_per_row = CVPixelBufferGetBytesPerRowOfPlane(pixelBuffer, 0);
+ size_t new_width = CVPixelBufferGetWidthOfPlane(pixelBuffer, 0);
+ size_t new_height = CVPixelBufferGetHeightOfPlane(pixelBuffer, 0);
+ size_t bytes_per_row = CVPixelBufferGetBytesPerRowOfPlane(pixelBuffer, 0);
if ((image_width[0] != new_width) || (image_height[0] != new_height)) {
printf("- Camera padding l:%lu r:%lu t:%lu b:%lu\n", extraLeft, extraRight, extraTop, extraBottom);
- printf("- Camera Y plane size: %i, %i - %i\n", new_width, new_height, bytes_per_row);
+ printf("- Camera Y plane size: %lu, %lu - %lu\n", new_width, new_height, bytes_per_row);
image_width[0] = new_width;
image_height[0] = new_height;
img_data[0].resize(new_width * new_height);
}
- uint8_t *w = img_data[0].write();
+ uint8_t *w = img_data[0].ptrw();
if (new_width == bytes_per_row) {
- memcpy(w.ptr(), dataY, new_width * new_height);
+ memcpy(w, dataY, new_width * new_height);
} else {
- int offset_a = 0;
- int offset_b = extraLeft + (extraTop * bytes_per_row);
- for (int r = 0; r < new_height; r++) {
- memcpy(w.ptr() + offset_a, dataY + offset_b, new_width);
+ size_t offset_a = 0;
+ size_t offset_b = extraLeft + (extraTop * bytes_per_row);
+ for (size_t r = 0; r < new_height; r++) {
+ memcpy(w + offset_a, dataY + offset_b, new_width);
offset_a += new_width;
offset_b += bytes_per_row;
}
@@ -507,26 +537,26 @@ void ARKitInterface::process() {
{
// do CbCr
- int new_width = CVPixelBufferGetWidthOfPlane(pixelBuffer, 1);
- int new_height = CVPixelBufferGetHeightOfPlane(pixelBuffer, 1);
- int bytes_per_row = CVPixelBufferGetBytesPerRowOfPlane(pixelBuffer, 0);
+ size_t new_width = CVPixelBufferGetWidthOfPlane(pixelBuffer, 1);
+ size_t new_height = CVPixelBufferGetHeightOfPlane(pixelBuffer, 1);
+ size_t bytes_per_row = CVPixelBufferGetBytesPerRowOfPlane(pixelBuffer, 0);
if ((image_width[1] != new_width) || (image_height[1] != new_height)) {
- printf("- Camera CbCr plane size: %i, %i - %i\n", new_width, new_height, bytes_per_row);
+ printf("- Camera CbCr plane size: %lu, %lu - %lu\n", new_width, new_height, bytes_per_row);
image_width[1] = new_width;
image_height[1] = new_height;
img_data[1].resize(2 * new_width * new_height);
}
- uint8_t *w = img_data[1].write();
+ uint8_t *w = img_data[1].ptrw();
if ((2 * new_width) == bytes_per_row) {
- memcpy(w.ptr(), dataCbCr, 2 * new_width * new_height);
+ memcpy(w, dataCbCr, 2 * new_width * new_height);
} else {
- int offset_a = 0;
- int offset_b = extraLeft + (extraTop * bytes_per_row);
- for (int r = 0; r < new_height; r++) {
- memcpy(w.ptr() + offset_a, dataCbCr + offset_b, 2 * new_width);
+ size_t offset_a = 0;
+ size_t offset_b = extraLeft + (extraTop * bytes_per_row);
+ for (size_t r = 0; r < new_height; r++) {
+ memcpy(w + offset_a, dataCbCr + offset_b, 2 * new_width);
offset_a += 2 * new_width;
offset_b += bytes_per_row;
}
@@ -658,69 +688,78 @@ void ARKitInterface::process() {
}
void ARKitInterface::_add_or_update_anchor(void *p_anchor) {
- _THREAD_SAFE_METHOD_
-
- ARAnchor *anchor = (ARAnchor *)p_anchor;
-
- unsigned char uuid[16];
- [anchor.identifier getUUIDBytes:uuid];
-
- XRPositionalTracker *tracker = get_anchor_for_uuid(uuid);
- if (tracker != NULL) {
- // lets update our mesh! (using Arjens code as is for now)
- // we should also probably limit how often we do this...
+ // _THREAD_SAFE_METHOD_
- // can we safely cast this?
- ARPlaneAnchor *planeAnchor = (ARPlaneAnchor *)anchor;
-
- if (planeAnchor.geometry.triangleCount > 0) {
- Ref<SurfaceTool> surftool;
- surftool.instance();
- surftool->begin(Mesh::PRIMITIVE_TRIANGLES);
+ if (@available(iOS 11.0, *)) {
+ ARAnchor *anchor = (ARAnchor *)p_anchor;
+
+ unsigned char uuid[16];
+ [anchor.identifier getUUIDBytes:uuid];
+
+ XRPositionalTracker *tracker = get_anchor_for_uuid(uuid);
+ if (tracker != NULL) {
+ // lets update our mesh! (using Arjens code as is for now)
+ // we should also probably limit how often we do this...
+
+ // can we safely cast this?
+ ARPlaneAnchor *planeAnchor = (ARPlaneAnchor *)anchor;
+
+ if (@available(iOS 11.3, *)) {
+ if (planeAnchor.geometry.triangleCount > 0) {
+ Ref<SurfaceTool> surftool;
+ surftool.instance();
+ surftool->begin(Mesh::PRIMITIVE_TRIANGLES);
+
+ for (int j = planeAnchor.geometry.triangleCount * 3 - 1; j >= 0; j--) {
+ int16_t index = planeAnchor.geometry.triangleIndices[j];
+ simd_float3 vrtx = planeAnchor.geometry.vertices[index];
+ simd_float2 textcoord = planeAnchor.geometry.textureCoordinates[index];
+ surftool->add_uv(Vector2(textcoord[0], textcoord[1]));
+ surftool->add_color(Color(0.8, 0.8, 0.8));
+ surftool->add_vertex(Vector3(vrtx[0], vrtx[1], vrtx[2]));
+ }
- for (int j = planeAnchor.geometry.triangleCount * 3 - 1; j >= 0; j--) {
- int16_t index = planeAnchor.geometry.triangleIndices[j];
- simd_float3 vrtx = planeAnchor.geometry.vertices[index];
- simd_float2 textcoord = planeAnchor.geometry.textureCoordinates[index];
- surftool->add_uv(Vector2(textcoord[0], textcoord[1]));
- surftool->add_color(Color(0.8, 0.8, 0.8));
- surftool->add_vertex(Vector3(vrtx[0], vrtx[1], vrtx[2]));
+ surftool->generate_normals();
+ tracker->set_mesh(surftool->commit());
+ } else {
+ Ref<Mesh> nomesh;
+ tracker->set_mesh(nomesh);
+ }
+ } else {
+ Ref<Mesh> nomesh;
+ tracker->set_mesh(nomesh);
}
- surftool->generate_normals();
- tracker->set_mesh(surftool->commit());
- } else {
- Ref<Mesh> nomesh;
- tracker->set_mesh(nomesh);
+ // Note, this also contains a scale factor which gives us an idea of the size of the anchor
+ // We may extract that in our XRAnchor class
+ Basis b;
+ matrix_float4x4 m44 = anchor.transform;
+ b.elements[0].x = m44.columns[0][0];
+ b.elements[1].x = m44.columns[0][1];
+ b.elements[2].x = m44.columns[0][2];
+ b.elements[0].y = m44.columns[1][0];
+ b.elements[1].y = m44.columns[1][1];
+ b.elements[2].y = m44.columns[1][2];
+ b.elements[0].z = m44.columns[2][0];
+ b.elements[1].z = m44.columns[2][1];
+ b.elements[2].z = m44.columns[2][2];
+ tracker->set_orientation(b);
+ tracker->set_rw_position(Vector3(m44.columns[3][0], m44.columns[3][1], m44.columns[3][2]));
}
-
- // Note, this also contains a scale factor which gives us an idea of the size of the anchor
- // We may extract that in our XRAnchor class
- Basis b;
- matrix_float4x4 m44 = anchor.transform;
- b.elements[0].x = m44.columns[0][0];
- b.elements[1].x = m44.columns[0][1];
- b.elements[2].x = m44.columns[0][2];
- b.elements[0].y = m44.columns[1][0];
- b.elements[1].y = m44.columns[1][1];
- b.elements[2].y = m44.columns[1][2];
- b.elements[0].z = m44.columns[2][0];
- b.elements[1].z = m44.columns[2][1];
- b.elements[2].z = m44.columns[2][2];
- tracker->set_orientation(b);
- tracker->set_rw_position(Vector3(m44.columns[3][0], m44.columns[3][1], m44.columns[3][2]));
}
}
void ARKitInterface::_remove_anchor(void *p_anchor) {
- _THREAD_SAFE_METHOD_
+ // _THREAD_SAFE_METHOD_
- ARAnchor *anchor = (ARAnchor *)p_anchor;
+ if (@available(iOS 11.0, *)) {
+ ARAnchor *anchor = (ARAnchor *)p_anchor;
- unsigned char uuid[16];
- [anchor.identifier getUUIDBytes:uuid];
+ unsigned char uuid[16];
+ [anchor.identifier getUUIDBytes:uuid];
- remove_anchor_for_uuid(uuid);
+ remove_anchor_for_uuid(uuid);
+ }
}
ARKitInterface::ARKitInterface() {
@@ -728,7 +767,9 @@ ARKitInterface::ARKitInterface() {
session_was_started = false;
plane_detection_is_enabled = false;
light_estimation_is_enabled = false;
- ar_session = NULL;
+ if (@available(iOS 11.0, *)) {
+ ar_session = NULL;
+ }
z_near = 0.01;
z_far = 1000.0;
projection.set_perspective(60.0, 1.0, z_near, z_far, false);
diff --git a/modules/arkit/arkit_session_delegate.h b/modules/arkit/arkit_session_delegate.h
index 158b80a60a..df98bf506e 100644
--- a/modules/arkit/arkit_session_delegate.h
+++ b/modules/arkit/arkit_session_delegate.h
@@ -42,9 +42,9 @@ class ARKitInterface;
@property(nonatomic) ARKitInterface *arkit_interface;
-- (void)session:(ARSession *)session didAddAnchors:(NSArray<ARAnchor *> *)anchors;
-- (void)session:(ARSession *)session didRemoveAnchors:(NSArray<ARAnchor *> *)anchors;
-- (void)session:(ARSession *)session didUpdateAnchors:(NSArray<ARAnchor *> *)anchors;
+- (void)session:(ARSession *)session didAddAnchors:(NSArray<ARAnchor *> *)anchors API_AVAILABLE(ios(11.0));
+- (void)session:(ARSession *)session didRemoveAnchors:(NSArray<ARAnchor *> *)anchors API_AVAILABLE(ios(11.0));
+- (void)session:(ARSession *)session didUpdateAnchors:(NSArray<ARAnchor *> *)anchors API_AVAILABLE(ios(11.0));
@end
#endif /* !ARKIT_SESSION_DELEGATE_H */
diff --git a/modules/bullet/shape_bullet.cpp b/modules/bullet/shape_bullet.cpp
index f4550c2024..274493ed17 100644
--- a/modules/bullet/shape_bullet.cpp
+++ b/modules/bullet/shape_bullet.cpp
@@ -504,6 +504,9 @@ void HeightMapShapeBullet::set_data(const Variant &p_data) {
int l_width = d["width"];
int l_depth = d["depth"];
+ ERR_FAIL_COND_MSG(l_width < 2, "Map width must be at least 2.");
+ ERR_FAIL_COND_MSG(l_depth < 2, "Map depth must be at least 2.");
+
// TODO This code will need adjustments if real_t is set to `double`,
// because that precision is unnecessary for a heightmap and Bullet doesn't support it...
diff --git a/modules/camera/camera_ios.mm b/modules/camera/camera_ios.mm
index f01135f251..c10b13b2af 100644
--- a/modules/camera/camera_ios.mm
+++ b/modules/camera/camera_ios.mm
@@ -158,25 +158,31 @@
} else if (dataCbCr == NULL) {
print_line("Couldn't access CbCr pixel buffer data");
} else {
- UIInterfaceOrientation orientation = [[UIApplication sharedApplication] statusBarOrientation];
+ UIInterfaceOrientation orientation = UIInterfaceOrientationUnknown;
+
+ if (@available(iOS 13, *)) {
+ orientation = [UIApplication sharedApplication].delegate.window.windowScene.interfaceOrientation;
+#if !defined(TARGET_OS_SIMULATOR) || !TARGET_OS_SIMULATOR
+ } else {
+ orientation = [[UIApplication sharedApplication] statusBarOrientation];
+#endif
+ }
+
Ref<Image> img[2];
{
// do Y
- int new_width = CVPixelBufferGetWidthOfPlane(pixelBuffer, 0);
- int new_height = CVPixelBufferGetHeightOfPlane(pixelBuffer, 0);
- int _bytes_per_row = CVPixelBufferGetBytesPerRowOfPlane(pixelBuffer, 0);
+ size_t new_width = CVPixelBufferGetWidthOfPlane(pixelBuffer, 0);
+ size_t new_height = CVPixelBufferGetHeightOfPlane(pixelBuffer, 0);
if ((width[0] != new_width) || (height[0] != new_height)) {
- // printf("Camera Y plane %i, %i - %i\n", new_width, new_height, bytes_per_row);
-
width[0] = new_width;
height[0] = new_height;
img_data[0].resize(new_width * new_height);
}
uint8_t *w = img_data[0].ptrw();
- memcpy(w.ptr(), dataY, new_width * new_height);
+ memcpy(w, dataY, new_width * new_height);
img[0].instance();
img[0]->create(new_width, new_height, 0, Image::FORMAT_R8, img_data[0]);
@@ -184,20 +190,17 @@
{
// do CbCr
- int new_width = CVPixelBufferGetWidthOfPlane(pixelBuffer, 1);
- int new_height = CVPixelBufferGetHeightOfPlane(pixelBuffer, 1);
- int bytes_per_row = CVPixelBufferGetBytesPerRowOfPlane(pixelBuffer, 1);
+ size_t new_width = CVPixelBufferGetWidthOfPlane(pixelBuffer, 1);
+ size_t new_height = CVPixelBufferGetHeightOfPlane(pixelBuffer, 1);
if ((width[1] != new_width) || (height[1] != new_height)) {
- // printf("Camera CbCr plane %i, %i - %i\n", new_width, new_height, bytes_per_row);
-
width[1] = new_width;
height[1] = new_height;
img_data[1].resize(2 * new_width * new_height);
}
uint8_t *w = img_data[1].ptrw();
- memcpy(w.ptr(), dataCbCr, 2 * new_width * new_height);
+ memcpy(w, dataCbCr, 2 * new_width * new_height);
///TODO GLES2 doesn't support FORMAT_RG8, need to do some form of conversion
img[1].instance();
@@ -359,41 +362,59 @@ void CameraIOS::update_feeds() {
// this way of doing things is deprecated but still works,
// rewrite to using AVCaptureDeviceDiscoverySession
- AVCaptureDeviceDiscoverySession *session = [AVCaptureDeviceDiscoverySession discoverySessionWithDeviceTypes:[NSArray arrayWithObjects:AVCaptureDeviceTypeBuiltInTelephotoCamera, AVCaptureDeviceTypeBuiltInDualCamera, AVCaptureDeviceTypeBuiltInTrueDepthCamera, AVCaptureDeviceTypeBuiltInWideAngleCamera, nil] mediaType:AVMediaTypeVideo position:AVCaptureDevicePositionUnspecified];
+ NSMutableArray *deviceTypes = [NSMutableArray array];
- // remove devices that are gone..
- for (int i = feeds.size() - 1; i >= 0; i--) {
- Ref<CameraFeedIOS> feed(feeds[i]);
+ if (@available(iOS 10, *)) {
+ [deviceTypes addObject:AVCaptureDeviceTypeBuiltInWideAngleCamera];
+ [deviceTypes addObject:AVCaptureDeviceTypeBuiltInTelephotoCamera];
- if (feed.is_null()) {
- // feed not managed by us
- } else if (![session.devices containsObject:feed->get_device()]) {
- // remove it from our array, this will also destroy it ;)
- remove_feed(feed);
- };
- };
+ if (@available(iOS 10.2, *)) {
+ [deviceTypes addObject:AVCaptureDeviceTypeBuiltInDualCamera];
+ }
- // add new devices..
- for (AVCaptureDevice *device in session.devices) {
- bool found = false;
+ if (@available(iOS 11.1, *)) {
+ [deviceTypes addObject:AVCaptureDeviceTypeBuiltInTrueDepthCamera];
+ }
+
+ AVCaptureDeviceDiscoverySession *session = [AVCaptureDeviceDiscoverySession
+ discoverySessionWithDeviceTypes:deviceTypes
+ mediaType:AVMediaTypeVideo
+ position:AVCaptureDevicePositionUnspecified];
- for (int i = 0; i < feeds.size() && !found; i++) {
+ // remove devices that are gone..
+ for (int i = feeds.size() - 1; i >= 0; i--) {
Ref<CameraFeedIOS> feed(feeds[i]);
if (feed.is_null()) {
// feed not managed by us
- } else if (feed->get_device() == device) {
- found = true;
+ } else if (![session.devices containsObject:feed->get_device()]) {
+ // remove it from our array, this will also destroy it ;)
+ remove_feed(feed);
};
};
- if (!found) {
- Ref<CameraFeedIOS> newfeed;
- newfeed.instance();
- newfeed->set_device(device);
- add_feed(newfeed);
+ // add new devices..
+ for (AVCaptureDevice *device in session.devices) {
+ bool found = false;
+
+ for (int i = 0; i < feeds.size() && !found; i++) {
+ Ref<CameraFeedIOS> feed(feeds[i]);
+
+ if (feed.is_null()) {
+ // feed not managed by us
+ } else if (feed->get_device() == device) {
+ found = true;
+ };
+ };
+
+ if (!found) {
+ Ref<CameraFeedIOS> newfeed;
+ newfeed.instance();
+ newfeed->set_device(device);
+ add_feed(newfeed);
+ };
};
- };
+ }
};
CameraIOS::CameraIOS() {
diff --git a/modules/gdnative/nativescript/nativescript.cpp b/modules/gdnative/nativescript/nativescript.cpp
index 94aa2125c2..632f4e5fee 100644
--- a/modules/gdnative/nativescript/nativescript.cpp
+++ b/modules/gdnative/nativescript/nativescript.cpp
@@ -991,7 +991,8 @@ void NativeScriptInstance::notification(int p_notification) {
Variant value = p_notification;
const Variant *args[1] = { &value };
- call_multilevel("_notification", args, 1);
+ Callable::CallError error;
+ call("_notification", args, 1, error);
}
String NativeScriptInstance::to_string(bool *r_valid) {
@@ -1087,31 +1088,6 @@ ScriptLanguage *NativeScriptInstance::get_language() {
return NativeScriptLanguage::get_singleton();
}
-void NativeScriptInstance::call_multilevel(const StringName &p_method, const Variant **p_args, int p_argcount) {
- NativeScriptDesc *script_data = GET_SCRIPT_DESC();
-
- while (script_data) {
- Map<StringName, NativeScriptDesc::Method>::Element *E = script_data->methods.find(p_method);
- if (E) {
- godot_variant res = E->get().method.method((godot_object *)owner,
- E->get().method.method_data,
- userdata,
- p_argcount,
- (godot_variant **)p_args);
- godot_variant_destroy(&res);
- }
- script_data = script_data->base_data;
- }
-}
-
-void NativeScriptInstance::call_multilevel_reversed(const StringName &p_method, const Variant **p_args, int p_argcount) {
- NativeScriptDesc *script_data = GET_SCRIPT_DESC();
-
- if (script_data) {
- _ml_call_reversed(script_data, p_method, p_args, p_argcount);
- }
-}
-
NativeScriptInstance::~NativeScriptInstance() {
NativeScriptDesc *script_data = GET_SCRIPT_DESC();
diff --git a/modules/gdnative/nativescript/nativescript.h b/modules/gdnative/nativescript/nativescript.h
index e709ce2337..145bf7dcb6 100644
--- a/modules/gdnative/nativescript/nativescript.h
+++ b/modules/gdnative/nativescript/nativescript.h
@@ -231,9 +231,6 @@ public:
virtual ScriptLanguage *get_language();
- virtual void call_multilevel(const StringName &p_method, const Variant **p_args, int p_argcount);
- virtual void call_multilevel_reversed(const StringName &p_method, const Variant **p_args, int p_argcount);
-
virtual void refcount_incremented();
virtual bool refcount_decremented();
diff --git a/modules/gdnative/pluginscript/pluginscript_instance.h b/modules/gdnative/pluginscript/pluginscript_instance.h
index 6309b6fde3..690d1a0432 100644
--- a/modules/gdnative/pluginscript/pluginscript_instance.h
+++ b/modules/gdnative/pluginscript/pluginscript_instance.h
@@ -62,12 +62,6 @@ public:
virtual Variant call(const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error);
- // Rely on default implementations provided by ScriptInstance for the moment.
- // Note that multilevel call could be removed in 3.0 release, so stay tuned
- // (see https://godotengine.org/qa/9244/can-override-the-_ready-and-_process-functions-child-classes)
- //virtual void call_multilevel(const StringName& p_method,const Variant** p_args,int p_argcount);
- //virtual void call_multilevel_reversed(const StringName& p_method,const Variant** p_args,int p_argcount);
-
virtual void notification(int p_notification);
virtual Ref<Script> get_script() const;
diff --git a/modules/gdscript/gdscript.cpp b/modules/gdscript/gdscript.cpp
index 40ef0aeec6..9170255c02 100644
--- a/modules/gdscript/gdscript.cpp
+++ b/modules/gdscript/gdscript.cpp
@@ -1309,39 +1309,6 @@ Variant GDScriptInstance::call(const StringName &p_method, const Variant **p_arg
return Variant();
}
-void GDScriptInstance::call_multilevel(const StringName &p_method, const Variant **p_args, int p_argcount) {
- GDScript *sptr = script.ptr();
- Callable::CallError ce;
-
- while (sptr) {
- Map<StringName, GDScriptFunction *>::Element *E = sptr->member_functions.find(p_method);
- if (E) {
- E->get()->call(this, p_args, p_argcount, ce);
- return;
- }
- sptr = sptr->_base;
- }
-}
-
-void GDScriptInstance::_ml_call_reversed(GDScript *sptr, const StringName &p_method, const Variant **p_args, int p_argcount) {
- if (sptr->_base) {
- _ml_call_reversed(sptr->_base, p_method, p_args, p_argcount);
- }
-
- Callable::CallError ce;
-
- Map<StringName, GDScriptFunction *>::Element *E = sptr->member_functions.find(p_method);
- if (E) {
- E->get()->call(this, p_args, p_argcount, ce);
- }
-}
-
-void GDScriptInstance::call_multilevel_reversed(const StringName &p_method, const Variant **p_args, int p_argcount) {
- if (script.ptr()) {
- _ml_call_reversed(script.ptr(), p_method, p_args, p_argcount);
- }
-}
-
void GDScriptInstance::notification(int p_notification) {
//notification is not virtual, it gets called at ALL levels just like in C.
Variant value = p_notification;
diff --git a/modules/gdscript/gdscript.h b/modules/gdscript/gdscript.h
index 8236464f15..9906b4014d 100644
--- a/modules/gdscript/gdscript.h
+++ b/modules/gdscript/gdscript.h
@@ -146,7 +146,6 @@ protected:
void _get_property_list(List<PropertyInfo> *p_properties) const;
Variant call(const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error) override;
- //void call_multilevel(const StringName& p_method,const Variant** p_args,int p_argcount);
static void _bind_methods();
@@ -258,8 +257,6 @@ class GDScriptInstance : public ScriptInstance {
SelfList<GDScriptFunctionState>::List pending_func_states;
- void _ml_call_reversed(GDScript *sptr, const StringName &p_method, const Variant **p_args, int p_argcount);
-
public:
virtual Object *get_owner() { return owner; }
@@ -271,8 +268,6 @@ public:
virtual void get_method_list(List<MethodInfo> *p_list) const;
virtual bool has_method(const StringName &p_method) const;
virtual Variant call(const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error);
- virtual void call_multilevel(const StringName &p_method, const Variant **p_args, int p_argcount);
- virtual void call_multilevel_reversed(const StringName &p_method, const Variant **p_args, int p_argcount);
Variant debug_get_member_by_index(int p_idx) const { return members[p_idx]; }
diff --git a/modules/gdscript/gdscript_compiler.cpp b/modules/gdscript/gdscript_compiler.cpp
index 3d37c7f803..e34d87f5cc 100644
--- a/modules/gdscript/gdscript_compiler.cpp
+++ b/modules/gdscript/gdscript_compiler.cpp
@@ -736,7 +736,7 @@ int GDScriptCompiler::_parse_expression(CodeGen &codegen, const GDScriptParser::
} else {
if (callee->type == GDScriptParser::Node::IDENTIFIER) {
// Self function call.
- if (codegen.function_node && codegen.function_node->is_static) {
+ if ((codegen.function_node && codegen.function_node->is_static) || call->function_name == "new") {
ret = (GDScriptFunction::ADDR_TYPE_CLASS << GDScriptFunction::ADDR_BITS);
} else {
ret = (GDScriptFunction::ADDR_TYPE_SELF << GDScriptFunction::ADDR_BITS);
diff --git a/modules/modules_builders.py b/modules/modules_builders.py
index e7be6380d1..2243162555 100644
--- a/modules/modules_builders.py
+++ b/modules/modules_builders.py
@@ -12,5 +12,16 @@ def generate_modules_enabled(target, source, env):
f.write("#define %s\n" % ("MODULE_" + module.upper() + "_ENABLED"))
+def generate_modules_tests(target, source, env):
+ import os
+ import glob
+
+ with open(target[0].path, "w") as f:
+ for name, path in env.module_list.items():
+ headers = glob.glob(os.path.join(path, "tests", "*.h"))
+ for h in headers:
+ f.write('#include "%s"\n' % (os.path.normpath(h)))
+
+
if __name__ == "__main__":
subprocess_main(globals())
diff --git a/modules/mono/csharp_script.cpp b/modules/mono/csharp_script.cpp
index 7d3ae31588..3d6ff1466d 100644
--- a/modules/mono/csharp_script.cpp
+++ b/modules/mono/csharp_script.cpp
@@ -897,7 +897,7 @@ void CSharpLanguage::reload_assemblies(bool p_soft_reload) {
// Call OnBeforeSerialize
if (csi->script->script_class->implements_interface(CACHED_CLASS(ISerializationListener))) {
- obj->get_script_instance()->call_multilevel(string_names.on_before_serialize);
+ obj->get_script_instance()->call(string_names.on_before_serialize);
}
// Save instance info
@@ -1133,7 +1133,7 @@ void CSharpLanguage::reload_assemblies(bool p_soft_reload) {
// Call OnAfterDeserialization
if (csi->script->script_class->implements_interface(CACHED_CLASS(ISerializationListener))) {
- obj->get_script_instance()->call_multilevel(string_names.on_after_deserialize);
+ obj->get_script_instance()->call(string_names.on_after_deserialize);
}
}
}
@@ -1866,41 +1866,6 @@ Variant CSharpInstance::call(const StringName &p_method, const Variant **p_args,
return Variant();
}
-void CSharpInstance::call_multilevel(const StringName &p_method, const Variant **p_args, int p_argcount) {
- GD_MONO_SCOPE_THREAD_ATTACH;
-
- if (script.is_valid()) {
- MonoObject *mono_object = get_mono_object();
-
- ERR_FAIL_NULL(mono_object);
-
- _call_multilevel(mono_object, p_method, p_args, p_argcount);
- }
-}
-
-void CSharpInstance::_call_multilevel(MonoObject *p_mono_object, const StringName &p_method, const Variant **p_args, int p_argcount) {
- GD_MONO_ASSERT_THREAD_ATTACHED;
-
- GDMonoClass *top = script->script_class;
-
- while (top && top != script->native) {
- GDMonoMethod *method = top->get_method(p_method, p_argcount);
-
- if (method) {
- method->invoke(p_mono_object, p_args);
- return;
- }
-
- top = top->get_parent_class();
- }
-}
-
-void CSharpInstance::call_multilevel_reversed(const StringName &p_method, const Variant **p_args, int p_argcount) {
- // Sorry, the method is the one that controls the call order
-
- call_multilevel(p_method, p_args, p_argcount);
-}
-
bool CSharpInstance::_reference_owner_unsafe() {
#ifdef DEBUG_ENABLED
CRASH_COND(!base_ref);
diff --git a/modules/mono/csharp_script.h b/modules/mono/csharp_script.h
index c2370364f9..f0b43a40f9 100644
--- a/modules/mono/csharp_script.h
+++ b/modules/mono/csharp_script.h
@@ -265,8 +265,6 @@ class CSharpInstance : public ScriptInstance {
friend void GDMonoInternals::tie_managed_to_unmanaged(MonoObject *, Object *);
static CSharpInstance *create_for_managed_type(Object *p_owner, CSharpScript *p_script, const MonoGCHandleData &p_gchandle);
- void _call_multilevel(MonoObject *p_mono_object, const StringName &p_method, const Variant **p_args, int p_argcount);
-
void get_properties_state_for_reloading(List<Pair<StringName, Variant>> &r_state);
void get_event_signals_state_for_reloading(List<Pair<StringName, Array>> &r_state);
@@ -285,8 +283,6 @@ public:
/* TODO */ void get_method_list(List<MethodInfo> *p_list) const override {}
bool has_method(const StringName &p_method) const override;
Variant call(const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error) override;
- void call_multilevel(const StringName &p_method, const Variant **p_args, int p_argcount) override;
- void call_multilevel_reversed(const StringName &p_method, const Variant **p_args, int p_argcount) override;
void mono_object_disposed(MonoObject *p_obj);
diff --git a/modules/visual_script/visual_script_editor.cpp b/modules/visual_script/visual_script_editor.cpp
index 2fcd9332a1..5581ea9318 100644
--- a/modules/visual_script/visual_script_editor.cpp
+++ b/modules/visual_script/visual_script_editor.cpp
@@ -2513,6 +2513,8 @@ RES VisualScriptEditor::get_edited_resource() const {
}
void VisualScriptEditor::set_edited_resource(const RES &p_res) {
+ ERR_FAIL_COND(script.is_valid());
+ ERR_FAIL_COND(p_res.is_null());
script = p_res;
signal_editor->script = script;
signal_editor->undo_redo = undo_redo;
@@ -2533,6 +2535,9 @@ void VisualScriptEditor::set_edited_resource(const RES &p_res) {
_update_members();
}
+void VisualScriptEditor::enable_editor() {
+}
+
Vector<String> VisualScriptEditor::get_functions() {
return Vector<String>();
}
@@ -2546,6 +2551,9 @@ String VisualScriptEditor::get_name() {
if (script->get_path().find("local://") == -1 && script->get_path().find("::") == -1) {
name = script->get_path().get_file();
if (is_unsaved()) {
+ if (script->get_path().empty()) {
+ name = TTR("[unsaved]");
+ }
name += "(*)";
}
} else if (script->get_name() != "") {
@@ -2562,7 +2570,11 @@ Ref<Texture2D> VisualScriptEditor::get_theme_icon() {
}
bool VisualScriptEditor::is_unsaved() {
- return script->is_edited() || script->are_subnodes_edited();
+ bool unsaved =
+ script->is_edited() ||
+ script->are_subnodes_edited() ||
+ script->get_path().empty(); // In memory.
+ return unsaved;
}
Variant VisualScriptEditor::get_edit_state() {
diff --git a/modules/visual_script/visual_script_editor.h b/modules/visual_script/visual_script_editor.h
index e59618e120..0c5665cee8 100644
--- a/modules/visual_script/visual_script_editor.h
+++ b/modules/visual_script/visual_script_editor.h
@@ -294,6 +294,7 @@ public:
virtual void apply_code() override;
virtual RES get_edited_resource() const override;
virtual void set_edited_resource(const RES &p_res) override;
+ virtual void enable_editor() override;
virtual Vector<String> get_functions() override;
virtual void reload_text() override;
virtual String get_name() override;
diff --git a/platform/iphone/SCsub b/platform/iphone/SCsub
index b72d29149c..49c77468ed 100644
--- a/platform/iphone/SCsub
+++ b/platform/iphone/SCsub
@@ -3,10 +3,8 @@
Import("env")
iphone_lib = [
- "godot_iphone.cpp",
- "os_iphone.cpp",
- "semaphore_iphone.cpp",
- "gl_view.mm",
+ "godot_iphone.mm",
+ "os_iphone.mm",
"main.m",
"app_delegate.mm",
"view_controller.mm",
@@ -15,6 +13,12 @@ iphone_lib = [
"icloud.mm",
"ios.mm",
"vulkan_context_iphone.mm",
+ "display_server_iphone.mm",
+ "joypad_iphone.mm",
+ "godot_view.mm",
+ "display_layer.mm",
+ "godot_view_renderer.mm",
+ "godot_view_gesture_recognizer.m",
]
env_ios = env.Clone()
diff --git a/platform/iphone/app_delegate.h b/platform/iphone/app_delegate.h
index 27552d781a..2f082f1e07 100644
--- a/platform/iphone/app_delegate.h
+++ b/platform/iphone/app_delegate.h
@@ -28,29 +28,20 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#if defined(OPENGL_ENABLED)
-#import "gl_view.h"
-#endif
-#import "view_controller.h"
#import <UIKit/UIKit.h>
-#import <CoreMotion/CoreMotion.h>
+@class ViewController;
// FIXME: Add support for both GLES2 and Vulkan when GLES2 is implemented again,
// so it can't be done with compilation time branching.
//#if defined(OPENGL_ENABLED)
//@interface AppDelegate : NSObject <UIApplicationDelegate, GLViewDelegate> {
//#endif
-#if defined(VULKAN_ENABLED)
-@interface AppDelegate : NSObject <UIApplicationDelegate> {
-#endif
- //@property (strong, nonatomic) UIWindow *window;
- ViewController *view_controller;
- bool is_focus_out;
-};
+//#if defined(VULKAN_ENABLED)
+@interface AppDelegate : NSObject <UIApplicationDelegate>
+//#endif
@property(strong, nonatomic) UIWindow *window;
-
-+ (ViewController *)getViewController;
+@property(strong, class, readonly, nonatomic) ViewController *viewController;
@end
diff --git a/platform/iphone/app_delegate.mm b/platform/iphone/app_delegate.mm
index c4ef185bf1..7edbcc4667 100644
--- a/platform/iphone/app_delegate.mm
+++ b/platform/iphone/app_delegate.mm
@@ -29,644 +29,60 @@
/*************************************************************************/
#import "app_delegate.h"
-
#include "core/project_settings.h"
#include "drivers/coreaudio/audio_driver_coreaudio.h"
-#if defined(OPENGL_ENABLED)
-#import "gl_view.h"
-#endif
+#import "godot_view.h"
#include "main/main.h"
#include "os_iphone.h"
+#import "view_controller.h"
-#import "GameController/GameController.h"
#import <AudioToolbox/AudioServices.h>
-#define kFilteringFactor 0.1
#define kRenderingFrequency 60
-#define kAccelerometerFrequency 100.0 // Hz
-
-Error _shell_open(String);
-void _set_keep_screen_on(bool p_enabled);
-
-Error _shell_open(String p_uri) {
- NSString *url = [[NSString alloc] initWithUTF8String:p_uri.utf8().get_data()];
-
- if (![[UIApplication sharedApplication] canOpenURL:[NSURL URLWithString:url]])
- return ERR_CANT_OPEN;
-
- printf("opening url %ls\n", p_uri.c_str());
- [[UIApplication sharedApplication] openURL:[NSURL URLWithString:url]];
- [url release];
- return OK;
-};
-
-void _set_keep_screen_on(bool p_enabled) {
- [[UIApplication sharedApplication] setIdleTimerDisabled:(BOOL)p_enabled];
-};
-
-void _vibrate() {
- AudioServicesPlaySystemSound(kSystemSoundID_Vibrate);
-};
-
-@implementation AppDelegate
-
-@synthesize window;
extern int gargc;
extern char **gargv;
-extern int iphone_main(int, int, int, char **, String);
+
+extern int iphone_main(int, char **, String);
extern void iphone_finish();
-CMMotionManager *motionManager;
-bool motionInitialised;
+@implementation AppDelegate
static ViewController *mainViewController = nil;
-+ (ViewController *)getViewController {
- return mainViewController;
-}
-
-NSMutableDictionary *ios_joysticks = nil;
-NSMutableArray *pending_ios_joysticks = nil;
-
-- (GCControllerPlayerIndex)getFreePlayerIndex {
- bool have_player_1 = false;
- bool have_player_2 = false;
- bool have_player_3 = false;
- bool have_player_4 = false;
-
- if (ios_joysticks == nil) {
- NSArray *keys = [ios_joysticks allKeys];
- for (NSNumber *key in keys) {
- GCController *controller = [ios_joysticks objectForKey:key];
- if (controller.playerIndex == GCControllerPlayerIndex1) {
- have_player_1 = true;
- } else if (controller.playerIndex == GCControllerPlayerIndex2) {
- have_player_2 = true;
- } else if (controller.playerIndex == GCControllerPlayerIndex3) {
- have_player_3 = true;
- } else if (controller.playerIndex == GCControllerPlayerIndex4) {
- have_player_4 = true;
- };
- };
- };
-
- if (!have_player_1) {
- return GCControllerPlayerIndex1;
- } else if (!have_player_2) {
- return GCControllerPlayerIndex2;
- } else if (!have_player_3) {
- return GCControllerPlayerIndex3;
- } else if (!have_player_4) {
- return GCControllerPlayerIndex4;
- } else {
- return GCControllerPlayerIndexUnset;
- };
-};
-
-void _ios_add_joystick(GCController *controller, AppDelegate *delegate) {
- // get a new id for our controller
- int joy_id = OSIPhone::get_singleton()->get_unused_joy_id();
- if (joy_id != -1) {
- // assign our player index
- if (controller.playerIndex == GCControllerPlayerIndexUnset) {
- controller.playerIndex = [delegate getFreePlayerIndex];
- };
-
- // tell Godot about our new controller
- OSIPhone::get_singleton()->joy_connection_changed(
- joy_id, true, [controller.vendorName UTF8String]);
-
- // add it to our dictionary, this will retain our controllers
- [ios_joysticks setObject:controller
- forKey:[NSNumber numberWithInt:joy_id]];
-
- // set our input handler
- [delegate setControllerInputHandler:controller];
- } else {
- printf("Couldn't retrieve new joy id\n");
- };
-}
-
-static void on_focus_out(ViewController *view_controller, bool *is_focus_out) {
- if (!*is_focus_out) {
- *is_focus_out = true;
- if (OS::get_singleton()->get_main_loop())
- OS::get_singleton()->get_main_loop()->notification(
- MainLoop::NOTIFICATION_WM_FOCUS_OUT);
-
- [view_controller.view stopAnimation];
- if (OS::get_singleton()->native_video_is_playing()) {
- OSIPhone::get_singleton()->native_video_focus_out();
- }
- AudioDriverCoreAudio *audio = dynamic_cast<AudioDriverCoreAudio *>(AudioDriverCoreAudio::get_singleton());
- if (audio)
- audio->stop();
- }
-}
-
-static void on_focus_in(ViewController *view_controller, bool *is_focus_out) {
- if (*is_focus_out) {
- *is_focus_out = false;
- if (OS::get_singleton()->get_main_loop())
- OS::get_singleton()->get_main_loop()->notification(
- MainLoop::NOTIFICATION_WM_FOCUS_IN);
-
- [view_controller.view startAnimation];
- if (OSIPhone::get_singleton()->native_video_is_playing()) {
- OSIPhone::get_singleton()->native_video_unpause();
- }
-
- AudioDriverCoreAudio *audio = dynamic_cast<AudioDriverCoreAudio *>(AudioDriverCoreAudio::get_singleton());
- if (audio)
- audio->start();
- }
++ (ViewController *)viewController {
+ return mainViewController;
}
-- (void)controllerWasConnected:(NSNotification *)notification {
- // create our dictionary if we don't have one yet
- if (ios_joysticks == nil) {
- ios_joysticks = [[NSMutableDictionary alloc] init];
- };
-
- // get our controller
- GCController *controller = (GCController *)notification.object;
- if (controller == nil) {
- printf("Couldn't retrieve new controller\n");
- } else if ([[ios_joysticks allKeysForObject:controller] count] != 0) {
- printf("Controller is already registered\n");
- } else if (frame_count > 1) {
- _ios_add_joystick(controller, self);
- } else {
- if (pending_ios_joysticks == nil)
- pending_ios_joysticks = [[NSMutableArray alloc] init];
- [pending_ios_joysticks addObject:controller];
- };
-};
-
-- (void)controllerWasDisconnected:(NSNotification *)notification {
- if (ios_joysticks != nil) {
- // find our joystick, there should be only one in our dictionary
- GCController *controller = (GCController *)notification.object;
- NSArray *keys = [ios_joysticks allKeysForObject:controller];
- for (NSNumber *key in keys) {
- // tell Godot this joystick is no longer there
- int joy_id = [key intValue];
- OSIPhone::get_singleton()->joy_connection_changed(joy_id, false, "");
-
- // and remove it from our dictionary
- [ios_joysticks removeObjectForKey:key];
- };
- };
-};
-
-- (int)getJoyIdForController:(GCController *)controller {
- if (ios_joysticks != nil) {
- // find our joystick, there should be only one in our dictionary
- NSArray *keys = [ios_joysticks allKeysForObject:controller];
- for (NSNumber *key in keys) {
- int joy_id = [key intValue];
- return joy_id;
- };
- };
-
- return -1;
-};
-
-- (void)setControllerInputHandler:(GCController *)controller {
- // Hook in the callback handler for the correct gamepad profile.
- // This is a bit of a weird design choice on Apples part.
- // You need to select the most capable gamepad profile for the
- // gamepad attached.
- if (controller.extendedGamepad != nil) {
- // The extended gamepad profile has all the input you could possibly find on
- // a gamepad but will only be active if your gamepad actually has all of
- // these...
- controller.extendedGamepad.valueChangedHandler = ^(
- GCExtendedGamepad *gamepad, GCControllerElement *element) {
- int joy_id = [self getJoyIdForController:controller];
-
- if (element == gamepad.buttonA) {
- OSIPhone::get_singleton()->joy_button(joy_id, JOY_BUTTON_A,
- gamepad.buttonA.isPressed);
- } else if (element == gamepad.buttonB) {
- OSIPhone::get_singleton()->joy_button(joy_id, JOY_BUTTON_B,
- gamepad.buttonB.isPressed);
- } else if (element == gamepad.buttonX) {
- OSIPhone::get_singleton()->joy_button(joy_id, JOY_BUTTON_X,
- gamepad.buttonX.isPressed);
- } else if (element == gamepad.buttonY) {
- OSIPhone::get_singleton()->joy_button(joy_id, JOY_BUTTON_Y,
- gamepad.buttonY.isPressed);
- } else if (element == gamepad.leftShoulder) {
- OSIPhone::get_singleton()->joy_button(joy_id, JOY_BUTTON_LEFT_SHOULDER,
- gamepad.leftShoulder.isPressed);
- } else if (element == gamepad.rightShoulder) {
- OSIPhone::get_singleton()->joy_button(joy_id, JOY_BUTTON_RIGHT_SHOULDER,
- gamepad.rightShoulder.isPressed);
- } else if (element == gamepad.dpad) {
- OSIPhone::get_singleton()->joy_button(joy_id, JOY_BUTTON_DPAD_UP,
- gamepad.dpad.up.isPressed);
- OSIPhone::get_singleton()->joy_button(joy_id, JOY_BUTTON_DPAD_DOWN,
- gamepad.dpad.down.isPressed);
- OSIPhone::get_singleton()->joy_button(joy_id, JOY_BUTTON_DPAD_LEFT,
- gamepad.dpad.left.isPressed);
- OSIPhone::get_singleton()->joy_button(joy_id, JOY_BUTTON_DPAD_RIGHT,
- gamepad.dpad.right.isPressed);
- };
-
- InputDefault::JoyAxis jx;
- jx.min = -1;
- if (element == gamepad.leftThumbstick) {
- jx.value = gamepad.leftThumbstick.xAxis.value;
- OSIPhone::get_singleton()->joy_axis(joy_id, JOY_AXIS_LEFT_X, jx);
- jx.value = -gamepad.leftThumbstick.yAxis.value;
- OSIPhone::get_singleton()->joy_axis(joy_id, JOY_AXIS_LEFT_Y, jx);
- } else if (element == gamepad.rightThumbstick) {
- jx.value = gamepad.rightThumbstick.xAxis.value;
- OSIPhone::get_singleton()->joy_axis(joy_id, JOY_AXIS_RIGHT_X, jx);
- jx.value = -gamepad.rightThumbstick.yAxis.value;
- OSIPhone::get_singleton()->joy_axis(joy_id, JOY_AXIS_RIGHT_Y, jx);
- } else if (element == gamepad.leftTrigger) {
- jx.value = gamepad.leftTrigger.value;
- OSIPhone::get_singleton()->joy_axis(joy_id, JOY_AXIS_TRIGGER_LEFT, jx);
- } else if (element == gamepad.rightTrigger) {
- jx.value = gamepad.rightTrigger.value;
- OSIPhone::get_singleton()->joy_axis(joy_id, JOY_AXIS_TRIGGER_RIGHT, jx);
- };
- };
- } else if (controller.gamepad != nil) {
- // gamepad is the standard profile with 4 buttons, shoulder buttons and a
- // D-pad
- controller.gamepad.valueChangedHandler = ^(GCGamepad *gamepad,
- GCControllerElement *element) {
- int joy_id = [self getJoyIdForController:controller];
-
- if (element == gamepad.buttonA) {
- OSIPhone::get_singleton()->joy_button(joy_id, JOY_BUTTON_A,
- gamepad.buttonA.isPressed);
- } else if (element == gamepad.buttonB) {
- OSIPhone::get_singleton()->joy_button(joy_id, JOY_BUTTON_B,
- gamepad.buttonB.isPressed);
- } else if (element == gamepad.buttonX) {
- OSIPhone::get_singleton()->joy_button(joy_id, JOY_BUTTON_X,
- gamepad.buttonX.isPressed);
- } else if (element == gamepad.buttonY) {
- OSIPhone::get_singleton()->joy_button(joy_id, JOY_BUTTON_Y,
- gamepad.buttonY.isPressed);
- } else if (element == gamepad.leftShoulder) {
- OSIPhone::get_singleton()->joy_button(joy_id, JOY_BUTTON_LEFT_SHOULDER,
- gamepad.leftShoulder.isPressed);
- } else if (element == gamepad.rightShoulder) {
- OSIPhone::get_singleton()->joy_button(joy_id, JOY_BUTTON_RIGHT_SHOULDER,
- gamepad.rightShoulder.isPressed);
- } else if (element == gamepad.dpad) {
- OSIPhone::get_singleton()->joy_button(joy_id, JOY_BUTTON_DPAD_UP,
- gamepad.dpad.up.isPressed);
- OSIPhone::get_singleton()->joy_button(joy_id, JOY_BUTTON_DPAD_DOWN,
- gamepad.dpad.down.isPressed);
- OSIPhone::get_singleton()->joy_button(joy_id, JOY_BUTTON_DPAD_LEFT,
- gamepad.dpad.left.isPressed);
- OSIPhone::get_singleton()->joy_button(joy_id, JOY_BUTTON_DPAD_RIGHT,
- gamepad.dpad.right.isPressed);
- };
- };
-#ifdef ADD_MICRO_GAMEPAD // disabling this for now, only available on iOS 9+,
- // while we are setting that as the minimum, seems our
- // build environment doesn't like it
- } else if (controller.microGamepad != nil) {
- // micro gamepads were added in OS 9 and feature just 2 buttons and a d-pad
- controller.microGamepad.valueChangedHandler =
- ^(GCMicroGamepad *gamepad, GCControllerElement *element) {
- int joy_id = [self getJoyIdForController:controller];
-
- if (element == gamepad.buttonA) {
- OSIPhone::get_singleton()->joy_button(joy_id, JOY_BUTTON_A,
- gamepad.buttonA.isPressed);
- } else if (element == gamepad.buttonX) {
- OSIPhone::get_singleton()->joy_button(joy_id, JOY_BUTTON_X,
- gamepad.buttonX.isPressed);
- } else if (element == gamepad.dpad) {
- OSIPhone::get_singleton()->joy_button(joy_id, JOY_BUTTON_DPAD_UP,
- gamepad.dpad.up.isPressed);
- OSIPhone::get_singleton()->joy_button(joy_id, JOY_BUTTON_DPAD_DOWN,
- gamepad.dpad.down.isPressed);
- OSIPhone::get_singleton()->joy_button(joy_id, JOY_BUTTON_DPAD_LEFT,
- gamepad.dpad.left.isPressed);
- OSIPhone::get_singleton()->joy_button(joy_id, JOY_BUTTON_DPAD_RIGHT,
- gamepad.dpad.right.isPressed);
- };
- };
-#endif
- };
-
- ///@TODO need to add support for controller.motion which gives us access to
- /// the orientation of the device (if supported)
-
- ///@TODO need to add support for controllerPausedHandler which should be a
- /// toggle
-};
-
-- (void)initGameControllers {
- // get told when controllers connect, this will be called right away for
- // already connected controllers
- [[NSNotificationCenter defaultCenter]
- addObserver:self
- selector:@selector(controllerWasConnected:)
- name:GCControllerDidConnectNotification
- object:nil];
-
- // get told when controllers disconnect
- [[NSNotificationCenter defaultCenter]
- addObserver:self
- selector:@selector(controllerWasDisconnected:)
- name:GCControllerDidDisconnectNotification
- object:nil];
-};
-
-- (void)deinitGameControllers {
- [[NSNotificationCenter defaultCenter]
- removeObserver:self
- name:GCControllerDidConnectNotification
- object:nil];
- [[NSNotificationCenter defaultCenter]
- removeObserver:self
- name:GCControllerDidDisconnectNotification
- object:nil];
-
- if (ios_joysticks != nil) {
- [ios_joysticks dealloc];
- ios_joysticks = nil;
- };
-
- if (pending_ios_joysticks != nil) {
- [pending_ios_joysticks dealloc];
- pending_ios_joysticks = nil;
- };
-};
-
-OS::VideoMode _get_video_mode() {
- int backingWidth;
- int backingHeight;
-#if defined(OPENGL_ENABLED)
- glGetRenderbufferParameterivOES(GL_RENDERBUFFER_OES,
- GL_RENDERBUFFER_WIDTH_OES, &backingWidth);
- glGetRenderbufferParameterivOES(GL_RENDERBUFFER_OES,
- GL_RENDERBUFFER_HEIGHT_OES, &backingHeight);
-#endif
-
- OS::VideoMode vm;
- vm.fullscreen = true;
- vm.width = backingWidth;
- vm.height = backingHeight;
- vm.resizable = false;
- return vm;
-};
-
-static int frame_count = 0;
-- (void)drawView:(UIView *)view;
-{
- switch (frame_count) {
- case 0: {
- OS::get_singleton()->set_video_mode(_get_video_mode());
-
- if (!OS::get_singleton()) {
- exit(0);
- };
- ++frame_count;
-
- NSString *locale_code = [[NSLocale currentLocale] localeIdentifier];
- OSIPhone::get_singleton()->set_locale(
- String::utf8([locale_code UTF8String]));
-
- NSString *uuid;
- if ([[UIDevice currentDevice]
- respondsToSelector:@selector(identifierForVendor)]) {
- uuid = [UIDevice currentDevice].identifierForVendor.UUIDString;
- } else {
- // before iOS 6, so just generate an identifier and store it
- uuid = [[NSUserDefaults standardUserDefaults]
- objectForKey:@"identiferForVendor"];
- if (!uuid) {
- CFUUIDRef cfuuid = CFUUIDCreate(NULL);
- uuid = (__bridge_transfer NSString *)CFUUIDCreateString(NULL, cfuuid);
- CFRelease(cfuuid);
- [[NSUserDefaults standardUserDefaults]
- setObject:uuid
- forKey:@"identifierForVendor"];
- }
- }
-
- OSIPhone::get_singleton()->set_unique_id(String::utf8([uuid UTF8String]));
-
- }; break;
-
- case 1: {
- Main::setup2();
- ++frame_count;
-
- if (pending_ios_joysticks != nil) {
- for (GCController *controller in pending_ios_joysticks) {
- _ios_add_joystick(controller, self);
- }
- [pending_ios_joysticks dealloc];
- pending_ios_joysticks = nil;
- }
-
- // this might be necessary before here
- NSDictionary *dict = [[NSBundle mainBundle] infoDictionary];
- for (NSString *key in dict) {
- NSObject *value = [dict objectForKey:key];
- String ukey = String::utf8([key UTF8String]);
-
- // we need a NSObject to Variant conversor
-
- if ([value isKindOfClass:[NSString class]]) {
- NSString *str = (NSString *)value;
- String uval = String::utf8([str UTF8String]);
-
- ProjectSettings::get_singleton()->set("Info.plist/" + ukey, uval);
-
- } else if ([value isKindOfClass:[NSNumber class]]) {
- NSNumber *n = (NSNumber *)value;
- double dval = [n doubleValue];
-
- ProjectSettings::get_singleton()->set("Info.plist/" + ukey, dval);
- };
- // do stuff
- }
-
- }; break;
-
- case 2: {
- Main::start();
- ++frame_count;
-
- }; break; // no fallthrough
-
- default: {
- if (OSIPhone::get_singleton()) {
- // OSIPhone::get_singleton()->update_accelerometer(accel[0], accel[1],
- // accel[2]);
- if (motionInitialised) {
- // Just using polling approach for now, we can set this up so it sends
- // data to us in intervals, might be better. See Apple reference pages
- // for more details:
- // https://developer.apple.com/reference/coremotion/cmmotionmanager?language=objc
-
- // Apple splits our accelerometer date into a gravity and user movement
- // component. We add them back together
- CMAcceleration gravity = motionManager.deviceMotion.gravity;
- CMAcceleration acceleration =
- motionManager.deviceMotion.userAcceleration;
-
- ///@TODO We don't seem to be getting data here, is my device broken or
- /// is this code incorrect?
- CMMagneticField magnetic =
- motionManager.deviceMotion.magneticField.field;
-
- ///@TODO we can access rotationRate as a CMRotationRate variable
- ///(processed date) or CMGyroData (raw data), have to see what works
- /// best
- CMRotationRate rotation = motionManager.deviceMotion.rotationRate;
-
- // Adjust for screen orientation.
- // [[UIDevice currentDevice] orientation] changes even if we've fixed
- // our orientation which is not a good thing when you're trying to get
- // your user to move the screen in all directions and want consistent
- // output
-
- ///@TODO Using [[UIApplication sharedApplication] statusBarOrientation]
- /// is a bit of a hack. Godot obviously knows the orientation so maybe
- /// we
- // can use that instead? (note that left and right seem swapped)
-
- switch ([[UIApplication sharedApplication] statusBarOrientation]) {
- case UIDeviceOrientationLandscapeLeft: {
- OSIPhone::get_singleton()->update_gravity(-gravity.y, gravity.x,
- gravity.z);
- OSIPhone::get_singleton()->update_accelerometer(
- -(acceleration.y + gravity.y), (acceleration.x + gravity.x),
- acceleration.z + gravity.z);
- OSIPhone::get_singleton()->update_magnetometer(
- -magnetic.y, magnetic.x, magnetic.z);
- OSIPhone::get_singleton()->update_gyroscope(-rotation.y, rotation.x,
- rotation.z);
- }; break;
- case UIDeviceOrientationLandscapeRight: {
- OSIPhone::get_singleton()->update_gravity(gravity.y, -gravity.x,
- gravity.z);
- OSIPhone::get_singleton()->update_accelerometer(
- (acceleration.y + gravity.y), -(acceleration.x + gravity.x),
- acceleration.z + gravity.z);
- OSIPhone::get_singleton()->update_magnetometer(
- magnetic.y, -magnetic.x, magnetic.z);
- OSIPhone::get_singleton()->update_gyroscope(rotation.y, -rotation.x,
- rotation.z);
- }; break;
- case UIDeviceOrientationPortraitUpsideDown: {
- OSIPhone::get_singleton()->update_gravity(-gravity.x, gravity.y,
- gravity.z);
- OSIPhone::get_singleton()->update_accelerometer(
- -(acceleration.x + gravity.x), (acceleration.y + gravity.y),
- acceleration.z + gravity.z);
- OSIPhone::get_singleton()->update_magnetometer(
- -magnetic.x, magnetic.y, magnetic.z);
- OSIPhone::get_singleton()->update_gyroscope(-rotation.x, rotation.y,
- rotation.z);
- }; break;
- default: { // assume portrait
- OSIPhone::get_singleton()->update_gravity(gravity.x, gravity.y,
- gravity.z);
- OSIPhone::get_singleton()->update_accelerometer(
- acceleration.x + gravity.x, acceleration.y + gravity.y,
- acceleration.z + gravity.z);
- OSIPhone::get_singleton()->update_magnetometer(magnetic.x, magnetic.y,
- magnetic.z);
- OSIPhone::get_singleton()->update_gyroscope(rotation.x, rotation.y,
- rotation.z);
- }; break;
- };
- }
-
- bool quit_request = OSIPhone::get_singleton()->iterate();
- };
-
- }; break;
- };
-};
-
-- (void)applicationDidReceiveMemoryWarning:(UIApplication *)application {
- if (OS::get_singleton()->get_main_loop()) {
- OS::get_singleton()->get_main_loop()->notification(
- MainLoop::NOTIFICATION_OS_MEMORY_WARNING);
- }
-};
-
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
- CGRect rect = [[UIScreen mainScreen] bounds];
+ // TODO: might be required to make an early return, so app wouldn't crash because of timeout.
+ // TODO: logo screen is not displayed while shaders are compiling
+ // DummyViewController(Splash/LoadingViewController) -> setup -> GodotViewController
- is_focus_out = false;
-
- [application setStatusBarHidden:YES withAnimation:UIStatusBarAnimationNone];
- // disable idle timer
- // application.idleTimerDisabled = YES;
+ CGRect windowBounds = [[UIScreen mainScreen] bounds];
// Create a full-screen window
- window = [[UIWindow alloc] initWithFrame:rect];
-
- OS::VideoMode vm = _get_video_mode();
+ self.window = [[[UIWindow alloc] initWithFrame:windowBounds] autorelease];
- NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,
- NSUserDomainMask, YES);
+ NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
- int err = iphone_main(vm.width, vm.height, gargc, gargv, String::utf8([documentsDirectory UTF8String]));
+ int err = iphone_main(gargc, gargv, String::utf8([documentsDirectory UTF8String]));
+
if (err != 0) {
// bail, things did not go very well for us, should probably output a message on screen with our error code...
exit(0);
- return FALSE;
+ return NO;
};
-#if defined(OPENGL_ENABLED)
- // WARNING: We must *always* create the GLView after we have constructed the
- // OS with iphone_main. This allows the GLView to access project settings so
- // it can properly initialize the OpenGL context
- GLView *glView = [[GLView alloc] initWithFrame:rect];
- glView.delegate = self;
-
- view_controller = [[ViewController alloc] init];
- view_controller.view = glView;
-
- _set_keep_screen_on(bool(GLOBAL_DEF("display/window/energy_saving/keep_screen_on", true)) ? YES : NO);
- glView.useCADisplayLink =
- bool(GLOBAL_DEF("display.iOS/use_cadisplaylink", true)) ? YES : NO;
- printf("cadisaplylink: %d", glView.useCADisplayLink);
- glView.animationInterval = 1.0 / kRenderingFrequency;
- [glView startAnimation];
-#endif
-
-#if defined(VULKAN_ENABLED)
- view_controller = [[ViewController alloc] init];
-#endif
+ ViewController *viewController = [[ViewController alloc] init];
+ viewController.godotView.useCADisplayLink = bool(GLOBAL_DEF("display.iOS/use_cadisplaylink", true)) ? YES : NO;
+ viewController.godotView.renderingInterval = 1.0 / kRenderingFrequency;
- window.rootViewController = view_controller;
+ self.window.rootViewController = viewController;
// Show the window
- [window makeKeyAndVisible];
-
- // Configure and start accelerometer
- if (!motionInitialised) {
- motionManager = [[CMMotionManager alloc] init];
- if (motionManager.deviceMotionAvailable) {
- motionManager.deviceMotionUpdateInterval = 1.0 / 70.0;
- [motionManager startDeviceMotionUpdatesUsingReferenceFrame:
- CMAttitudeReferenceFrameXMagneticNorthZVertical];
- motionInitialised = YES;
- };
- };
-
- [self initGameControllers];
+ [self.window makeKeyAndVisible];
[[NSNotificationCenter defaultCenter]
addObserver:self
@@ -674,40 +90,33 @@ static int frame_count = 0;
name:AVAudioSessionInterruptionNotification
object:[AVAudioSession sharedInstance]];
- // OSIPhone::screen_width = rect.size.width - rect.origin.x;
- // OSIPhone::screen_height = rect.size.height - rect.origin.y;
-
- mainViewController = view_controller;
+ mainViewController = viewController;
// prevent to stop music in another background app
[[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryAmbient error:nil];
- return TRUE;
+ return YES;
};
- (void)onAudioInterruption:(NSNotification *)notification {
if ([notification.name isEqualToString:AVAudioSessionInterruptionNotification]) {
if ([[notification.userInfo valueForKey:AVAudioSessionInterruptionTypeKey] isEqualToNumber:[NSNumber numberWithInt:AVAudioSessionInterruptionTypeBegan]]) {
NSLog(@"Audio interruption began");
- on_focus_out(view_controller, &is_focus_out);
+ OSIPhone::get_singleton()->on_focus_out();
} else if ([[notification.userInfo valueForKey:AVAudioSessionInterruptionTypeKey] isEqualToNumber:[NSNumber numberWithInt:AVAudioSessionInterruptionTypeEnded]]) {
NSLog(@"Audio interruption ended");
- on_focus_in(view_controller, &is_focus_out);
+ OSIPhone::get_singleton()->on_focus_in();
}
}
};
-- (void)applicationWillTerminate:(UIApplication *)application {
- [self deinitGameControllers];
-
- if (motionInitialised) {
- ///@TODO is this the right place to clean this up?
- [motionManager stopDeviceMotionUpdates];
- [motionManager release];
- motionManager = nil;
- motionInitialised = NO;
- };
+- (void)applicationDidReceiveMemoryWarning:(UIApplication *)application {
+ if (OS::get_singleton()->get_main_loop()) {
+ OS::get_singleton()->get_main_loop()->notification(MainLoop::NOTIFICATION_OS_MEMORY_WARNING);
+ }
+};
+- (void)applicationWillTerminate:(UIApplication *)application {
iphone_finish();
};
@@ -722,15 +131,15 @@ static int frame_count = 0;
// notification panel by swiping from the upper part of the screen.
- (void)applicationWillResignActive:(UIApplication *)application {
- on_focus_out(view_controller, &is_focus_out);
+ OSIPhone::get_singleton()->on_focus_out();
}
- (void)applicationDidBecomeActive:(UIApplication *)application {
- on_focus_in(view_controller, &is_focus_out);
+ OSIPhone::get_singleton()->on_focus_in();
}
- (void)dealloc {
- [window release];
+ self.window = nil;
[super dealloc];
}
diff --git a/platform/iphone/detect.py b/platform/iphone/detect.py
index c48eb7ff0e..6f67fc53c2 100644
--- a/platform/iphone/detect.py
+++ b/platform/iphone/detect.py
@@ -120,18 +120,18 @@ def configure(env):
CCFLAGS=(
"-arch "
+ arch_flag
- + " -fobjc-abi-version=2 -fobjc-legacy-dispatch -fmessage-length=0 -fpascal-strings -fblocks -fasm-blocks -isysroot $IPHONESDK -mios-simulator-version-min=10.0"
+ + " -fobjc-abi-version=2 -fobjc-legacy-dispatch -fmessage-length=0 -fpascal-strings -fblocks -fasm-blocks -isysroot $IPHONESDK -mios-simulator-version-min=13.0"
).split()
)
elif env["arch"] == "arm":
detect_darwin_sdk_path("iphone", env)
env.Append(
- CCFLAGS='-fno-objc-arc -arch armv7 -fmessage-length=0 -fno-strict-aliasing -fdiagnostics-print-source-range-info -fdiagnostics-show-category=id -fdiagnostics-parseable-fixits -fpascal-strings -fblocks -isysroot $IPHONESDK -fvisibility=hidden -mthumb "-DIBOutlet=__attribute__((iboutlet))" "-DIBOutletCollection(ClassName)=__attribute__((iboutletcollection(ClassName)))" "-DIBAction=void)__attribute__((ibaction)" -miphoneos-version-min=10.0 -MMD -MT dependencies'.split()
+ CCFLAGS='-fno-objc-arc -arch armv7 -fmessage-length=0 -fno-strict-aliasing -fdiagnostics-print-source-range-info -fdiagnostics-show-category=id -fdiagnostics-parseable-fixits -fpascal-strings -fblocks -isysroot $IPHONESDK -fvisibility=hidden -mthumb "-DIBOutlet=__attribute__((iboutlet))" "-DIBOutletCollection(ClassName)=__attribute__((iboutletcollection(ClassName)))" "-DIBAction=void)__attribute__((ibaction)" -miphoneos-version-min=11.0 -MMD -MT dependencies'.split()
)
elif env["arch"] == "arm64":
detect_darwin_sdk_path("iphone", env)
env.Append(
- CCFLAGS="-fno-objc-arc -arch arm64 -fmessage-length=0 -fno-strict-aliasing -fdiagnostics-print-source-range-info -fdiagnostics-show-category=id -fdiagnostics-parseable-fixits -fpascal-strings -fblocks -fvisibility=hidden -MMD -MT dependencies -miphoneos-version-min=10.0 -isysroot $IPHONESDK".split()
+ CCFLAGS="-fno-objc-arc -arch arm64 -fmessage-length=0 -fno-strict-aliasing -fdiagnostics-print-source-range-info -fdiagnostics-show-category=id -fdiagnostics-parseable-fixits -fpascal-strings -fblocks -fvisibility=hidden -MMD -MT dependencies -miphoneos-version-min=11.0 -isysroot $IPHONESDK".split()
)
env.Append(CPPDEFINES=["NEED_LONG_INT"])
env.Append(CPPDEFINES=["LIBYUV_DISABLE_NEON"])
@@ -143,6 +143,9 @@ def configure(env):
else:
env.Append(CCFLAGS=["-fno-exceptions"])
+ # Temp fix for ABS/MAX/MIN macros in iPhone SDK blocking compilation
+ env.Append(CCFLAGS=["-Wno-ambiguous-macro"])
+
## Link flags
if env["arch"] == "x86" or env["arch"] == "x86_64":
@@ -151,7 +154,7 @@ def configure(env):
LINKFLAGS=[
"-arch",
arch_flag,
- "-mios-simulator-version-min=10.0",
+ "-mios-simulator-version-min=13.0",
"-isysroot",
"$IPHONESDK",
"-Xlinker",
@@ -162,9 +165,9 @@ def configure(env):
]
)
elif env["arch"] == "arm":
- env.Append(LINKFLAGS=["-arch", "armv7", "-Wl,-dead_strip", "-miphoneos-version-min=10.0"])
+ env.Append(LINKFLAGS=["-arch", "armv7", "-Wl,-dead_strip", "-miphoneos-version-min=11.0"])
if env["arch"] == "arm64":
- env.Append(LINKFLAGS=["-arch", "arm64", "-Wl,-dead_strip", "-miphoneos-version-min=10.0"])
+ env.Append(LINKFLAGS=["-arch", "arm64", "-Wl,-dead_strip", "-miphoneos-version-min=11.0"])
env.Append(
LINKFLAGS=[
@@ -228,8 +231,7 @@ def configure(env):
env.Append(CPPDEFINES=["VULKAN_ENABLED"])
env.Append(LINKFLAGS=["-framework", "IOSurface"])
- if env["use_static_mvk"]:
- env.Append(LINKFLAGS=["-framework", "MoltenVK"])
- env["builtin_vulkan"] = False
- elif not env["builtin_vulkan"]:
- env.Append(LIBS=["vulkan"])
+
+ # Use Static Vulkan for iOS. Dynamic Framework works fine too.
+ env.Append(LINKFLAGS=["-framework", "MoltenVK"])
+ env["builtin_vulkan"] = False
diff --git a/platform/iphone/display_layer.h b/platform/iphone/display_layer.h
new file mode 100644
index 0000000000..bfde8f96a3
--- /dev/null
+++ b/platform/iphone/display_layer.h
@@ -0,0 +1,58 @@
+/*************************************************************************/
+/* display_layer.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. */
+/*************************************************************************/
+
+#import <OpenGLES/EAGLDrawable.h>
+#import <QuartzCore/QuartzCore.h>
+
+@protocol DisplayLayer <NSObject>
+
+- (void)renderDisplayLayer;
+- (void)initializeDisplayLayer;
+- (void)layoutDisplayLayer;
+
+@end
+
+// An ugly workaround for iOS simulator
+#if defined(TARGET_OS_SIMULATOR) && TARGET_OS_SIMULATOR
+#if defined(__IPHONE_13_0)
+API_AVAILABLE(ios(13.0))
+@interface GodotMetalLayer : CAMetalLayer <DisplayLayer>
+#else
+@interface GodotMetalLayer : CALayer <DisplayLayer>
+#endif
+#else
+@interface GodotMetalLayer : CAMetalLayer <DisplayLayer>
+#endif
+@end
+
+API_DEPRECATED("OpenGLES is deprecated", ios(2.0, 12.0))
+@interface GodotOpenGLLayer : CAEAGLLayer <DisplayLayer>
+
+@end
diff --git a/platform/iphone/display_layer.mm b/platform/iphone/display_layer.mm
new file mode 100644
index 0000000000..5ec94fb471
--- /dev/null
+++ b/platform/iphone/display_layer.mm
@@ -0,0 +1,186 @@
+/*************************************************************************/
+/* display_layer.mm */
+/*************************************************************************/
+/* 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. */
+/*************************************************************************/
+
+#import "display_layer.h"
+#include "core/os/keyboard.h"
+#include "core/project_settings.h"
+#include "display_server_iphone.h"
+#include "main/main.h"
+#include "os_iphone.h"
+#include "servers/audio_server.h"
+
+#import <AudioToolbox/AudioServices.h>
+#import <GameController/GameController.h>
+#import <OpenGLES/EAGL.h>
+#import <OpenGLES/ES1/gl.h>
+#import <OpenGLES/ES1/glext.h>
+#import <QuartzCore/QuartzCore.h>
+#import <UIKit/UIKit.h>
+
+@implementation GodotMetalLayer
+
+- (void)initializeDisplayLayer {
+#if defined(TARGET_OS_SIMULATOR) && TARGET_OS_SIMULATOR
+ if (@available(iOS 13, *)) {
+ // Simulator supports Metal since iOS 13
+ } else {
+ NSLog(@"iOS Simulator prior to iOS 13 does not support Metal rendering.");
+ }
+#endif
+}
+
+- (void)layoutDisplayLayer {
+}
+
+- (void)renderDisplayLayer {
+}
+
+@end
+
+@implementation GodotOpenGLLayer {
+ // The pixel dimensions of the backbuffer
+ GLint backingWidth;
+ GLint backingHeight;
+
+ EAGLContext *context;
+ GLuint viewRenderbuffer, viewFramebuffer;
+ GLuint depthRenderbuffer;
+}
+
+- (void)initializeDisplayLayer {
+ // Get our backing layer
+
+ // Configure it so that it is opaque, does not retain the contents of the backbuffer when displayed, and uses RGBA8888 color.
+ self.opaque = YES;
+ self.drawableProperties = [NSDictionary
+ dictionaryWithObjectsAndKeys:[NSNumber numberWithBool:FALSE],
+ kEAGLDrawablePropertyRetainedBacking,
+ kEAGLColorFormatRGBA8,
+ kEAGLDrawablePropertyColorFormat,
+ nil];
+
+ // FIXME: Add Vulkan support via MoltenVK. Add fallback code back?
+
+ // Create GL ES 2 context
+ if (GLOBAL_GET("rendering/quality/driver/driver_name") == "GLES2") {
+ context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2];
+ NSLog(@"Setting up an OpenGL ES 2.0 context.");
+ if (!context) {
+ NSLog(@"Failed to create OpenGL ES 2.0 context!");
+ return;
+ }
+ }
+
+ if (![EAGLContext setCurrentContext:context]) {
+ NSLog(@"Failed to set EAGLContext!");
+ return;
+ }
+ if (![self createFramebuffer]) {
+ NSLog(@"Failed to create frame buffer!");
+ return;
+ }
+}
+
+- (void)layoutDisplayLayer {
+ [EAGLContext setCurrentContext:context];
+ [self destroyFramebuffer];
+ [self createFramebuffer];
+}
+
+- (void)renderDisplayLayer {
+ [EAGLContext setCurrentContext:context];
+}
+
+- (void)dealloc {
+ if ([EAGLContext currentContext] == context) {
+ [EAGLContext setCurrentContext:nil];
+ }
+
+ if (context) {
+ [context release];
+ context = nil;
+ }
+
+ [super dealloc];
+}
+
+- (BOOL)createFramebuffer {
+ glGenFramebuffersOES(1, &viewFramebuffer);
+ glGenRenderbuffersOES(1, &viewRenderbuffer);
+
+ glBindFramebufferOES(GL_FRAMEBUFFER_OES, viewFramebuffer);
+ glBindRenderbufferOES(GL_RENDERBUFFER_OES, viewRenderbuffer);
+ // This call associates the storage for the current render buffer with the EAGLDrawable (our CAself)
+ // allowing us to draw into a buffer that will later be rendered to screen wherever the layer is (which corresponds with our view).
+ [context renderbufferStorage:GL_RENDERBUFFER_OES fromDrawable:(id<EAGLDrawable>)self];
+ glFramebufferRenderbufferOES(GL_FRAMEBUFFER_OES, GL_COLOR_ATTACHMENT0_OES, GL_RENDERBUFFER_OES, viewRenderbuffer);
+
+ glGetRenderbufferParameterivOES(GL_RENDERBUFFER_OES, GL_RENDERBUFFER_WIDTH_OES, &backingWidth);
+ glGetRenderbufferParameterivOES(GL_RENDERBUFFER_OES, GL_RENDERBUFFER_HEIGHT_OES, &backingHeight);
+
+ // For this sample, we also need a depth buffer, so we'll create and attach one via another renderbuffer.
+ glGenRenderbuffersOES(1, &depthRenderbuffer);
+ glBindRenderbufferOES(GL_RENDERBUFFER_OES, depthRenderbuffer);
+ glRenderbufferStorageOES(GL_RENDERBUFFER_OES, GL_DEPTH_COMPONENT16_OES, backingWidth, backingHeight);
+ glFramebufferRenderbufferOES(GL_FRAMEBUFFER_OES, GL_DEPTH_ATTACHMENT_OES, GL_RENDERBUFFER_OES, depthRenderbuffer);
+
+ if (glCheckFramebufferStatusOES(GL_FRAMEBUFFER_OES) != GL_FRAMEBUFFER_COMPLETE_OES) {
+ NSLog(@"failed to make complete framebuffer object %x", glCheckFramebufferStatusOES(GL_FRAMEBUFFER_OES));
+ return NO;
+ }
+
+ // if (OS::get_singleton()) {
+ // OS::VideoMode vm;
+ // vm.fullscreen = true;
+ // vm.width = backingWidth;
+ // vm.height = backingHeight;
+ // vm.resizable = false;
+ // OS::get_singleton()->set_video_mode(vm);
+ // OSIPhone::get_singleton()->set_base_framebuffer(viewFramebuffer);
+ // };
+ // gl_view_base_fb = viewFramebuffer;
+
+ return YES;
+}
+
+// Clean up any buffers we have allocated.
+- (void)destroyFramebuffer {
+ glDeleteFramebuffersOES(1, &viewFramebuffer);
+ viewFramebuffer = 0;
+ glDeleteRenderbuffersOES(1, &viewRenderbuffer);
+ viewRenderbuffer = 0;
+
+ if (depthRenderbuffer) {
+ glDeleteRenderbuffersOES(1, &depthRenderbuffer);
+ depthRenderbuffer = 0;
+ }
+}
+
+@end
diff --git a/platform/iphone/display_server_iphone.h b/platform/iphone/display_server_iphone.h
new file mode 100644
index 0000000000..e82cef4122
--- /dev/null
+++ b/platform/iphone/display_server_iphone.h
@@ -0,0 +1,202 @@
+/*************************************************************************/
+/* display_server_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 display_server_iphone_h
+#define display_server_iphone_h
+
+#include "core/input/input.h"
+#include "servers/display_server.h"
+
+#if defined(VULKAN_ENABLED)
+#include "drivers/vulkan/rendering_device_vulkan.h"
+#include "servers/rendering/rasterizer_rd/rasterizer_rd.h"
+
+#include "vulkan_context_iphone.h"
+
+#import <QuartzCore/CAMetalLayer.h>
+#include <vulkan/vulkan_metal.h>
+#endif
+
+class DisplayServerIPhone : public DisplayServer {
+ GDCLASS(DisplayServerIPhone, DisplayServer)
+
+ _THREAD_SAFE_CLASS_
+
+#if defined(VULKAN_ENABLED)
+ VulkanContextIPhone *context_vulkan;
+ RenderingDeviceVulkan *rendering_device_vulkan;
+#endif
+
+ DisplayServer::ScreenOrientation screen_orientation;
+
+ ObjectID window_attached_instance_id;
+
+ Callable window_event_callback;
+ Callable window_resize_callback;
+ Callable input_event_callback;
+ Callable input_text_callback;
+
+ int virtual_keyboard_height = 0;
+
+ void perform_event(const Ref<InputEvent> &p_event);
+
+ DisplayServerIPhone(const String &p_rendering_driver, DisplayServer::WindowMode p_mode, uint32_t p_flags, const Vector2i &p_resolution, Error &r_error);
+ ~DisplayServerIPhone();
+
+public:
+ String rendering_driver;
+
+ static DisplayServerIPhone *get_singleton();
+
+ static void register_iphone_driver();
+ static DisplayServer *create_func(const String &p_rendering_driver, WindowMode p_mode, uint32_t p_flags, const Vector2i &p_resolution, Error &r_error);
+ static Vector<String> get_rendering_drivers_func();
+
+ // MARK: - Events
+
+ virtual void process_events() override;
+
+ virtual void window_set_rect_changed_callback(const Callable &p_callable, WindowID p_window = MAIN_WINDOW_ID) override;
+ virtual void window_set_window_event_callback(const Callable &p_callable, WindowID p_window = MAIN_WINDOW_ID) override;
+ virtual void window_set_input_event_callback(const Callable &p_callable, WindowID p_window = MAIN_WINDOW_ID) override;
+ virtual void window_set_input_text_callback(const Callable &p_callable, WindowID p_window = MAIN_WINDOW_ID) override;
+ virtual void window_set_drop_files_callback(const Callable &p_callable, WindowID p_window = MAIN_WINDOW_ID) override;
+
+ static void _dispatch_input_events(const Ref<InputEvent> &p_event);
+ void send_input_event(const Ref<InputEvent> &p_event) const;
+ void send_input_text(const String &p_text) const;
+ void send_window_event(DisplayServer::WindowEvent p_event) const;
+ void _window_callback(const Callable &p_callable, const Variant &p_arg) const;
+
+ // MARK: - Input
+
+ // MARK: Touches
+
+ void touch_press(int p_idx, int p_x, int p_y, bool p_pressed, bool p_doubleclick);
+ void touch_drag(int p_idx, int p_prev_x, int p_prev_y, int p_x, int p_y);
+ void touches_cancelled(int p_idx);
+
+ // MARK: Keyboard
+
+ void key(uint32_t p_key, bool p_pressed);
+
+ // MARK: Motion
+
+ void update_gravity(float p_x, float p_y, float p_z);
+ void update_accelerometer(float p_x, float p_y, float p_z);
+ void update_magnetometer(float p_x, float p_y, float p_z);
+ void update_gyroscope(float p_x, float p_y, float p_z);
+
+ // MARK: -
+
+ virtual bool has_feature(Feature p_feature) const override;
+ virtual String get_name() const override;
+
+ virtual void alert(const String &p_alert, const String &p_title = "ALERT!") override;
+
+ virtual int get_screen_count() const override;
+ virtual Point2i screen_get_position(int p_screen = SCREEN_OF_MAIN_WINDOW) const override;
+ virtual Size2i screen_get_size(int p_screen = SCREEN_OF_MAIN_WINDOW) const override;
+ virtual Rect2i screen_get_usable_rect(int p_screen = SCREEN_OF_MAIN_WINDOW) const override;
+ virtual int screen_get_dpi(int p_screen = SCREEN_OF_MAIN_WINDOW) const override;
+ virtual float screen_get_scale(int p_screen = SCREEN_OF_MAIN_WINDOW) const override;
+
+ virtual Vector<DisplayServer::WindowID> get_window_list() const override;
+
+ virtual WindowID
+ get_window_at_screen_position(const Point2i &p_position) const override;
+
+ virtual void window_attach_instance_id(ObjectID p_instance, WindowID p_window = MAIN_WINDOW_ID) override;
+ virtual ObjectID window_get_attached_instance_id(WindowID p_window = MAIN_WINDOW_ID) const override;
+
+ virtual void window_set_title(const String &p_title, WindowID p_window = MAIN_WINDOW_ID) override;
+
+ virtual int window_get_current_screen(WindowID p_window = MAIN_WINDOW_ID) const override;
+ virtual void window_set_current_screen(int p_screen, WindowID p_window = MAIN_WINDOW_ID) override;
+
+ virtual Point2i window_get_position(WindowID p_window = MAIN_WINDOW_ID) const override;
+ virtual void window_set_position(const Point2i &p_position, WindowID p_window = MAIN_WINDOW_ID) override;
+
+ virtual void window_set_transient(WindowID p_window, WindowID p_parent) override;
+
+ virtual void window_set_max_size(const Size2i p_size, WindowID p_window = MAIN_WINDOW_ID) override;
+ virtual Size2i window_get_max_size(WindowID p_window = MAIN_WINDOW_ID) const override;
+
+ virtual void window_set_min_size(const Size2i p_size, WindowID p_window = MAIN_WINDOW_ID) override;
+ virtual Size2i window_get_min_size(WindowID p_window = MAIN_WINDOW_ID) const override;
+
+ virtual void window_set_size(const Size2i p_size, WindowID p_window = MAIN_WINDOW_ID) override;
+ virtual Size2i window_get_size(WindowID p_window = MAIN_WINDOW_ID) const override;
+ virtual Size2i window_get_real_size(WindowID p_window = MAIN_WINDOW_ID) const override;
+
+ virtual void window_set_mode(WindowMode p_mode, WindowID p_window = MAIN_WINDOW_ID) override;
+ virtual WindowMode window_get_mode(WindowID p_window = MAIN_WINDOW_ID) const override;
+
+ virtual bool window_is_maximize_allowed(WindowID p_window = MAIN_WINDOW_ID) const override;
+
+ virtual void window_set_flag(WindowFlags p_flag, bool p_enabled, WindowID p_window = MAIN_WINDOW_ID) override;
+ virtual bool window_get_flag(WindowFlags p_flag, WindowID p_window = MAIN_WINDOW_ID) const override;
+
+ virtual void window_request_attention(WindowID p_window = MAIN_WINDOW_ID) override;
+ virtual void window_move_to_foreground(WindowID p_window = MAIN_WINDOW_ID) override;
+
+ virtual float screen_get_max_scale() const override;
+
+ virtual void screen_set_orientation(DisplayServer::ScreenOrientation p_orientation, int p_screen) override;
+ virtual DisplayServer::ScreenOrientation screen_get_orientation(int p_screen) const override;
+
+ virtual bool window_can_draw(WindowID p_window = MAIN_WINDOW_ID) const override;
+
+ virtual bool can_any_window_draw() const override;
+
+ virtual bool screen_is_touchscreen(int p_screen) const override;
+
+ virtual void virtual_keyboard_show(const String &p_existing_text, const Rect2 &p_screen_rect, int p_max_length, int p_cursor_start, int p_cursor_end) override;
+ virtual void virtual_keyboard_hide() override;
+
+ void virtual_keyboard_set_height(int height);
+ virtual int virtual_keyboard_get_height() const override;
+
+ virtual void clipboard_set(const String &p_text) override;
+ virtual String clipboard_get() const override;
+
+ virtual void screen_set_keep_on(bool p_enable) override;
+ virtual bool screen_is_kept_on() const override;
+
+ virtual Error native_video_play(String p_path, float p_volume, String p_audio_track, String p_subtitle_track, int p_screen = SCREEN_OF_MAIN_WINDOW) override;
+ virtual bool native_video_is_playing() const override;
+ virtual void native_video_pause() override;
+ virtual void native_video_unpause() override;
+ virtual void native_video_stop() override;
+
+ void resize_window(CGSize size);
+};
+
+#endif /* display_server_iphone_h */
diff --git a/platform/iphone/display_server_iphone.mm b/platform/iphone/display_server_iphone.mm
new file mode 100644
index 0000000000..498be89e48
--- /dev/null
+++ b/platform/iphone/display_server_iphone.mm
@@ -0,0 +1,751 @@
+/*************************************************************************/
+/* display_server_iphone.mm */
+/*************************************************************************/
+/* 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 "display_server_iphone.h"
+#import "app_delegate.h"
+#include "core/io/file_access_pack.h"
+#include "core/project_settings.h"
+#import "godot_view.h"
+#include "ios.h"
+#include "os_iphone.h"
+#import "view_controller.h"
+
+#import <Foundation/Foundation.h>
+#import <sys/utsname.h>
+
+static const float kDisplayServerIPhoneAcceleration = 1;
+static NSDictionary *iOSModelToDPI = @{
+ @[
+ @"iPad1,1",
+ @"iPad2,1",
+ @"iPad2,2",
+ @"iPad2,3",
+ @"iPad2,4",
+ ] : @132,
+ @[
+ @"iPhone1,1",
+ @"iPhone1,2",
+ @"iPhone2,1",
+ @"iPad2,5",
+ @"iPad2,6",
+ @"iPad2,7",
+ @"iPod1,1",
+ @"iPod2,1",
+ @"iPod3,1",
+ ] : @163,
+ @[
+ @"iPad3,1",
+ @"iPad3,2",
+ @"iPad3,3",
+ @"iPad3,4",
+ @"iPad3,5",
+ @"iPad3,6",
+ @"iPad4,1",
+ @"iPad4,2",
+ @"iPad4,3",
+ @"iPad5,3",
+ @"iPad5,4",
+ @"iPad6,3",
+ @"iPad6,4",
+ @"iPad6,7",
+ @"iPad6,8",
+ @"iPad6,11",
+ @"iPad6,12",
+ @"iPad7,1",
+ @"iPad7,2",
+ @"iPad7,3",
+ @"iPad7,4",
+ @"iPad7,5",
+ @"iPad7,6",
+ @"iPad7,11",
+ @"iPad7,12",
+ @"iPad8,1",
+ @"iPad8,2",
+ @"iPad8,3",
+ @"iPad8,4",
+ @"iPad8,5",
+ @"iPad8,6",
+ @"iPad8,7",
+ @"iPad8,8",
+ @"iPad8,9",
+ @"iPad8,10",
+ @"iPad8,11",
+ @"iPad8,12",
+ @"iPad11,3",
+ @"iPad11,4",
+ ] : @264,
+ @[
+ @"iPhone3,1",
+ @"iPhone3,2",
+ @"iPhone3,3",
+ @"iPhone4,1",
+ @"iPhone5,1",
+ @"iPhone5,2",
+ @"iPhone5,3",
+ @"iPhone5,4",
+ @"iPhone6,1",
+ @"iPhone6,2",
+ @"iPhone7,2",
+ @"iPhone8,1",
+ @"iPhone8,4",
+ @"iPhone9,1",
+ @"iPhone9,3",
+ @"iPhone10,1",
+ @"iPhone10,4",
+ @"iPhone11,8",
+ @"iPhone12,1",
+ @"iPhone12,8",
+ @"iPad4,4",
+ @"iPad4,5",
+ @"iPad4,6",
+ @"iPad4,7",
+ @"iPad4,8",
+ @"iPad4,9",
+ @"iPad5,1",
+ @"iPad5,2",
+ @"iPad11,1",
+ @"iPad11,2",
+ @"iPod4,1",
+ @"iPod5,1",
+ @"iPod7,1",
+ @"iPod9,1",
+ ] : @326,
+ @[
+ @"iPhone7,1",
+ @"iPhone8,2",
+ @"iPhone9,2",
+ @"iPhone9,4",
+ @"iPhone10,2",
+ @"iPhone10,5",
+ ] : @401,
+ @[
+ @"iPhone10,3",
+ @"iPhone10,6",
+ @"iPhone11,2",
+ @"iPhone11,4",
+ @"iPhone11,6",
+ @"iPhone12,3",
+ @"iPhone12,5",
+ ] : @458,
+};
+
+DisplayServerIPhone *DisplayServerIPhone::get_singleton() {
+ return (DisplayServerIPhone *)DisplayServer::get_singleton();
+}
+
+DisplayServerIPhone::DisplayServerIPhone(const String &p_rendering_driver, DisplayServer::WindowMode p_mode, uint32_t p_flags, const Vector2i &p_resolution, Error &r_error) {
+ rendering_driver = p_rendering_driver;
+
+#if defined(OPENGL_ENABLED)
+ // FIXME: Add support for both GLES2 and Vulkan when GLES2 is implemented
+ // again,
+
+ if (rendering_driver == "opengl_es") {
+ bool gl_initialization_error = false;
+
+ // FIXME: Add Vulkan support via MoltenVK. Add fallback code back?
+
+ 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.", "Unable to initialize video driver");
+ // return ERR_UNAVAILABLE;
+ }
+
+ // rendering_server = memnew(RenderingServerRaster);
+ // // FIXME: Reimplement threaded rendering
+ // if (get_render_thread_mode() != RENDER_THREAD_UNSAFE) {
+ // rendering_server = memnew(RenderingServerWrapMT(rendering_server,
+ // false));
+ // }
+ // rendering_server->init();
+ // rendering_server->cursor_set_visible(false, 0);
+
+ // reset this to what it should be, it will have been set to 0 after
+ // rendering_server->init() is called
+ // RasterizerStorageGLES2::system_fbo = gl_view_base_fb;
+ }
+#endif
+
+#if defined(VULKAN_ENABLED)
+ rendering_driver = "vulkan";
+
+ context_vulkan = nullptr;
+ rendering_device_vulkan = nullptr;
+
+ if (rendering_driver == "vulkan") {
+ context_vulkan = memnew(VulkanContextIPhone);
+ if (context_vulkan->initialize() != OK) {
+ memdelete(context_vulkan);
+ context_vulkan = nullptr;
+ ERR_FAIL_MSG("Failed to initialize Vulkan context");
+ }
+
+ CALayer *layer = [AppDelegate.viewController.godotView initializeRenderingForDriver:@"vulkan"];
+
+ if (!layer) {
+ ERR_FAIL_MSG("Failed to create iOS rendering layer.");
+ }
+
+ Size2i size = Size2i(layer.bounds.size.width, layer.bounds.size.height) * screen_get_max_scale();
+ if (context_vulkan->window_create(MAIN_WINDOW_ID, layer, size.width, size.height) != OK) {
+ memdelete(context_vulkan);
+ context_vulkan = nullptr;
+ ERR_FAIL_MSG("Failed to create Vulkan window.");
+ }
+
+ rendering_device_vulkan = memnew(RenderingDeviceVulkan);
+ rendering_device_vulkan->initialize(context_vulkan);
+
+ RasterizerRD::make_current();
+ }
+#endif
+
+ bool keep_screen_on = bool(GLOBAL_DEF("display/window/energy_saving/keep_screen_on", true));
+ screen_set_keep_on(keep_screen_on);
+
+ Input::get_singleton()->set_event_dispatch_function(_dispatch_input_events);
+
+ r_error = OK;
+}
+
+DisplayServerIPhone::~DisplayServerIPhone() {
+#if defined(VULKAN_ENABLED)
+ if (rendering_driver == "vulkan") {
+ if (rendering_device_vulkan) {
+ rendering_device_vulkan->finalize();
+ memdelete(rendering_device_vulkan);
+ rendering_device_vulkan = NULL;
+ }
+
+ if (context_vulkan) {
+ context_vulkan->window_destroy(MAIN_WINDOW_ID);
+ memdelete(context_vulkan);
+ context_vulkan = NULL;
+ }
+ }
+#endif
+}
+
+DisplayServer *DisplayServerIPhone::create_func(const String &p_rendering_driver, WindowMode p_mode, uint32_t p_flags, const Vector2i &p_resolution, Error &r_error) {
+ return memnew(DisplayServerIPhone(p_rendering_driver, p_mode, p_flags, p_resolution, r_error));
+}
+
+Vector<String> DisplayServerIPhone::get_rendering_drivers_func() {
+ Vector<String> drivers;
+
+#if defined(VULKAN_ENABLED)
+ drivers.push_back("vulkan");
+#endif
+#if defined(OPENGL_ENABLED)
+ drivers.push_back("opengl_es");
+#endif
+
+ return drivers;
+}
+
+void DisplayServerIPhone::register_iphone_driver() {
+ register_create_function("iphone", create_func, get_rendering_drivers_func);
+}
+
+// MARK: Events
+
+void DisplayServerIPhone::window_set_rect_changed_callback(const Callable &p_callable, WindowID p_window) {
+ window_resize_callback = p_callable;
+}
+
+void DisplayServerIPhone::window_set_window_event_callback(const Callable &p_callable, WindowID p_window) {
+ window_event_callback = p_callable;
+}
+void DisplayServerIPhone::window_set_input_event_callback(const Callable &p_callable, WindowID p_window) {
+ input_event_callback = p_callable;
+}
+
+void DisplayServerIPhone::window_set_input_text_callback(const Callable &p_callable, WindowID p_window) {
+ input_text_callback = p_callable;
+}
+
+void DisplayServerIPhone::window_set_drop_files_callback(const Callable &p_callable, WindowID p_window) {
+ // Probably not supported for iOS
+}
+
+void DisplayServerIPhone::process_events() {
+}
+
+void DisplayServerIPhone::_dispatch_input_events(const Ref<InputEvent> &p_event) {
+ DisplayServerIPhone::get_singleton()->send_input_event(p_event);
+}
+
+void DisplayServerIPhone::send_input_event(const Ref<InputEvent> &p_event) const {
+ _window_callback(input_event_callback, p_event);
+}
+
+void DisplayServerIPhone::send_input_text(const String &p_text) const {
+ _window_callback(input_text_callback, p_text);
+}
+
+void DisplayServerIPhone::send_window_event(DisplayServer::WindowEvent p_event) const {
+ _window_callback(window_event_callback, int(p_event));
+}
+
+void DisplayServerIPhone::_window_callback(const Callable &p_callable, const Variant &p_arg) const {
+ if (!p_callable.is_null()) {
+ const Variant *argp = &p_arg;
+ Variant ret;
+ Callable::CallError ce;
+ p_callable.call((const Variant **)&argp, 1, ret, ce);
+ }
+}
+
+// MARK: - Input
+
+// MARK: Touches
+
+void DisplayServerIPhone::touch_press(int p_idx, int p_x, int p_y, bool p_pressed, bool p_doubleclick) {
+ if (!GLOBAL_DEF("debug/disable_touch", false)) {
+ Ref<InputEventScreenTouch> ev;
+ ev.instance();
+
+ ev->set_index(p_idx);
+ ev->set_pressed(p_pressed);
+ ev->set_position(Vector2(p_x, p_y));
+ perform_event(ev);
+ }
+};
+
+void DisplayServerIPhone::touch_drag(int p_idx, int p_prev_x, int p_prev_y, int p_x, int p_y) {
+ if (!GLOBAL_DEF("debug/disable_touch", false)) {
+ Ref<InputEventScreenDrag> ev;
+ ev.instance();
+ ev->set_index(p_idx);
+ ev->set_position(Vector2(p_x, p_y));
+ ev->set_relative(Vector2(p_x - p_prev_x, p_y - p_prev_y));
+ perform_event(ev);
+ };
+};
+
+void DisplayServerIPhone::perform_event(const Ref<InputEvent> &p_event) {
+ Input::get_singleton()->parse_input_event(p_event);
+};
+
+void DisplayServerIPhone::touches_cancelled(int p_idx) {
+ touch_press(p_idx, -1, -1, false, false);
+};
+
+// MARK: Keyboard
+
+void DisplayServerIPhone::key(uint32_t p_key, bool p_pressed) {
+ Ref<InputEventKey> ev;
+ ev.instance();
+ ev->set_echo(false);
+ ev->set_pressed(p_pressed);
+ ev->set_keycode(p_key);
+ ev->set_physical_keycode(p_key);
+ ev->set_unicode(p_key);
+ perform_event(ev);
+};
+
+// MARK: Motion
+
+void DisplayServerIPhone::update_gravity(float p_x, float p_y, float p_z) {
+ Input::get_singleton()->set_gravity(Vector3(p_x, p_y, p_z));
+};
+
+void DisplayServerIPhone::update_accelerometer(float p_x, float p_y,
+ float p_z) {
+ // Found out the Z should not be negated! Pass as is!
+ Vector3 v_accelerometer = Vector3(
+ p_x / kDisplayServerIPhoneAcceleration,
+ p_y / kDisplayServerIPhoneAcceleration,
+ p_z / kDisplayServerIPhoneAcceleration);
+
+ Input::get_singleton()->set_accelerometer(v_accelerometer);
+
+ /*
+ if (p_x != last_accel.x) {
+ //printf("updating accel x %f\n", p_x);
+ InputEvent ev;
+ ev.type = InputEvent::JOYPAD_MOTION;
+ ev.device = 0;
+ ev.joy_motion.axis = JOY_ANALOG_0;
+ ev.joy_motion.axis_value = (p_x / (float)ACCEL_RANGE);
+ last_accel.x = p_x;
+ queue_event(ev);
+ };
+ if (p_y != last_accel.y) {
+ //printf("updating accel y %f\n", p_y);
+ InputEvent ev;
+ ev.type = InputEvent::JOYPAD_MOTION;
+ ev.device = 0;
+ ev.joy_motion.axis = JOY_ANALOG_1;
+ ev.joy_motion.axis_value = (p_y / (float)ACCEL_RANGE);
+ last_accel.y = p_y;
+ queue_event(ev);
+ };
+ if (p_z != last_accel.z) {
+ //printf("updating accel z %f\n", p_z);
+ InputEvent ev;
+ ev.type = InputEvent::JOYPAD_MOTION;
+ ev.device = 0;
+ ev.joy_motion.axis = JOY_ANALOG_2;
+ ev.joy_motion.axis_value = ( (1.0 - p_z) / (float)ACCEL_RANGE);
+ last_accel.z = p_z;
+ queue_event(ev);
+ };
+ */
+};
+
+void DisplayServerIPhone::update_magnetometer(float p_x, float p_y, float p_z) {
+ Input::get_singleton()->set_magnetometer(Vector3(p_x, p_y, p_z));
+};
+
+void DisplayServerIPhone::update_gyroscope(float p_x, float p_y, float p_z) {
+ Input::get_singleton()->set_gyroscope(Vector3(p_x, p_y, p_z));
+};
+
+// MARK: -
+
+bool DisplayServerIPhone::has_feature(Feature p_feature) const {
+ switch (p_feature) {
+ // case FEATURE_CONSOLE_WINDOW:
+ // case FEATURE_CURSOR_SHAPE:
+ // case FEATURE_CUSTOM_CURSOR_SHAPE:
+ // case FEATURE_GLOBAL_MENU:
+ // case FEATURE_HIDPI:
+ // case FEATURE_ICON:
+ // case FEATURE_IME:
+ // case FEATURE_MOUSE:
+ // case FEATURE_MOUSE_WARP:
+ // case FEATURE_NATIVE_DIALOG:
+ // case FEATURE_NATIVE_ICON:
+ // case FEATURE_NATIVE_VIDEO:
+ // case FEATURE_WINDOW_TRANSPARENCY:
+ case FEATURE_CLIPBOARD:
+ case FEATURE_KEEP_SCREEN_ON:
+ case FEATURE_ORIENTATION:
+ case FEATURE_TOUCHSCREEN:
+ case FEATURE_VIRTUAL_KEYBOARD:
+ return true;
+ default:
+ return false;
+ }
+}
+
+String DisplayServerIPhone::get_name() const {
+ return "iPhone";
+}
+
+void DisplayServerIPhone::alert(const String &p_alert, const String &p_title) {
+ const CharString utf8_alert = p_alert.utf8();
+ const CharString utf8_title = p_title.utf8();
+ iOS::alert(utf8_alert.get_data(), utf8_title.get_data());
+}
+
+int DisplayServerIPhone::get_screen_count() const {
+ return 1;
+}
+
+Point2i DisplayServerIPhone::screen_get_position(int p_screen) const {
+ return Size2i();
+}
+
+Size2i DisplayServerIPhone::screen_get_size(int p_screen) const {
+ CALayer *layer = AppDelegate.viewController.godotView.renderingLayer;
+
+ if (!layer) {
+ return Size2i();
+ }
+
+ return Size2i(layer.bounds.size.width, layer.bounds.size.height) * screen_get_scale(p_screen);
+}
+
+Rect2i DisplayServerIPhone::screen_get_usable_rect(int p_screen) const {
+ return Rect2i(screen_get_position(p_screen), screen_get_size(p_screen));
+}
+
+int DisplayServerIPhone::screen_get_dpi(int p_screen) const {
+ struct utsname systemInfo;
+ uname(&systemInfo);
+
+ NSString *string = [NSString stringWithCString:systemInfo.machine encoding:NSUTF8StringEncoding];
+
+ for (NSArray *keyArray in iOSModelToDPI) {
+ if ([keyArray containsObject:string]) {
+ NSNumber *value = iOSModelToDPI[keyArray];
+ return [value intValue];
+ }
+ }
+
+ return 163;
+}
+
+float DisplayServerIPhone::screen_get_scale(int p_screen) const {
+ return [UIScreen mainScreen].nativeScale;
+}
+
+Vector<DisplayServer::WindowID> DisplayServerIPhone::get_window_list() const {
+ Vector<DisplayServer::WindowID> list;
+ list.push_back(MAIN_WINDOW_ID);
+ return list;
+}
+
+DisplayServer::WindowID DisplayServerIPhone::get_window_at_screen_position(const Point2i &p_position) const {
+ return MAIN_WINDOW_ID;
+}
+
+void DisplayServerIPhone::window_attach_instance_id(ObjectID p_instance, WindowID p_window) {
+ window_attached_instance_id = p_instance;
+}
+
+ObjectID DisplayServerIPhone::window_get_attached_instance_id(WindowID p_window) const {
+ return window_attached_instance_id;
+}
+
+void DisplayServerIPhone::window_set_title(const String &p_title, WindowID p_window) {
+ // Probably not supported for iOS
+}
+
+int DisplayServerIPhone::window_get_current_screen(WindowID p_window) const {
+ return SCREEN_OF_MAIN_WINDOW;
+}
+
+void DisplayServerIPhone::window_set_current_screen(int p_screen, WindowID p_window) {
+ // Probably not supported for iOS
+}
+
+Point2i DisplayServerIPhone::window_get_position(WindowID p_window) const {
+ return Point2i();
+}
+
+void DisplayServerIPhone::window_set_position(const Point2i &p_position, WindowID p_window) {
+ // Probably not supported for single window iOS app
+}
+
+void DisplayServerIPhone::window_set_transient(WindowID p_window, WindowID p_parent) {
+ // Probably not supported for iOS
+}
+
+void DisplayServerIPhone::window_set_max_size(const Size2i p_size, WindowID p_window) {
+ // Probably not supported for iOS
+}
+
+Size2i DisplayServerIPhone::window_get_max_size(WindowID p_window) const {
+ return Size2i();
+}
+
+void DisplayServerIPhone::window_set_min_size(const Size2i p_size, WindowID p_window) {
+ // Probably not supported for iOS
+}
+
+Size2i DisplayServerIPhone::window_get_min_size(WindowID p_window) const {
+ return Size2i();
+}
+
+void DisplayServerIPhone::window_set_size(const Size2i p_size, WindowID p_window) {
+ // Probably not supported for iOS
+}
+
+Size2i DisplayServerIPhone::window_get_size(WindowID p_window) const {
+ CGRect screenBounds = [UIScreen mainScreen].bounds;
+ return Size2i(screenBounds.size.width, screenBounds.size.height) * screen_get_max_scale();
+}
+
+Size2i DisplayServerIPhone::window_get_real_size(WindowID p_window) const {
+ return window_get_size(p_window);
+}
+
+void DisplayServerIPhone::window_set_mode(WindowMode p_mode, WindowID p_window) {
+ // Probably not supported for iOS
+}
+
+DisplayServer::WindowMode DisplayServerIPhone::window_get_mode(WindowID p_window) const {
+ return WindowMode::WINDOW_MODE_FULLSCREEN;
+}
+
+bool DisplayServerIPhone::window_is_maximize_allowed(WindowID p_window) const {
+ return false;
+}
+
+void DisplayServerIPhone::window_set_flag(WindowFlags p_flag, bool p_enabled, WindowID p_window) {
+ // Probably not supported for iOS
+}
+
+bool DisplayServerIPhone::window_get_flag(WindowFlags p_flag, WindowID p_window) const {
+ return false;
+}
+
+void DisplayServerIPhone::window_request_attention(WindowID p_window) {
+ // Probably not supported for iOS
+}
+
+void DisplayServerIPhone::window_move_to_foreground(WindowID p_window) {
+ // Probably not supported for iOS
+}
+
+float DisplayServerIPhone::screen_get_max_scale() const {
+ return screen_get_scale(SCREEN_OF_MAIN_WINDOW);
+};
+
+void DisplayServerIPhone::screen_set_orientation(DisplayServer::ScreenOrientation p_orientation, int p_screen) {
+ screen_orientation = p_orientation;
+}
+
+DisplayServer::ScreenOrientation DisplayServerIPhone::screen_get_orientation(int p_screen) const {
+ return screen_orientation;
+}
+
+bool DisplayServerIPhone::window_can_draw(WindowID p_window) const {
+ return true;
+}
+
+bool DisplayServerIPhone::can_any_window_draw() const {
+ return true;
+}
+
+bool DisplayServerIPhone::screen_is_touchscreen(int p_screen) const {
+ return true;
+}
+
+void DisplayServerIPhone::virtual_keyboard_show(const String &p_existing_text, const Rect2 &p_screen_rect, int p_max_length, int p_cursor_start, int p_cursor_end) {
+ [AppDelegate.viewController.godotView becomeFirstResponderWithString:p_existing_text];
+}
+
+void DisplayServerIPhone::virtual_keyboard_hide() {
+ [AppDelegate.viewController.godotView resignFirstResponder];
+}
+
+void DisplayServerIPhone::virtual_keyboard_set_height(int height) {
+ virtual_keyboard_height = height * screen_get_max_scale();
+}
+
+int DisplayServerIPhone::virtual_keyboard_get_height() const {
+ return virtual_keyboard_height;
+}
+
+void DisplayServerIPhone::clipboard_set(const String &p_text) {
+ [UIPasteboard generalPasteboard].string = [NSString stringWithUTF8String:p_text.utf8()];
+}
+
+String DisplayServerIPhone::clipboard_get() const {
+ NSString *text = [UIPasteboard generalPasteboard].string;
+
+ return String::utf8([text UTF8String]);
+}
+
+void DisplayServerIPhone::screen_set_keep_on(bool p_enable) {
+ [UIApplication sharedApplication].idleTimerDisabled = p_enable;
+}
+
+bool DisplayServerIPhone::screen_is_kept_on() const {
+ return [UIApplication sharedApplication].idleTimerDisabled;
+}
+
+Error DisplayServerIPhone::native_video_play(String p_path, float p_volume, String p_audio_track, String p_subtitle_track, int p_screen) {
+ FileAccess *f = FileAccess::open(p_path, FileAccess::READ);
+ bool exists = f && f->is_open();
+
+ String user_data_dir = OSIPhone::get_singleton()->get_user_data_dir();
+
+ if (!exists) {
+ return FAILED;
+ }
+
+ String tempFile = OSIPhone::get_singleton()->get_user_data_dir();
+
+ if (p_path.begins_with("res://")) {
+ if (PackedData::get_singleton()->has_path(p_path)) {
+ printf("Unable to play %S using the native player as it resides in a .pck file\n", p_path.c_str());
+ return ERR_INVALID_PARAMETER;
+ } else {
+ p_path = p_path.replace("res:/", ProjectSettings::get_singleton()->get_resource_path());
+ }
+ } else if (p_path.begins_with("user://")) {
+ p_path = p_path.replace("user:/", user_data_dir);
+ }
+
+ memdelete(f);
+
+ printf("Playing video: %S\n", p_path.c_str());
+
+ String file_path = ProjectSettings::get_singleton()->globalize_path(p_path);
+
+ NSString *filePath = [[[NSString alloc] initWithUTF8String:file_path.utf8().get_data()] autorelease];
+ NSString *audioTrack = [NSString stringWithUTF8String:p_audio_track.utf8()];
+ NSString *subtitleTrack = [NSString stringWithUTF8String:p_subtitle_track.utf8()];
+
+ if (![AppDelegate.viewController playVideoAtPath:filePath
+ volume:p_volume
+ audio:audioTrack
+ subtitle:subtitleTrack]) {
+ return OK;
+ }
+
+ return FAILED;
+}
+
+bool DisplayServerIPhone::native_video_is_playing() const {
+ return [AppDelegate.viewController isVideoPlaying];
+}
+
+void DisplayServerIPhone::native_video_pause() {
+ if (native_video_is_playing()) {
+ [AppDelegate.viewController pauseVideo];
+ }
+}
+
+void DisplayServerIPhone::native_video_unpause() {
+ [AppDelegate.viewController unpauseVideo];
+};
+
+void DisplayServerIPhone::native_video_stop() {
+ if (native_video_is_playing()) {
+ [AppDelegate.viewController stopVideo];
+ }
+}
+
+void DisplayServerIPhone::resize_window(CGSize viewSize) {
+ Size2i size = Size2i(viewSize.width, viewSize.height) * screen_get_max_scale();
+
+#if defined(VULKAN_ENABLED)
+ if (rendering_driver == "vulkan") {
+ if (context_vulkan) {
+ context_vulkan->window_resize(MAIN_WINDOW_ID, size.x, size.y);
+ }
+ }
+#endif
+
+ Variant resize_rect = Rect2i(Point2i(), size);
+ _window_callback(window_resize_callback, resize_rect);
+}
diff --git a/platform/iphone/game_center.h b/platform/iphone/game_center.h
index 0d3ef5b696..1e9a68fe48 100644
--- a/platform/iphone/game_center.h
+++ b/platform/iphone/game_center.h
@@ -51,12 +51,12 @@ public:
void connect();
bool is_authenticated();
- Error post_score(Variant p_score);
- Error award_achievement(Variant p_params);
+ Error post_score(Dictionary p_score);
+ Error award_achievement(Dictionary p_params);
void reset_achievements();
void request_achievements();
void request_achievement_descriptions();
- Error show_game_center(Variant p_params);
+ Error show_game_center(Dictionary p_params);
Error request_identity_verification_signature();
void game_center_closed();
diff --git a/platform/iphone/game_center.mm b/platform/iphone/game_center.mm
index 8d470da1a8..4481775c32 100644
--- a/platform/iphone/game_center.mm
+++ b/platform/iphone/game_center.mm
@@ -47,13 +47,15 @@ extern "C" {
#import "app_delegate.h"
};
+#import "view_controller.h"
+
GameCenter *GameCenter::instance = NULL;
void GameCenter::_bind_methods() {
ClassDB::bind_method(D_METHOD("is_authenticated"), &GameCenter::is_authenticated);
ClassDB::bind_method(D_METHOD("post_score"), &GameCenter::post_score);
- ClassDB::bind_method(D_METHOD("award_achievement"), &GameCenter::award_achievement);
+ ClassDB::bind_method(D_METHOD("award_achievement", "achievement"), &GameCenter::award_achievement);
ClassDB::bind_method(D_METHOD("reset_achievements"), &GameCenter::reset_achievements);
ClassDB::bind_method(D_METHOD("request_achievements"), &GameCenter::request_achievements);
ClassDB::bind_method(D_METHOD("request_achievement_descriptions"), &GameCenter::request_achievement_descriptions);
@@ -105,7 +107,14 @@ void GameCenter::connect() {
ret["type"] = "authentication";
if (player.isAuthenticated) {
ret["result"] = "ok";
- ret["player_id"] = [player.playerID UTF8String];
+ if (@available(iOS 13, *)) {
+ ret["player_id"] = [player.teamPlayerID UTF8String];
+#if !defined(TARGET_OS_SIMULATOR) || !TARGET_OS_SIMULATOR
+ } else {
+ ret["player_id"] = [player.playerID UTF8String];
+#endif
+ }
+
GameCenter::get_singleton()->authenticated = true;
} else {
ret["result"] = "error";
@@ -123,11 +132,10 @@ bool GameCenter::is_authenticated() {
return authenticated;
};
-Error GameCenter::post_score(Variant p_score) {
- Dictionary params = p_score;
- ERR_FAIL_COND_V(!params.has("score") || !params.has("category"), ERR_INVALID_PARAMETER);
- float score = params["score"];
- String category = params["category"];
+Error GameCenter::post_score(Dictionary p_score) {
+ ERR_FAIL_COND_V(!p_score.has("score") || !p_score.has("category"), ERR_INVALID_PARAMETER);
+ float score = p_score["score"];
+ String category = p_score["category"];
NSString *cat_str = [[[NSString alloc] initWithUTF8String:category.utf8().get_data()] autorelease];
GKScore *reporter = [[[GKScore alloc] initWithLeaderboardIdentifier:cat_str] autorelease];
@@ -153,11 +161,10 @@ Error GameCenter::post_score(Variant p_score) {
return OK;
};
-Error GameCenter::award_achievement(Variant p_params) {
- Dictionary params = p_params;
- ERR_FAIL_COND_V(!params.has("name") || !params.has("progress"), ERR_INVALID_PARAMETER);
- String name = params["name"];
- float progress = params["progress"];
+Error GameCenter::award_achievement(Dictionary p_params) {
+ ERR_FAIL_COND_V(!p_params.has("name") || !p_params.has("progress"), ERR_INVALID_PARAMETER);
+ String name = p_params["name"];
+ float progress = p_params["progress"];
NSString *name_str = [[[NSString alloc] initWithUTF8String:name.utf8().get_data()] autorelease];
GKAchievement *achievement = [[[GKAchievement alloc] initWithIdentifier:name_str] autorelease];
@@ -167,8 +174,8 @@ Error GameCenter::award_achievement(Variant p_params) {
achievement.percentComplete = progress;
achievement.showsCompletionBanner = NO;
- if (params.has("show_completion_banner")) {
- achievement.showsCompletionBanner = params["show_completion_banner"] ? YES : NO;
+ if (p_params.has("show_completion_banner")) {
+ achievement.showsCompletionBanner = p_params["show_completion_banner"] ? YES : NO;
}
[GKAchievement reportAchievements:@[ achievement ]
@@ -202,7 +209,7 @@ void GameCenter::request_achievement_descriptions() {
Array hidden;
Array replayable;
- for (int i = 0; i < [descriptions count]; i++) {
+ for (NSUInteger i = 0; i < [descriptions count]; i++) {
GKAchievementDescription *description = [descriptions objectAtIndex:i];
const char *str = [description.identifier UTF8String];
@@ -250,7 +257,7 @@ void GameCenter::request_achievements() {
PackedStringArray names;
PackedFloat32Array percentages;
- for (int i = 0; i < [achievements count]; i++) {
+ for (NSUInteger i = 0; i < [achievements count]; i++) {
GKAchievement *achievement = [achievements objectAtIndex:i];
const char *str = [achievement.identifier UTF8String];
names.push_back(String::utf8(str != NULL ? str : ""));
@@ -285,14 +292,12 @@ void GameCenter::reset_achievements() {
}];
};
-Error GameCenter::show_game_center(Variant p_params) {
+Error GameCenter::show_game_center(Dictionary p_params) {
ERR_FAIL_COND_V(!NSProtocolFromString(@"GKGameCenterControllerDelegate"), FAILED);
- Dictionary params = p_params;
-
GKGameCenterViewControllerState view_state = GKGameCenterViewControllerStateDefault;
- if (params.has("view")) {
- String view_name = params["view"];
+ if (p_params.has("view")) {
+ String view_name = p_params["view"];
if (view_name == "default") {
view_state = GKGameCenterViewControllerStateDefault;
} else if (view_name == "leaderboards") {
@@ -306,7 +311,7 @@ Error GameCenter::show_game_center(Variant p_params) {
}
}
- GKGameCenterViewController *controller = [[GKGameCenterViewController alloc] init];
+ GKGameCenterViewController *controller = [[[GKGameCenterViewController alloc] init] autorelease];
ERR_FAIL_COND_V(!controller, FAILED);
ViewController *root_controller = (ViewController *)((AppDelegate *)[[UIApplication sharedApplication] delegate]).window.rootViewController;
@@ -316,8 +321,8 @@ Error GameCenter::show_game_center(Variant p_params) {
controller.viewState = view_state;
if (view_state == GKGameCenterViewControllerStateLeaderboards) {
controller.leaderboardIdentifier = nil;
- if (params.has("leaderboard_name")) {
- String name = params["leaderboard_name"];
+ if (p_params.has("leaderboard_name")) {
+ String name = p_params["leaderboard_name"];
NSString *name_str = [[[NSString alloc] initWithUTF8String:name.utf8().get_data()] autorelease];
controller.leaderboardIdentifier = name_str;
}
@@ -341,7 +346,13 @@ Error GameCenter::request_identity_verification_signature() {
ret["signature"] = [[signature base64EncodedStringWithOptions:0] UTF8String];
ret["salt"] = [[salt base64EncodedStringWithOptions:0] UTF8String];
ret["timestamp"] = timestamp;
- ret["player_id"] = [player.playerID UTF8String];
+ if (@available(iOS 13, *)) {
+ ret["player_id"] = [player.teamPlayerID UTF8String];
+#if !defined(TARGET_OS_SIMULATOR) || !TARGET_OS_SIMULATOR
+ } else {
+ ret["player_id"] = [player.playerID UTF8String];
+#endif
+ }
} else {
ret["result"] = "error";
ret["error_code"] = (int64_t)error.code;
diff --git a/platform/iphone/gl_view.h b/platform/iphone/gl_view.h
deleted file mode 100644
index 975aa4b70a..0000000000
--- a/platform/iphone/gl_view.h
+++ /dev/null
@@ -1,123 +0,0 @@
-/*************************************************************************/
-/* gl_view.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. */
-/*************************************************************************/
-
-#import <AVFoundation/AVFoundation.h>
-#import <MediaPlayer/MediaPlayer.h>
-#import <OpenGLES/EAGL.h>
-#import <OpenGLES/ES1/gl.h>
-#import <OpenGLES/ES1/glext.h>
-#import <UIKit/UIKit.h>
-
-@protocol GLViewDelegate;
-
-@interface GLView : UIView <UIKeyInput> {
-@private
- // The pixel dimensions of the backbuffer
- GLint backingWidth;
- GLint backingHeight;
-
- EAGLContext *context;
-
- // OpenGL names for the renderbuffer and framebuffers used to render to this view
- GLuint viewRenderbuffer, viewFramebuffer;
-
- // OpenGL name for the depth buffer that is attached to viewFramebuffer, if it exists (0 if it does not exist)
- GLuint depthRenderbuffer;
-
- BOOL useCADisplayLink;
- // CADisplayLink available on 3.1+ synchronizes the animation timer & drawing with the refresh rate of the display, only supports animation intervals of 1/60 1/30 & 1/15
- CADisplayLink *displayLink;
-
- // An animation timer that, when animation is started, will periodically call -drawView at the given rate.
- // Only used if CADisplayLink is not
- NSTimer *animationTimer;
-
- NSTimeInterval animationInterval;
-
- // Delegate to do our drawing, called by -drawView, which can be called manually or via the animation timer.
- id<GLViewDelegate> delegate;
-
- // Flag to denote that the -setupView method of a delegate has been called.
- // Resets to NO whenever the delegate changes.
- BOOL delegateSetup;
- BOOL active;
- float screen_scale;
-}
-
-@property(nonatomic, assign) id<GLViewDelegate> delegate;
-
-// AVPlayer-related properties
-@property(strong, nonatomic) AVAsset *avAsset;
-@property(strong, nonatomic) AVPlayerItem *avPlayerItem;
-@property(strong, nonatomic) AVPlayer *avPlayer;
-@property(strong, nonatomic) AVPlayerLayer *avPlayerLayer;
-
-@property(strong, nonatomic) UIWindow *backgroundWindow;
-
-@property(nonatomic) UITextAutocorrectionType autocorrectionType;
-
-- (void)startAnimation;
-- (void)stopAnimation;
-- (void)drawView;
-
-- (BOOL)canBecomeFirstResponder;
-
-- (void)open_keyboard;
-- (void)hide_keyboard;
-- (void)deleteBackward;
-- (BOOL)hasText;
-- (void)insertText:(NSString *)p_text;
-
-- (id)initGLES;
-- (BOOL)createFramebuffer;
-- (void)destroyFramebuffer;
-
-- (void)audioRouteChangeListenerCallback:(NSNotification *)notification;
-- (void)keyboardOnScreen:(NSNotification *)notification;
-- (void)keyboardHidden:(NSNotification *)notification;
-
-@property NSTimeInterval animationInterval;
-@property(nonatomic, assign) BOOL useCADisplayLink;
-
-@end
-
-@protocol GLViewDelegate <NSObject>
-
-@required
-
-// Draw with OpenGL ES
-- (void)drawView:(GLView *)view;
-
-@optional
-
-// Called whenever you need to do some initialization before rendering.
-- (void)setupView:(GLView *)view;
-
-@end
diff --git a/platform/iphone/gl_view.mm b/platform/iphone/gl_view.mm
deleted file mode 100644
index 1169ebc6b4..0000000000
--- a/platform/iphone/gl_view.mm
+++ /dev/null
@@ -1,702 +0,0 @@
-/*************************************************************************/
-/* gl_view.mm */
-/*************************************************************************/
-/* 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. */
-/*************************************************************************/
-
-#import "gl_view.h"
-
-#include "core/os/keyboard.h"
-#include "core/project_settings.h"
-#include "os_iphone.h"
-#include "servers/audio_server.h"
-
-#import <OpenGLES/EAGLDrawable.h>
-#import <QuartzCore/QuartzCore.h>
-
-/*
-@interface GLView (private)
-
-- (id)initGLES;
-- (BOOL)createFramebuffer;
-- (void)destroyFramebuffer;
-@end
-*/
-
-int gl_view_base_fb;
-static String keyboard_text;
-static GLView *_instance = NULL;
-
-static bool video_found_error = false;
-static bool video_playing = false;
-static CMTime video_current_time;
-
-void _show_keyboard(String);
-void _hide_keyboard();
-bool _play_video(String, float, String, String);
-bool _is_video_playing();
-void _pause_video();
-void _focus_out_video();
-void _unpause_video();
-void _stop_video();
-CGFloat _points_to_pixels(CGFloat);
-
-void _show_keyboard(String p_existing) {
- keyboard_text = p_existing;
- printf("instance on show is %p\n", _instance);
- [_instance open_keyboard];
-};
-
-void _hide_keyboard() {
- printf("instance on hide is %p\n", _instance);
- [_instance hide_keyboard];
- keyboard_text = "";
-};
-
-Rect2 _get_ios_window_safe_area(float p_window_width, float p_window_height) {
- UIEdgeInsets insets = UIEdgeInsetsMake(0, 0, 0, 0);
- if (_instance != nil && [_instance respondsToSelector:@selector(safeAreaInsets)]) {
- insets = [_instance safeAreaInsets];
- }
- ERR_FAIL_COND_V(insets.left < 0 || insets.top < 0 || insets.right < 0 || insets.bottom < 0,
- Rect2(0, 0, p_window_width, p_window_height));
- UIEdgeInsets window_insets = UIEdgeInsetsMake(_points_to_pixels(insets.top), _points_to_pixels(insets.left), _points_to_pixels(insets.bottom), _points_to_pixels(insets.right));
- return Rect2(window_insets.left, window_insets.top, p_window_width - window_insets.right - window_insets.left, p_window_height - window_insets.bottom - window_insets.top);
-}
-
-bool _play_video(String p_path, float p_volume, String p_audio_track, String p_subtitle_track) {
- p_path = ProjectSettings::get_singleton()->globalize_path(p_path);
-
- NSString *file_path = [[[NSString alloc] initWithUTF8String:p_path.utf8().get_data()] autorelease];
-
- _instance.avAsset = [AVAsset assetWithURL:[NSURL fileURLWithPath:file_path]];
-
- _instance.avPlayerItem = [[AVPlayerItem alloc] initWithAsset:_instance.avAsset];
- [_instance.avPlayerItem addObserver:_instance forKeyPath:@"status" options:0 context:nil];
-
- _instance.avPlayer = [[AVPlayer alloc] initWithPlayerItem:_instance.avPlayerItem];
- _instance.avPlayerLayer = [AVPlayerLayer playerLayerWithPlayer:_instance.avPlayer];
-
- [_instance.avPlayer addObserver:_instance forKeyPath:@"status" options:0 context:nil];
- [[NSNotificationCenter defaultCenter]
- addObserver:_instance
- selector:@selector(playerItemDidReachEnd:)
- name:AVPlayerItemDidPlayToEndTimeNotification
- object:[_instance.avPlayer currentItem]];
-
- [_instance.avPlayer addObserver:_instance forKeyPath:@"rate" options:NSKeyValueObservingOptionNew context:0];
-
- [_instance.avPlayerLayer setFrame:_instance.bounds];
- [_instance.layer addSublayer:_instance.avPlayerLayer];
- [_instance.avPlayer play];
-
- AVMediaSelectionGroup *audioGroup = [_instance.avAsset mediaSelectionGroupForMediaCharacteristic:AVMediaCharacteristicAudible];
-
- NSMutableArray *allAudioParams = [NSMutableArray array];
- for (id track in audioGroup.options) {
- NSString *language = [[track locale] localeIdentifier];
- NSLog(@"subtitle lang: %@", language);
-
- if ([language isEqualToString:[NSString stringWithUTF8String:p_audio_track.utf8()]]) {
- AVMutableAudioMixInputParameters *audioInputParams = [AVMutableAudioMixInputParameters audioMixInputParameters];
- [audioInputParams setVolume:p_volume atTime:kCMTimeZero];
- [audioInputParams setTrackID:[track trackID]];
- [allAudioParams addObject:audioInputParams];
-
- AVMutableAudioMix *audioMix = [AVMutableAudioMix audioMix];
- [audioMix setInputParameters:allAudioParams];
-
- [_instance.avPlayer.currentItem selectMediaOption:track inMediaSelectionGroup:audioGroup];
- [_instance.avPlayer.currentItem setAudioMix:audioMix];
-
- break;
- }
- }
-
- AVMediaSelectionGroup *subtitlesGroup = [_instance.avAsset mediaSelectionGroupForMediaCharacteristic:AVMediaCharacteristicLegible];
- NSArray *useableTracks = [AVMediaSelectionGroup mediaSelectionOptionsFromArray:subtitlesGroup.options withoutMediaCharacteristics:[NSArray arrayWithObject:AVMediaCharacteristicContainsOnlyForcedSubtitles]];
-
- for (id track in useableTracks) {
- NSString *language = [[track locale] localeIdentifier];
- NSLog(@"subtitle lang: %@", language);
-
- if ([language isEqualToString:[NSString stringWithUTF8String:p_subtitle_track.utf8()]]) {
- [_instance.avPlayer.currentItem selectMediaOption:track inMediaSelectionGroup:subtitlesGroup];
- break;
- }
- }
-
- video_playing = true;
-
- return true;
-}
-
-bool _is_video_playing() {
- if (_instance.avPlayer.error) {
- printf("Error during playback\n");
- }
- return (_instance.avPlayer.rate > 0 && !_instance.avPlayer.error);
-}
-
-void _pause_video() {
- video_current_time = _instance.avPlayer.currentTime;
- [_instance.avPlayer pause];
- video_playing = false;
-}
-
-void _focus_out_video() {
- printf("focus out pausing video\n");
- [_instance.avPlayer pause];
-};
-
-void _unpause_video() {
- [_instance.avPlayer play];
- video_playing = true;
-};
-
-void _stop_video() {
- [_instance.avPlayer pause];
- [_instance.avPlayerLayer removeFromSuperlayer];
- _instance.avPlayer = nil;
- video_playing = false;
-}
-
-CGFloat _points_to_pixels(CGFloat points) {
- float pixelPerInch;
- if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
- pixelPerInch = 132;
- } else if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone) {
- pixelPerInch = 163;
- } else {
- pixelPerInch = 160;
- }
- CGFloat pointsPerInch = 72.0;
- return (points / pointsPerInch * pixelPerInch);
-}
-
-@implementation GLView
-
-@synthesize animationInterval;
-
-static const int max_touches = 8;
-static UITouch *touches[max_touches];
-
-static void init_touches() {
- for (int i = 0; i < max_touches; i++) {
- touches[i] = NULL;
- };
-};
-
-static int get_touch_id(UITouch *p_touch) {
- int first = -1;
- for (int i = 0; i < max_touches; i++) {
- if (first == -1 && touches[i] == NULL) {
- first = i;
- continue;
- };
- if (touches[i] == p_touch)
- return i;
- };
-
- if (first != -1) {
- touches[first] = p_touch;
- return first;
- };
-
- return -1;
-};
-
-static int remove_touch(UITouch *p_touch) {
- int remaining = 0;
- for (int i = 0; i < max_touches; i++) {
- if (touches[i] == NULL)
- continue;
- if (touches[i] == p_touch)
- touches[i] = NULL;
- else
- ++remaining;
- };
- return remaining;
-};
-
-static void clear_touches() {
- for (int i = 0; i < max_touches; i++) {
- touches[i] = NULL;
- };
-};
-
-// Implement this to override the default layer class (which is [CALayer class]).
-// We do this so that our view will be backed by a layer that is capable of OpenGL ES rendering.
-+ (Class)layerClass {
- return [CAEAGLLayer class];
-}
-
-//The GL view is stored in the nib file. When it's unarchived it's sent -initWithCoder:
-- (id)initWithCoder:(NSCoder *)coder {
- active = FALSE;
- if ((self = [super initWithCoder:coder])) {
- self = [self initGLES];
- }
- return self;
-}
-
-- (id)initGLES {
- // Get our backing layer
- CAEAGLLayer *eaglLayer = (CAEAGLLayer *)self.layer;
-
- // Configure it so that it is opaque, does not retain the contents of the backbuffer when displayed, and uses RGBA8888 color.
- eaglLayer.opaque = YES;
- eaglLayer.drawableProperties = [NSDictionary
- dictionaryWithObjectsAndKeys:[NSNumber numberWithBool:FALSE],
- kEAGLDrawablePropertyRetainedBacking,
- kEAGLColorFormatRGBA8,
- kEAGLDrawablePropertyColorFormat,
- nil];
-
- // FIXME: Add Vulkan support via MoltenVK. Add fallback code back?
-
- // Create GL ES 2 context
- if (GLOBAL_GET("rendering/quality/driver/driver_name") == "GLES2") {
- context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2];
- NSLog(@"Setting up an OpenGL ES 2.0 context.");
- if (!context) {
- NSLog(@"Failed to create OpenGL ES 2.0 context!");
- return nil;
- }
- }
-
- if (![EAGLContext setCurrentContext:context]) {
- NSLog(@"Failed to set EAGLContext!");
- return nil;
- }
- if (![self createFramebuffer]) {
- NSLog(@"Failed to create frame buffer!");
- return nil;
- }
-
- // Default the animation interval to 1/60th of a second.
- animationInterval = 1.0 / 60.0;
- return self;
-}
-
-- (id<GLViewDelegate>)delegate {
- return delegate;
-}
-
-// Update the delegate, and if it needs a -setupView: call, set our internal flag so that it will be called.
-- (void)setDelegate:(id<GLViewDelegate>)d {
- delegate = d;
- delegateSetup = ![delegate respondsToSelector:@selector(setupView:)];
-}
-
-@synthesize useCADisplayLink;
-
-// If our view is resized, we'll be asked to layout subviews.
-// This is the perfect opportunity to also update the framebuffer so that it is
-// the same size as our display area.
-
-- (void)layoutSubviews {
- [EAGLContext setCurrentContext:context];
- [self destroyFramebuffer];
- [self createFramebuffer];
- [self drawView];
-}
-
-- (BOOL)createFramebuffer {
- // Generate IDs for a framebuffer object and a color renderbuffer
- UIScreen *mainscr = [UIScreen mainScreen];
- printf("******** screen size %i, %i\n", (int)mainscr.currentMode.size.width, (int)mainscr.currentMode.size.height);
- self.contentScaleFactor = mainscr.nativeScale;
-
- glGenFramebuffersOES(1, &viewFramebuffer);
- glGenRenderbuffersOES(1, &viewRenderbuffer);
-
- glBindFramebufferOES(GL_FRAMEBUFFER_OES, viewFramebuffer);
- glBindRenderbufferOES(GL_RENDERBUFFER_OES, viewRenderbuffer);
- // This call associates the storage for the current render buffer with the EAGLDrawable (our CAEAGLLayer)
- // allowing us to draw into a buffer that will later be rendered to screen wherever the layer is (which corresponds with our view).
- [context renderbufferStorage:GL_RENDERBUFFER_OES fromDrawable:(id<EAGLDrawable>)self.layer];
- glFramebufferRenderbufferOES(GL_FRAMEBUFFER_OES, GL_COLOR_ATTACHMENT0_OES, GL_RENDERBUFFER_OES, viewRenderbuffer);
-
- glGetRenderbufferParameterivOES(GL_RENDERBUFFER_OES, GL_RENDERBUFFER_WIDTH_OES, &backingWidth);
- glGetRenderbufferParameterivOES(GL_RENDERBUFFER_OES, GL_RENDERBUFFER_HEIGHT_OES, &backingHeight);
-
- // For this sample, we also need a depth buffer, so we'll create and attach one via another renderbuffer.
- glGenRenderbuffersOES(1, &depthRenderbuffer);
- glBindRenderbufferOES(GL_RENDERBUFFER_OES, depthRenderbuffer);
- glRenderbufferStorageOES(GL_RENDERBUFFER_OES, GL_DEPTH_COMPONENT16_OES, backingWidth, backingHeight);
- glFramebufferRenderbufferOES(GL_FRAMEBUFFER_OES, GL_DEPTH_ATTACHMENT_OES, GL_RENDERBUFFER_OES, depthRenderbuffer);
-
- if (glCheckFramebufferStatusOES(GL_FRAMEBUFFER_OES) != GL_FRAMEBUFFER_COMPLETE_OES) {
- NSLog(@"failed to make complete framebuffer object %x", glCheckFramebufferStatusOES(GL_FRAMEBUFFER_OES));
- return NO;
- }
-
- if (OS::get_singleton()) {
- OS::VideoMode vm;
- vm.fullscreen = true;
- vm.width = backingWidth;
- vm.height = backingHeight;
- vm.resizable = false;
- OS::get_singleton()->set_video_mode(vm);
- OSIPhone::get_singleton()->set_base_framebuffer(viewFramebuffer);
- };
- gl_view_base_fb = viewFramebuffer;
-
- return YES;
-}
-
-// Clean up any buffers we have allocated.
-- (void)destroyFramebuffer {
- glDeleteFramebuffersOES(1, &viewFramebuffer);
- viewFramebuffer = 0;
- glDeleteRenderbuffersOES(1, &viewRenderbuffer);
- viewRenderbuffer = 0;
-
- if (depthRenderbuffer) {
- glDeleteRenderbuffersOES(1, &depthRenderbuffer);
- depthRenderbuffer = 0;
- }
-}
-
-- (void)startAnimation {
- if (active)
- return;
- active = TRUE;
- printf("start animation!\n");
- if (useCADisplayLink) {
- // Approximate frame rate
- // assumes device refreshes at 60 fps
- int frameInterval = (int)floor(animationInterval * 60.0f);
-
- displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(drawView)];
- [displayLink setFrameInterval:frameInterval];
-
- // Setup DisplayLink in main thread
- [displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];
- } else {
- animationTimer = [NSTimer scheduledTimerWithTimeInterval:animationInterval target:self selector:@selector(drawView) userInfo:nil repeats:YES];
- }
-
- if (video_playing) {
- _unpause_video();
- }
-}
-
-- (void)stopAnimation {
- if (!active)
- return;
- active = FALSE;
- printf("******** stop animation!\n");
-
- if (useCADisplayLink) {
- [displayLink invalidate];
- displayLink = nil;
- } else {
- [animationTimer invalidate];
- animationTimer = nil;
- }
-
- clear_touches();
-
- if (video_playing) {
- // save position
- }
-}
-
-- (void)setAnimationInterval:(NSTimeInterval)interval {
- animationInterval = interval;
- if ((useCADisplayLink && displayLink) || (!useCADisplayLink && animationTimer)) {
- [self stopAnimation];
- [self startAnimation];
- }
-}
-
-// Updates the OpenGL view when the timer fires
-- (void)drawView {
- if (!active) {
- printf("draw view not active!\n");
- return;
- };
- if (useCADisplayLink) {
- // Pause the CADisplayLink to avoid recursion
- [displayLink setPaused:YES];
-
- // Process all input events
- while (CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.0, TRUE) == kCFRunLoopRunHandledSource)
- ;
-
- // We are good to go, resume the CADisplayLink
- [displayLink setPaused:NO];
- }
-
- // Make sure that you are drawing to the current context
- [EAGLContext setCurrentContext:context];
-
- // If our drawing delegate needs to have the view setup, then call -setupView: and flag that it won't need to be called again.
- if (!delegateSetup) {
- [delegate setupView:self];
- delegateSetup = YES;
- }
-
- glBindFramebufferOES(GL_FRAMEBUFFER_OES, viewFramebuffer);
-
- [delegate drawView:self];
-
- glBindRenderbufferOES(GL_RENDERBUFFER_OES, viewRenderbuffer);
- [context presentRenderbuffer:GL_RENDERBUFFER_OES];
-
-#ifdef DEBUG_ENABLED
- GLenum err = glGetError();
- if (err)
- NSLog(@"DrawView: %x error", err);
-#endif
-}
-
-- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
- NSArray *tlist = [[event allTouches] allObjects];
- for (unsigned int i = 0; i < [tlist count]; i++) {
- if ([touches containsObject:[tlist objectAtIndex:i]]) {
- UITouch *touch = [tlist objectAtIndex:i];
- if (touch.phase != UITouchPhaseBegan)
- continue;
- int tid = get_touch_id(touch);
- ERR_FAIL_COND(tid == -1);
- CGPoint touchPoint = [touch locationInView:self];
- OSIPhone::get_singleton()->touch_press(tid, touchPoint.x * self.contentScaleFactor, touchPoint.y * self.contentScaleFactor, true, touch.tapCount > 1);
- };
- };
-}
-
-- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
- NSArray *tlist = [[event allTouches] allObjects];
- for (unsigned int i = 0; i < [tlist count]; i++) {
- if ([touches containsObject:[tlist objectAtIndex:i]]) {
- UITouch *touch = [tlist objectAtIndex:i];
- if (touch.phase != UITouchPhaseMoved)
- continue;
- int tid = get_touch_id(touch);
- ERR_FAIL_COND(tid == -1);
- CGPoint touchPoint = [touch locationInView:self];
- CGPoint prev_point = [touch previousLocationInView:self];
- OSIPhone::get_singleton()->touch_drag(tid, prev_point.x * self.contentScaleFactor, prev_point.y * self.contentScaleFactor, touchPoint.x * self.contentScaleFactor, touchPoint.y * self.contentScaleFactor);
- };
- };
-}
-
-- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
- NSArray *tlist = [[event allTouches] allObjects];
- for (unsigned int i = 0; i < [tlist count]; i++) {
- if ([touches containsObject:[tlist objectAtIndex:i]]) {
- UITouch *touch = [tlist objectAtIndex:i];
- if (touch.phase != UITouchPhaseEnded)
- continue;
- int tid = get_touch_id(touch);
- ERR_FAIL_COND(tid == -1);
- remove_touch(touch);
- CGPoint touchPoint = [touch locationInView:self];
- OSIPhone::get_singleton()->touch_press(tid, touchPoint.x * self.contentScaleFactor, touchPoint.y * self.contentScaleFactor, false, false);
- };
- };
-}
-
-- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event {
- OSIPhone::get_singleton()->touches_cancelled();
- clear_touches();
-};
-
-- (BOOL)canBecomeFirstResponder {
- return YES;
-};
-
-- (void)open_keyboard {
- //keyboard_text = p_existing;
- [self becomeFirstResponder];
-};
-
-- (void)hide_keyboard {
- //keyboard_text = p_existing;
- [self resignFirstResponder];
-};
-
-- (void)keyboardOnScreen:(NSNotification *)notification {
- NSDictionary *info = notification.userInfo;
- NSValue *value = info[UIKeyboardFrameEndUserInfoKey];
-
- CGRect rawFrame = [value CGRectValue];
- CGRect keyboardFrame = [self convertRect:rawFrame fromView:nil];
-
- OSIPhone::get_singleton()->set_virtual_keyboard_height(_points_to_pixels(keyboardFrame.size.height));
-}
-
-- (void)keyboardHidden:(NSNotification *)notification {
- OSIPhone::get_singleton()->set_virtual_keyboard_height(0);
-}
-
-- (void)deleteBackward {
- if (keyboard_text.length())
- keyboard_text.erase(keyboard_text.length() - 1, 1);
- OSIPhone::get_singleton()->key(KEY_BACKSPACE, true);
-};
-
-- (BOOL)hasText {
- return keyboard_text.length() ? YES : NO;
-};
-
-- (void)insertText:(NSString *)p_text {
- String character;
- character.parse_utf8([p_text UTF8String]);
- keyboard_text = keyboard_text + character;
- OSIPhone::get_singleton()->key(character[0] == 10 ? KEY_ENTER : character[0], true);
- printf("inserting text with character %lc\n", (CharType)character[0]);
-};
-
-- (void)audioRouteChangeListenerCallback:(NSNotification *)notification {
- printf("*********** route changed!\n");
- NSDictionary *interuptionDict = notification.userInfo;
-
- NSInteger routeChangeReason = [[interuptionDict valueForKey:AVAudioSessionRouteChangeReasonKey] integerValue];
-
- switch (routeChangeReason) {
- case AVAudioSessionRouteChangeReasonNewDeviceAvailable: {
- NSLog(@"AVAudioSessionRouteChangeReasonNewDeviceAvailable");
- NSLog(@"Headphone/Line plugged in");
- }; break;
-
- case AVAudioSessionRouteChangeReasonOldDeviceUnavailable: {
- NSLog(@"AVAudioSessionRouteChangeReasonOldDeviceUnavailable");
- NSLog(@"Headphone/Line was pulled. Resuming video play....");
- if (_is_video_playing()) {
- dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 0.5f * NSEC_PER_SEC), dispatch_get_main_queue(), ^{
- [_instance.avPlayer play]; // NOTE: change this line according your current player implementation
- NSLog(@"resumed play");
- });
- };
- }; break;
-
- case AVAudioSessionRouteChangeReasonCategoryChange: {
- // called at start - also when other audio wants to play
- NSLog(@"AVAudioSessionRouteChangeReasonCategoryChange");
- }; break;
- }
-}
-
-// When created via code however, we get initWithFrame
-- (id)initWithFrame:(CGRect)frame {
- self = [super initWithFrame:frame];
- _instance = self;
- printf("after init super %p\n", self);
- if (self != nil) {
- self = [self initGLES];
- printf("after init gles %p\n", self);
- }
- init_touches();
- self.multipleTouchEnabled = YES;
- self.autocorrectionType = UITextAutocorrectionTypeNo;
-
- printf("******** adding observer for sound routing changes\n");
- [[NSNotificationCenter defaultCenter]
- addObserver:self
- selector:@selector(audioRouteChangeListenerCallback:)
- name:AVAudioSessionRouteChangeNotification
- object:nil];
-
- printf("******** adding observer for keyboard show/hide\n");
- [[NSNotificationCenter defaultCenter]
- addObserver:self
- selector:@selector(keyboardOnScreen:)
- name:UIKeyboardDidShowNotification
- object:nil];
- [[NSNotificationCenter defaultCenter]
- addObserver:self
- selector:@selector(keyboardHidden:)
- name:UIKeyboardDidHideNotification
- object:nil];
-
- //self.autoresizesSubviews = YES;
- //[self setAutoresizingMask:UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleWidth];
-
- return self;
-}
-
-//- (BOOL)automaticallyForwardAppearanceAndRotationMethodsToChildViewControllers {
-// return YES;
-//}
-
-//- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation{
-// return YES;
-//}
-
-// Stop animating and release resources when they are no longer needed.
-- (void)dealloc {
- [self stopAnimation];
-
- if ([EAGLContext currentContext] == context) {
- [EAGLContext setCurrentContext:nil];
- }
-
- [context release];
- context = nil;
-
- [super dealloc];
-}
-
-- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
- if (object == _instance.avPlayerItem && [keyPath isEqualToString:@"status"]) {
- if (_instance.avPlayerItem.status == AVPlayerStatusFailed || _instance.avPlayer.status == AVPlayerStatusFailed) {
- _stop_video();
- video_found_error = true;
- }
-
- if (_instance.avPlayer.status == AVPlayerStatusReadyToPlay &&
- _instance.avPlayerItem.status == AVPlayerItemStatusReadyToPlay &&
- CMTIME_COMPARE_INLINE(video_current_time, ==, kCMTimeZero)) {
- //NSLog(@"time: %@", video_current_time);
-
- [_instance.avPlayer seekToTime:video_current_time];
- video_current_time = kCMTimeZero;
- }
- }
-
- if (object == _instance.avPlayer && [keyPath isEqualToString:@"rate"]) {
- NSLog(@"Player playback rate changed: %.5f", _instance.avPlayer.rate);
- if (_is_video_playing() && _instance.avPlayer.rate == 0.0 && !_instance.avPlayer.error) {
- dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 0.5f * NSEC_PER_SEC), dispatch_get_main_queue(), ^{
- [_instance.avPlayer play]; // NOTE: change this line according your current player implementation
- NSLog(@"resumed play");
- });
-
- NSLog(@" . . . PAUSED (or just started)");
- }
- }
-}
-
-- (void)playerItemDidReachEnd:(NSNotification *)notification {
- _stop_video();
-}
-
-@end
diff --git a/platform/iphone/godot_iphone.cpp b/platform/iphone/godot_iphone.mm
index aa5dbd5130..090b772947 100644
--- a/platform/iphone/godot_iphone.cpp
+++ b/platform/iphone/godot_iphone.mm
@@ -1,5 +1,5 @@
/*************************************************************************/
-/* godot_iphone.cpp */
+/* godot_iphone.mm */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
@@ -38,19 +38,53 @@
static OSIPhone *os = nullptr;
-extern "C" {
-int add_path(int p_argc, char **p_args);
-int add_cmdline(int p_argc, char **p_args);
+int add_path(int, char **);
+int add_cmdline(int, char **);
+int iphone_main(int, char **, String);
+
+int add_path(int p_argc, char **p_args) {
+ NSString *str = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"godot_path"];
+ if (!str) {
+ return p_argc;
+ }
+
+ p_args[p_argc++] = (char *)"--path";
+ [str retain]; // memory leak lol (maybe make it static here and delete it in ViewController destructor? @todo
+ p_args[p_argc++] = (char *)[str cStringUsingEncoding:NSUTF8StringEncoding];
+ p_args[p_argc] = NULL;
+ [str release];
+
+ return p_argc;
};
-int iphone_main(int, int, int, char **, String);
+int add_cmdline(int p_argc, char **p_args) {
+ NSArray *arr = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"godot_cmdline"];
+ if (!arr) {
+ return p_argc;
+ }
+
+ for (NSUInteger i = 0; i < [arr count]; i++) {
+ NSString *str = [arr objectAtIndex:i];
+ if (!str) {
+ continue;
+ }
+ [str retain]; // @todo delete these at some point
+ p_args[p_argc++] = (char *)[str cStringUsingEncoding:NSUTF8StringEncoding];
+ [str release];
+ };
-int iphone_main(int width, int height, int argc, char **argv, String data_dir) {
+ p_args[p_argc] = NULL;
+
+ return p_argc;
+};
+
+int iphone_main(int argc, char **argv, String data_dir) {
size_t len = strlen(argv[0]);
while (len--) {
- if (argv[0][len] == '/')
+ if (argv[0][len] == '/') {
break;
+ }
}
if (len >= 0) {
@@ -65,7 +99,7 @@ int iphone_main(int width, int height, int argc, char **argv, String data_dir) {
char cwd[512];
getcwd(cwd, sizeof(cwd));
printf("cwd %s\n", cwd);
- os = new OSIPhone(width, height, data_dir);
+ os = new OSIPhone(data_dir);
// We must override main when testing is enabled
TEST_MAIN_OVERRIDE
@@ -79,10 +113,14 @@ int iphone_main(int width, int height, int argc, char **argv, String data_dir) {
argc = add_cmdline(argc, fargv);
printf("os created\n");
+
Error err = Main::setup(fargv[0], argc - 1, &fargv[1], false);
printf("setup %i\n", err);
- if (err != OK)
+ if (err != OK) {
return 255;
+ }
+
+ os->initialize_modules();
return 0;
};
diff --git a/platform/iphone/godot_view.h b/platform/iphone/godot_view.h
new file mode 100644
index 0000000000..62fa2f5a32
--- /dev/null
+++ b/platform/iphone/godot_view.h
@@ -0,0 +1,56 @@
+/*************************************************************************/
+/* godot_view.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. */
+/*************************************************************************/
+
+#import <UIKit/UIKit.h>
+
+class String;
+
+@protocol DisplayLayer;
+@protocol GodotViewRendererProtocol;
+
+@interface GodotView : UIView <UIKeyInput>
+
+@property(assign, nonatomic) id<GodotViewRendererProtocol> renderer;
+
+@property(assign, readonly, nonatomic) BOOL isActive;
+
+@property(assign, nonatomic) BOOL useCADisplayLink;
+@property(strong, readonly, nonatomic) CALayer<DisplayLayer> *renderingLayer;
+@property(assign, readonly, nonatomic) BOOL canRender;
+
+@property(assign, nonatomic) NSTimeInterval renderingInterval;
+
+- (CALayer<DisplayLayer> *)initializeRenderingForDriver:(NSString *)driverName;
+- (void)stopRendering;
+- (void)startRendering;
+
+- (BOOL)becomeFirstResponderWithString:(String)p_existing;
+
+@end
diff --git a/platform/iphone/godot_view.mm b/platform/iphone/godot_view.mm
new file mode 100644
index 0000000000..c0a31549c4
--- /dev/null
+++ b/platform/iphone/godot_view.mm
@@ -0,0 +1,499 @@
+/*************************************************************************/
+/* godot_view.mm */
+/*************************************************************************/
+/* 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. */
+/*************************************************************************/
+
+#import "godot_view.h"
+#include "core/os/keyboard.h"
+#include "core/ustring.h"
+#import "display_layer.h"
+#include "display_server_iphone.h"
+#import "godot_view_gesture_recognizer.h"
+#import "godot_view_renderer.h"
+
+#import <CoreMotion/CoreMotion.h>
+
+static const int max_touches = 8;
+
+@interface GodotView () {
+ UITouch *godot_touches[max_touches];
+ String keyboard_text;
+}
+
+@property(assign, nonatomic) BOOL isActive;
+
+// CADisplayLink available on 3.1+ synchronizes the animation timer & drawing with the refresh rate of the display, only supports animation intervals of 1/60 1/30 & 1/15
+@property(strong, nonatomic) CADisplayLink *displayLink;
+
+// An animation timer that, when animation is started, will periodically call -drawView at the given rate.
+// Only used if CADisplayLink is not
+@property(strong, nonatomic) NSTimer *animationTimer;
+
+@property(strong, nonatomic) CALayer<DisplayLayer> *renderingLayer;
+
+@property(strong, nonatomic) CMMotionManager *motionManager;
+
+@property(strong, nonatomic) GodotViewGestureRecognizer *delayGestureRecognizer;
+
+@end
+
+@implementation GodotView
+
+- (CALayer<DisplayLayer> *)initializeRenderingForDriver:(NSString *)driverName {
+ if (self.renderingLayer) {
+ return self.renderingLayer;
+ }
+
+ CALayer<DisplayLayer> *layer;
+
+ if ([driverName isEqualToString:@"vulkan"]) {
+ layer = [GodotMetalLayer layer];
+ } else if ([driverName isEqualToString:@"opengl_es"]) {
+ if (@available(iOS 13, *)) {
+ NSLog(@"OpenGL ES is deprecated on iOS 13");
+ }
+#if defined(TARGET_OS_SIMULATOR) && TARGET_OS_SIMULATOR
+ return nil;
+#else
+ layer = [GodotOpenGLLayer layer];
+#endif
+ } else {
+ return nil;
+ }
+
+ layer.frame = self.bounds;
+ layer.contentsScale = self.contentScaleFactor;
+
+ [self.layer addSublayer:layer];
+ self.renderingLayer = layer;
+
+ [layer initializeDisplayLayer];
+
+ return self.renderingLayer;
+}
+
+- (instancetype)initWithCoder:(NSCoder *)coder {
+ self = [super initWithCoder:coder];
+
+ if (self) {
+ [self godot_commonInit];
+ }
+
+ return self;
+}
+
+- (instancetype)initWithFrame:(CGRect)frame {
+ self = [super initWithFrame:frame];
+
+ if (self) {
+ [self godot_commonInit];
+ }
+
+ return self;
+}
+
+- (void)dealloc {
+ [self stopRendering];
+
+ self.renderer = nil;
+
+ if (self.renderingLayer) {
+ [self.renderingLayer removeFromSuperlayer];
+ self.renderingLayer = nil;
+ }
+
+ if (self.motionManager) {
+ [self.motionManager stopDeviceMotionUpdates];
+ self.motionManager = nil;
+ }
+
+ if (self.displayLink) {
+ [self.displayLink invalidate];
+ self.displayLink = nil;
+ }
+
+ if (self.animationTimer) {
+ [self.animationTimer invalidate];
+ self.animationTimer = nil;
+ }
+
+ if (self.delayGestureRecognizer) {
+ self.delayGestureRecognizer = nil;
+ }
+
+ [super dealloc];
+}
+
+- (void)godot_commonInit {
+ self.contentScaleFactor = [UIScreen mainScreen].nativeScale;
+
+ [self initTouches];
+
+ // Configure and start accelerometer
+ if (!self.motionManager) {
+ self.motionManager = [[[CMMotionManager alloc] init] autorelease];
+ if (self.motionManager.deviceMotionAvailable) {
+ self.motionManager.deviceMotionUpdateInterval = 1.0 / 70.0;
+ [self.motionManager startDeviceMotionUpdatesUsingReferenceFrame:CMAttitudeReferenceFrameXMagneticNorthZVertical];
+ } else {
+ self.motionManager = nil;
+ }
+ }
+
+ // Initialize delay gesture recognizer
+ GodotViewGestureRecognizer *gestureRecognizer = [[GodotViewGestureRecognizer alloc] init];
+ self.delayGestureRecognizer = gestureRecognizer;
+ [self addGestureRecognizer:self.delayGestureRecognizer];
+ [gestureRecognizer release];
+}
+
+- (void)stopRendering {
+ if (!self.isActive) {
+ return;
+ }
+
+ self.isActive = NO;
+
+ printf("******** stop animation!\n");
+
+ if (self.useCADisplayLink) {
+ [self.displayLink invalidate];
+ self.displayLink = nil;
+ } else {
+ [self.animationTimer invalidate];
+ self.animationTimer = nil;
+ }
+
+ [self clearTouches];
+}
+
+- (void)startRendering {
+ if (self.isActive) {
+ return;
+ }
+
+ self.isActive = YES;
+
+ printf("start animation!\n");
+
+ if (self.useCADisplayLink) {
+ self.displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(drawView)];
+
+ // if (@available(iOS 10, *)) {
+ self.displayLink.preferredFramesPerSecond = (NSInteger)(1.0 / self.renderingInterval);
+ // } else {
+ // // Approximate frame rate
+ // // assumes device refreshes at 60 fps
+ // int frameInterval = (int)floor(self.renderingInterval * 60.0f);
+ // [self.displayLink setFrameInterval:frameInterval];
+ // }
+
+ // Setup DisplayLink in main thread
+ [self.displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];
+ } else {
+ self.animationTimer = [NSTimer scheduledTimerWithTimeInterval:self.renderingInterval target:self selector:@selector(drawView) userInfo:nil repeats:YES];
+ }
+}
+
+- (void)drawView {
+ if (!self.isActive) {
+ printf("draw view not active!\n");
+ return;
+ }
+
+ if (self.useCADisplayLink) {
+ // Pause the CADisplayLink to avoid recursion
+ [self.displayLink setPaused:YES];
+
+ // Process all input events
+ while (CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.0, TRUE) == kCFRunLoopRunHandledSource)
+ ;
+
+ // We are good to go, resume the CADisplayLink
+ [self.displayLink setPaused:NO];
+ }
+
+ [self.renderingLayer renderDisplayLayer];
+
+ if (!self.renderer) {
+ return;
+ }
+
+ if ([self.renderer setupView:self]) {
+ return;
+ }
+
+ [self handleMotion];
+ [self.renderer renderOnView:self];
+}
+
+- (BOOL)canRender {
+ if (self.useCADisplayLink) {
+ return self.displayLink != nil;
+ } else {
+ return self.animationTimer != nil;
+ }
+}
+
+- (void)setRenderingInterval:(NSTimeInterval)renderingInterval {
+ _renderingInterval = renderingInterval;
+
+ if (self.canRender) {
+ [self stopRendering];
+ [self startRendering];
+ }
+}
+
+- (void)layoutSubviews {
+ if (self.renderingLayer) {
+ self.renderingLayer.frame = self.bounds;
+ [self.renderingLayer layoutDisplayLayer];
+
+ if (DisplayServerIPhone::get_singleton()) {
+ DisplayServerIPhone::get_singleton()->resize_window(self.bounds.size);
+ }
+ }
+
+ [super layoutSubviews];
+}
+
+// MARK: - Input
+
+// MARK: Keyboard
+
+- (BOOL)canBecomeFirstResponder {
+ return YES;
+}
+
+- (BOOL)becomeFirstResponderWithString:(String)p_existing {
+ keyboard_text = p_existing;
+ return [self becomeFirstResponder];
+}
+
+- (BOOL)resignFirstResponder {
+ keyboard_text = String();
+ return [super resignFirstResponder];
+}
+
+- (void)deleteBackward {
+ if (keyboard_text.length()) {
+ keyboard_text.erase(keyboard_text.length() - 1, 1);
+ }
+ DisplayServerIPhone::get_singleton()->key(KEY_BACKSPACE, true);
+}
+
+- (BOOL)hasText {
+ return keyboard_text.length() > 0;
+}
+
+- (void)insertText:(NSString *)p_text {
+ String character;
+ character.parse_utf8([p_text UTF8String]);
+ keyboard_text = keyboard_text + character;
+ DisplayServerIPhone::get_singleton()->key(character[0] == 10 ? KEY_ENTER : character[0], true);
+}
+
+// MARK: Touches
+
+- (void)initTouches {
+ for (int i = 0; i < max_touches; i++) {
+ godot_touches[i] = NULL;
+ }
+}
+
+- (int)getTouchIDForTouch:(UITouch *)p_touch {
+ int first = -1;
+ for (int i = 0; i < max_touches; i++) {
+ if (first == -1 && godot_touches[i] == NULL) {
+ first = i;
+ continue;
+ }
+ if (godot_touches[i] == p_touch) {
+ return i;
+ }
+ }
+
+ if (first != -1) {
+ godot_touches[first] = p_touch;
+ return first;
+ }
+
+ return -1;
+}
+
+- (int)removeTouch:(UITouch *)p_touch {
+ int remaining = 0;
+ for (int i = 0; i < max_touches; i++) {
+ if (godot_touches[i] == NULL) {
+ continue;
+ }
+ if (godot_touches[i] == p_touch) {
+ godot_touches[i] = NULL;
+ } else {
+ ++remaining;
+ }
+ }
+ return remaining;
+}
+
+- (void)clearTouches {
+ for (int i = 0; i < max_touches; i++) {
+ godot_touches[i] = NULL;
+ }
+}
+
+- (void)touchesBegan:(NSSet *)touchesSet withEvent:(UIEvent *)event {
+ NSArray *tlist = [event.allTouches allObjects];
+ for (unsigned int i = 0; i < [tlist count]; i++) {
+ if ([touchesSet containsObject:[tlist objectAtIndex:i]]) {
+ UITouch *touch = [tlist objectAtIndex:i];
+ int tid = [self getTouchIDForTouch:touch];
+ ERR_FAIL_COND(tid == -1);
+ CGPoint touchPoint = [touch locationInView:self];
+ DisplayServerIPhone::get_singleton()->touch_press(tid, touchPoint.x * self.contentScaleFactor, touchPoint.y * self.contentScaleFactor, true, touch.tapCount > 1);
+ }
+ }
+}
+
+- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
+ NSArray *tlist = [event.allTouches allObjects];
+ for (unsigned int i = 0; i < [tlist count]; i++) {
+ if ([touches containsObject:[tlist objectAtIndex:i]]) {
+ UITouch *touch = [tlist objectAtIndex:i];
+ int tid = [self getTouchIDForTouch:touch];
+ ERR_FAIL_COND(tid == -1);
+ CGPoint touchPoint = [touch locationInView:self];
+ CGPoint prev_point = [touch previousLocationInView:self];
+ DisplayServerIPhone::get_singleton()->touch_drag(tid, prev_point.x * self.contentScaleFactor, prev_point.y * self.contentScaleFactor, touchPoint.x * self.contentScaleFactor, touchPoint.y * self.contentScaleFactor);
+ }
+ }
+}
+
+- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
+ NSArray *tlist = [event.allTouches allObjects];
+ for (unsigned int i = 0; i < [tlist count]; i++) {
+ if ([touches containsObject:[tlist objectAtIndex:i]]) {
+ UITouch *touch = [tlist objectAtIndex:i];
+ int tid = [self getTouchIDForTouch:touch];
+ ERR_FAIL_COND(tid == -1);
+ [self removeTouch:touch];
+ CGPoint touchPoint = [touch locationInView:self];
+ DisplayServerIPhone::get_singleton()->touch_press(tid, touchPoint.x * self.contentScaleFactor, touchPoint.y * self.contentScaleFactor, false, false);
+ }
+ }
+}
+
+- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event {
+ NSArray *tlist = [event.allTouches allObjects];
+ for (unsigned int i = 0; i < [tlist count]; i++) {
+ if ([touches containsObject:[tlist objectAtIndex:i]]) {
+ UITouch *touch = [tlist objectAtIndex:i];
+ int tid = [self getTouchIDForTouch:touch];
+ ERR_FAIL_COND(tid == -1);
+ DisplayServerIPhone::get_singleton()->touches_cancelled(tid);
+ }
+ }
+ [self clearTouches];
+}
+
+// MARK: Motion
+
+- (void)handleMotion {
+ if (!self.motionManager) {
+ return;
+ }
+
+ // Just using polling approach for now, we can set this up so it sends
+ // data to us in intervals, might be better. See Apple reference pages
+ // for more details:
+ // https://developer.apple.com/reference/coremotion/cmmotionmanager?language=objc
+
+ // Apple splits our accelerometer date into a gravity and user movement
+ // component. We add them back together
+ CMAcceleration gravity = self.motionManager.deviceMotion.gravity;
+ CMAcceleration acceleration = self.motionManager.deviceMotion.userAcceleration;
+
+ ///@TODO We don't seem to be getting data here, is my device broken or
+ /// is this code incorrect?
+ CMMagneticField magnetic = self.motionManager.deviceMotion.magneticField.field;
+
+ ///@TODO we can access rotationRate as a CMRotationRate variable
+ ///(processed date) or CMGyroData (raw data), have to see what works
+ /// best
+ CMRotationRate rotation = self.motionManager.deviceMotion.rotationRate;
+
+ // Adjust for screen orientation.
+ // [[UIDevice currentDevice] orientation] changes even if we've fixed
+ // our orientation which is not a good thing when you're trying to get
+ // your user to move the screen in all directions and want consistent
+ // output
+
+ ///@TODO Using [[UIApplication sharedApplication] statusBarOrientation]
+ /// is a bit of a hack. Godot obviously knows the orientation so maybe
+ /// we
+ // can use that instead? (note that left and right seem swapped)
+
+ UIInterfaceOrientation interfaceOrientation = UIInterfaceOrientationUnknown;
+
+ if (@available(iOS 13, *)) {
+ interfaceOrientation = [UIApplication sharedApplication].delegate.window.windowScene.interfaceOrientation;
+#if !defined(TARGET_OS_SIMULATOR) || !TARGET_OS_SIMULATOR
+ } else {
+ interfaceOrientation = [[UIApplication sharedApplication] statusBarOrientation];
+#endif
+ }
+
+ switch (interfaceOrientation) {
+ case UIInterfaceOrientationLandscapeLeft: {
+ DisplayServerIPhone::get_singleton()->update_gravity(-gravity.y, gravity.x, gravity.z);
+ DisplayServerIPhone::get_singleton()->update_accelerometer(-(acceleration.y + gravity.y), (acceleration.x + gravity.x), acceleration.z + gravity.z);
+ DisplayServerIPhone::get_singleton()->update_magnetometer(-magnetic.y, magnetic.x, magnetic.z);
+ DisplayServerIPhone::get_singleton()->update_gyroscope(-rotation.y, rotation.x, rotation.z);
+ } break;
+ case UIInterfaceOrientationLandscapeRight: {
+ DisplayServerIPhone::get_singleton()->update_gravity(gravity.y, -gravity.x, gravity.z);
+ DisplayServerIPhone::get_singleton()->update_accelerometer((acceleration.y + gravity.y), -(acceleration.x + gravity.x), acceleration.z + gravity.z);
+ DisplayServerIPhone::get_singleton()->update_magnetometer(magnetic.y, -magnetic.x, magnetic.z);
+ DisplayServerIPhone::get_singleton()->update_gyroscope(rotation.y, -rotation.x, rotation.z);
+ } break;
+ case UIInterfaceOrientationPortraitUpsideDown: {
+ DisplayServerIPhone::get_singleton()->update_gravity(-gravity.x, gravity.y, gravity.z);
+ DisplayServerIPhone::get_singleton()->update_accelerometer(-(acceleration.x + gravity.x), (acceleration.y + gravity.y), acceleration.z + gravity.z);
+ DisplayServerIPhone::get_singleton()->update_magnetometer(-magnetic.x, magnetic.y, magnetic.z);
+ DisplayServerIPhone::get_singleton()->update_gyroscope(-rotation.x, rotation.y, rotation.z);
+ } break;
+ default: { // assume portrait
+ DisplayServerIPhone::get_singleton()->update_gravity(gravity.x, gravity.y, gravity.z);
+ DisplayServerIPhone::get_singleton()->update_accelerometer(acceleration.x + gravity.x, acceleration.y + gravity.y, acceleration.z + gravity.z);
+ DisplayServerIPhone::get_singleton()->update_magnetometer(magnetic.x, magnetic.y, magnetic.z);
+ DisplayServerIPhone::get_singleton()->update_gyroscope(rotation.x, rotation.y, rotation.z);
+ } break;
+ }
+}
+
+@end
diff --git a/platform/iphone/godot_view_gesture_recognizer.h b/platform/iphone/godot_view_gesture_recognizer.h
new file mode 100644
index 0000000000..ca3bd808d1
--- /dev/null
+++ b/platform/iphone/godot_view_gesture_recognizer.h
@@ -0,0 +1,44 @@
+/*************************************************************************/
+/* godot_view_gesture_recognizer.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. */
+/*************************************************************************/
+
+// GLViewGestureRecognizer allows iOS gestures to work currectly by
+// emulating UIScrollView's UIScrollViewDelayedTouchesBeganGestureRecognizer.
+// It catches all gestures incoming to UIView and delays them for 150ms
+// (the same value used by UIScrollViewDelayedTouchesBeganGestureRecognizer)
+// If touch cancelation or end message is fired it fires delayed
+// begin touch immediately as well as last touch signal
+
+#import <UIKit/UIKit.h>
+
+@interface GodotViewGestureRecognizer : UIGestureRecognizer
+
+- (instancetype)init;
+
+@end
diff --git a/platform/iphone/godot_view_gesture_recognizer.m b/platform/iphone/godot_view_gesture_recognizer.m
new file mode 100644
index 0000000000..377ccd52a5
--- /dev/null
+++ b/platform/iphone/godot_view_gesture_recognizer.m
@@ -0,0 +1,171 @@
+/*************************************************************************/
+/* godot_view_gesture_recognizer.m */
+/*************************************************************************/
+/* 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. */
+/*************************************************************************/
+
+#import "godot_view_gesture_recognizer.h"
+
+// Using same delay interval that is used for `UIScrollView`
+const NSTimeInterval kGLGestureDelayInterval = 0.150;
+
+// Minimum distance for touches to move to fire
+// a delay timer before scheduled time.
+// Should be the low enough to not cause issues with dragging
+// but big enough to allow click to work.
+const CGFloat kGLGestureMovementDistance = 0.5;
+
+@interface GodotViewGestureRecognizer ()
+
+// Timer used to delay begin touch message.
+// Should work as simple emulation of UIDelayedAction
+@property(strong, nonatomic) NSTimer *delayTimer;
+
+// Delayed touch parameters
+@property(strong, nonatomic) NSSet *delayedTouches;
+@property(strong, nonatomic) UIEvent *delayedEvent;
+
+@end
+
+@implementation GodotViewGestureRecognizer
+
+- (instancetype)init {
+ self = [super init];
+
+ self.cancelsTouchesInView = YES;
+ self.delaysTouchesBegan = YES;
+ self.delaysTouchesEnded = YES;
+
+ return self;
+}
+
+- (void)dealloc {
+ if (self.delayTimer) {
+ [self.delayTimer invalidate];
+ self.delayTimer = nil;
+ }
+
+ if (self.delayedTouches) {
+ self.delayedTouches = nil;
+ }
+
+ if (self.delayedEvent) {
+ self.delayedEvent = nil;
+ }
+
+ [super dealloc];
+}
+
+- (void)delayTouches:(NSSet *)touches andEvent:(UIEvent *)event {
+ [self.delayTimer fire];
+
+ self.delayedTouches = touches;
+ self.delayedEvent = event;
+
+ self.delayTimer = [NSTimer
+ scheduledTimerWithTimeInterval:kGLGestureDelayInterval
+ target:self
+ selector:@selector(fireDelayedTouches:)
+ userInfo:nil
+ repeats:NO];
+}
+
+- (void)fireDelayedTouches:(id)timer {
+ [self.delayTimer invalidate];
+ self.delayTimer = nil;
+
+ if (self.delayedTouches) {
+ [self.view touchesBegan:self.delayedTouches withEvent:self.delayedEvent];
+ }
+
+ self.delayedTouches = nil;
+ self.delayedEvent = nil;
+}
+
+- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
+ NSSet *cleared = [self copyClearedTouches:touches phase:UITouchPhaseBegan];
+ [self delayTouches:cleared andEvent:event];
+ [cleared release];
+}
+
+- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
+ NSSet *cleared = [self copyClearedTouches:touches phase:UITouchPhaseMoved];
+
+ if (self.delayTimer) {
+ // We should check if movement was significant enough to fire an event
+ // for dragging to work correctly.
+ for (UITouch *touch in cleared) {
+ CGPoint from = [touch locationInView:self.view];
+ CGPoint to = [touch previousLocationInView:self.view];
+ CGFloat xDistance = from.x - to.x;
+ CGFloat yDistance = from.y - to.y;
+
+ CGFloat distance = sqrt(xDistance * xDistance + yDistance * yDistance);
+
+ // Early exit, since one of touches has moved enough to fire a drag event.
+ if (distance > kGLGestureMovementDistance) {
+ [self.delayTimer fire];
+ [self.view touchesMoved:cleared withEvent:event];
+ [cleared release];
+ return;
+ }
+ }
+
+ [cleared release];
+ return;
+ }
+
+ [self.view touchesMoved:cleared withEvent:event];
+ [cleared release];
+}
+
+- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
+ [self.delayTimer fire];
+
+ NSSet *cleared = [self copyClearedTouches:touches phase:UITouchPhaseEnded];
+ [self.view touchesEnded:cleared withEvent:event];
+ [cleared release];
+}
+
+- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event {
+ [self.delayTimer fire];
+ [self.view touchesCancelled:touches withEvent:event];
+};
+
+- (NSSet *)copyClearedTouches:(NSSet *)touches phase:(UITouchPhase)phaseToSave {
+ NSMutableSet *cleared = [touches mutableCopy];
+
+ for (UITouch *touch in touches) {
+ if (touch.phase != phaseToSave) {
+ [cleared removeObject:touch];
+ }
+ }
+
+ return cleared;
+}
+
+@end
diff --git a/platform/iphone/godot_view_renderer.h b/platform/iphone/godot_view_renderer.h
new file mode 100644
index 0000000000..ea8998c808
--- /dev/null
+++ b/platform/iphone/godot_view_renderer.h
@@ -0,0 +1,44 @@
+/*************************************************************************/
+/* godot_view_renderer.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. */
+/*************************************************************************/
+
+#import <UIKit/UIKit.h>
+
+@protocol GodotViewRendererProtocol <NSObject>
+
+@property(assign, readonly, nonatomic) BOOL hasFinishedSetup;
+
+- (BOOL)setupView:(UIView *)view;
+- (void)renderOnView:(UIView *)view;
+
+@end
+
+@interface GodotViewRenderer : NSObject <GodotViewRendererProtocol>
+
+@end
diff --git a/platform/iphone/godot_view_renderer.mm b/platform/iphone/godot_view_renderer.mm
new file mode 100644
index 0000000000..1fc822b457
--- /dev/null
+++ b/platform/iphone/godot_view_renderer.mm
@@ -0,0 +1,146 @@
+/*************************************************************************/
+/* godot_view_renderer.mm */
+/*************************************************************************/
+/* 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. */
+/*************************************************************************/
+
+#import "godot_view_renderer.h"
+#include "core/os/keyboard.h"
+#include "core/project_settings.h"
+#import "display_server_iphone.h"
+#include "main/main.h"
+#include "os_iphone.h"
+#include "servers/audio_server.h"
+
+#import <AudioToolbox/AudioServices.h>
+#import <CoreMotion/CoreMotion.h>
+#import <GameController/GameController.h>
+#import <QuartzCore/QuartzCore.h>
+#import <UIKit/UIKit.h>
+
+@interface GodotViewRenderer ()
+
+@property(assign, nonatomic) BOOL hasFinishedLocaleSetup;
+@property(assign, nonatomic) BOOL hasFinishedProjectDataSetup;
+@property(assign, nonatomic) BOOL hasStartedMain;
+@property(assign, nonatomic) BOOL hasFinishedSetup;
+
+@end
+
+@implementation GodotViewRenderer
+
+- (BOOL)setupView:(UIView *)view {
+ if (self.hasFinishedSetup) {
+ return NO;
+ }
+
+ if (!self.hasFinishedLocaleSetup) {
+ [self setupLocaleAndUUID];
+ return YES;
+ }
+
+ if (!self.hasFinishedProjectDataSetup) {
+ [self setupProjectData];
+ return YES;
+ }
+
+ if (!self.hasStartedMain) {
+ self.hasStartedMain = YES;
+ OSIPhone::get_singleton()->start();
+ return YES;
+ }
+
+ self.hasFinishedSetup = YES;
+
+ return NO;
+}
+
+- (void)setupLocaleAndUUID {
+ self.hasFinishedLocaleSetup = YES;
+
+ if (!OS::get_singleton()) {
+ exit(0);
+ }
+
+ NSString *locale_code = [[NSLocale currentLocale] localeIdentifier];
+ OSIPhone::get_singleton()->set_locale(String::utf8([locale_code UTF8String]));
+
+ NSString *uuid;
+ if ([[UIDevice currentDevice] respondsToSelector:@selector(identifierForVendor)]) {
+ uuid = [UIDevice currentDevice].identifierForVendor.UUIDString;
+ } else {
+ // before iOS 6, so just generate an identifier and store it
+ uuid = [[NSUserDefaults standardUserDefaults] objectForKey:@"identiferForVendor"];
+ if (!uuid) {
+ CFUUIDRef cfuuid = CFUUIDCreate(NULL);
+ uuid = [(NSString *)CFUUIDCreateString(NULL, cfuuid) autorelease];
+ CFRelease(cfuuid);
+ [[NSUserDefaults standardUserDefaults] setObject:uuid forKey:@"identifierForVendor"];
+ }
+ }
+
+ OSIPhone::get_singleton()->set_unique_id(String::utf8([uuid UTF8String]));
+}
+
+- (void)setupProjectData {
+ self.hasFinishedProjectDataSetup = YES;
+
+ Main::setup2();
+
+ // this might be necessary before here
+ NSDictionary *dict = [[NSBundle mainBundle] infoDictionary];
+ for (NSString *key in dict) {
+ NSObject *value = [dict objectForKey:key];
+ String ukey = String::utf8([key UTF8String]);
+
+ // we need a NSObject to Variant conversor
+
+ if ([value isKindOfClass:[NSString class]]) {
+ NSString *str = (NSString *)value;
+ String uval = String::utf8([str UTF8String]);
+
+ ProjectSettings::get_singleton()->set("Info.plist/" + ukey, uval);
+
+ } else if ([value isKindOfClass:[NSNumber class]]) {
+ NSNumber *n = (NSNumber *)value;
+ double dval = [n doubleValue];
+
+ ProjectSettings::get_singleton()->set("Info.plist/" + ukey, dval);
+ };
+ // do stuff
+ }
+}
+
+- (void)renderOnView:(UIView *)view {
+ if (!OSIPhone::get_singleton()) {
+ return;
+ }
+
+ OSIPhone::get_singleton()->iterate();
+}
+
+@end
diff --git a/platform/iphone/icloud.h b/platform/iphone/icloud.h
index b11e22fec6..381edfa718 100644
--- a/platform/iphone/icloud.h
+++ b/platform/iphone/icloud.h
@@ -44,9 +44,9 @@ class ICloud : public Object {
List<Variant> pending_events;
public:
- Error remove_key(Variant p_param);
- Variant set_key_values(Variant p_param);
- Variant get_key_value(Variant p_param);
+ Error remove_key(String p_param);
+ Array set_key_values(Dictionary p_params);
+ Variant get_key_value(String p_param);
Error synchronize_key_values();
Variant get_all_key_values();
diff --git a/platform/iphone/icloud.mm b/platform/iphone/icloud.mm
index c768274b1f..d3086e6cea 100644
--- a/platform/iphone/icloud.mm
+++ b/platform/iphone/icloud.mm
@@ -48,8 +48,10 @@ ICloud *ICloud::instance = NULL;
void ICloud::_bind_methods() {
ClassDB::bind_method(D_METHOD("remove_key"), &ICloud::remove_key);
+
ClassDB::bind_method(D_METHOD("set_key_values"), &ICloud::set_key_values);
ClassDB::bind_method(D_METHOD("get_key_value"), &ICloud::get_key_value);
+
ClassDB::bind_method(D_METHOD("synchronize_key_values"), &ICloud::synchronize_key_values);
ClassDB::bind_method(D_METHOD("get_all_key_values"), &ICloud::get_all_key_values);
@@ -91,7 +93,7 @@ Variant nsobject_to_variant(NSObject *object) {
} else if ([object isKindOfClass:[NSArray class]]) {
Array result;
NSArray *array = (NSArray *)object;
- for (unsigned int i = 0; i < [array count]; ++i) {
+ for (NSUInteger i = 0; i < [array count]; ++i) {
NSObject *value = [array objectAtIndex:i];
result.push_back(nsobject_to_variant(value));
}
@@ -149,7 +151,7 @@ Variant nsobject_to_variant(NSObject *object) {
NSObject *variant_to_nsobject(Variant v) {
if (v.get_type() == Variant::STRING) {
return [[[NSString alloc] initWithUTF8String:((String)v).utf8().get_data()] autorelease];
- } else if (v.get_type() == Variant::REAL) {
+ } else if (v.get_type() == Variant::FLOAT) {
return [NSNumber numberWithDouble:(double)v];
} else if (v.get_type() == Variant::INT) {
return [NSNumber numberWithLongLong:(long)(int)v];
@@ -159,7 +161,7 @@ NSObject *variant_to_nsobject(Variant v) {
NSMutableDictionary *result = [[[NSMutableDictionary alloc] init] autorelease];
Dictionary dic = v;
Array keys = dic.keys();
- for (unsigned int i = 0; i < keys.size(); ++i) {
+ for (int i = 0; i < keys.size(); ++i) {
NSString *key = [[[NSString alloc] initWithUTF8String:((String)(keys[i])).utf8().get_data()] autorelease];
NSObject *value = variant_to_nsobject(dic[keys[i]]);
@@ -173,7 +175,7 @@ NSObject *variant_to_nsobject(Variant v) {
} else if (v.get_type() == Variant::ARRAY) {
NSMutableArray *result = [[[NSMutableArray alloc] init] autorelease];
Array arr = v;
- for (unsigned int i = 0; i < arr.size(); ++i) {
+ for (int i = 0; i < arr.size(); ++i) {
NSObject *value = variant_to_nsobject(arr[i]);
if (value == NULL) {
//trying to add something unsupported to the array. cancel the whole array
@@ -192,9 +194,8 @@ NSObject *variant_to_nsobject(Variant v) {
return NULL;
}
-Error ICloud::remove_key(Variant p_param) {
- String param = p_param;
- NSString *key = [[[NSString alloc] initWithUTF8String:param.utf8().get_data()] autorelease];
+Error ICloud::remove_key(String p_param) {
+ NSString *key = [[[NSString alloc] initWithUTF8String:p_param.utf8().get_data()] autorelease];
NSUbiquitousKeyValueStore *store = [NSUbiquitousKeyValueStore defaultStore];
@@ -207,15 +208,14 @@ Error ICloud::remove_key(Variant p_param) {
}
//return an array of the keys that could not be set
-Variant ICloud::set_key_values(Variant p_params) {
- Dictionary params = p_params;
- Array keys = params.keys();
+Array ICloud::set_key_values(Dictionary p_params) {
+ Array keys = p_params.keys();
Array error_keys;
- for (unsigned int i = 0; i < keys.size(); ++i) {
+ for (int i = 0; i < keys.size(); ++i) {
String variant_key = keys[i];
- Variant variant_value = params[variant_key];
+ Variant variant_value = p_params[variant_key];
NSString *key = [[[NSString alloc] initWithUTF8String:variant_key.utf8().get_data()] autorelease];
if (key == NULL) {
@@ -237,10 +237,8 @@ Variant ICloud::set_key_values(Variant p_params) {
return error_keys;
}
-Variant ICloud::get_key_value(Variant p_param) {
- String param = p_param;
-
- NSString *key = [[[NSString alloc] initWithUTF8String:param.utf8().get_data()] autorelease];
+Variant ICloud::get_key_value(String p_param) {
+ NSString *key = [[[NSString alloc] initWithUTF8String:p_param.utf8().get_data()] autorelease];
NSUbiquitousKeyValueStore *store = [NSUbiquitousKeyValueStore defaultStore];
if (![[store dictionaryRepresentation] objectForKey:key]) {
diff --git a/platform/iphone/in_app_store.h b/platform/iphone/in_app_store.h
index 44e65e77ed..beb58af2c7 100644
--- a/platform/iphone/in_app_store.h
+++ b/platform/iphone/in_app_store.h
@@ -44,9 +44,9 @@ class InAppStore : public Object {
List<Variant> pending_events;
public:
- Error request_product_info(Variant p_params);
+ Error request_product_info(Dictionary p_params);
Error restore_purchases();
- Error purchase(Variant p_params);
+ Error purchase(Dictionary p_params);
int get_pending_event_count();
Variant pop_pending_event();
diff --git a/platform/iphone/in_app_store.mm b/platform/iphone/in_app_store.mm
index 548dcc549d..dfec5d7634 100644
--- a/platform/iphone/in_app_store.mm
+++ b/platform/iphone/in_app_store.mm
@@ -39,8 +39,10 @@ extern "C" {
bool auto_finish_transactions = true;
NSMutableDictionary *pending_transactions = [NSMutableDictionary dictionary];
+static NSArray *latestProducts;
@interface SKProduct (LocalizedPrice)
+
@property(nonatomic, readonly) NSString *localizedPrice;
@end
@@ -82,6 +84,8 @@ void InAppStore::_bind_methods() {
- (void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response {
NSArray *products = response.products;
+ latestProducts = products;
+
Dictionary ret;
ret["type"] = "product_info";
ret["result"] = "ok";
@@ -126,11 +130,10 @@ void InAppStore::_bind_methods() {
@end
-Error InAppStore::request_product_info(Variant p_params) {
- Dictionary params = p_params;
- ERR_FAIL_COND_V(!params.has("product_ids"), ERR_INVALID_PARAMETER);
+Error InAppStore::request_product_info(Dictionary p_params) {
+ ERR_FAIL_COND_V(!p_params.has("product_ids"), ERR_INVALID_PARAMETER);
- PackedStringArray pids = params["product_ids"];
+ PackedStringArray pids = p_params["product_ids"];
printf("************ request product info! %i\n", pids.size());
NSMutableArray *array = [[[NSMutableArray alloc] initWithCapacity:pids.size()] autorelease];
@@ -198,11 +201,11 @@ Error InAppStore::restore_purchases() {
// which is still available in iOS 7.
// Use SKPaymentTransaction's transactionReceipt.
- receipt = transaction.transactionReceipt;
+ receipt = [NSData dataWithContentsOfURL:[[NSBundle mainBundle] appStoreReceiptURL]];
}
} else {
- receipt = transaction.transactionReceipt;
+ receipt = [NSData dataWithContentsOfURL:[[NSBundle mainBundle] appStoreReceiptURL]];
}
NSString *receipt_to_send = nil;
@@ -254,17 +257,32 @@ Error InAppStore::restore_purchases() {
@end
-Error InAppStore::purchase(Variant p_params) {
+Error InAppStore::purchase(Dictionary p_params) {
ERR_FAIL_COND_V(![SKPaymentQueue canMakePayments], ERR_UNAVAILABLE);
if (![SKPaymentQueue canMakePayments])
return ERR_UNAVAILABLE;
printf("purchasing!\n");
- Dictionary params = p_params;
- ERR_FAIL_COND_V(!params.has("product_id"), ERR_INVALID_PARAMETER);
+ ERR_FAIL_COND_V(!p_params.has("product_id"), ERR_INVALID_PARAMETER);
+
+ NSString *pid = [[[NSString alloc] initWithUTF8String:String(p_params["product_id"]).utf8().get_data()] autorelease];
+
+ SKProduct *product = nil;
+
+ if (latestProducts) {
+ for (SKProduct *storedProduct in latestProducts) {
+ if ([storedProduct.productIdentifier isEqualToString:pid]) {
+ product = storedProduct;
+ break;
+ }
+ }
+ }
+
+ if (!product) {
+ return ERR_INVALID_PARAMETER;
+ }
- NSString *pid = [[[NSString alloc] initWithUTF8String:String(params["product_id"]).utf8().get_data()] autorelease];
- SKPayment *payment = [SKPayment paymentWithProductIdentifier:pid];
+ SKPayment *payment = [SKPayment paymentWithProduct:product];
SKPaymentQueue *defq = [SKPaymentQueue defaultQueue];
[defq addPayment:payment];
printf("purchase sent!\n");
diff --git a/platform/iphone/ios.mm b/platform/iphone/ios.mm
index 5923f558a5..ad26d0ada3 100644
--- a/platform/iphone/ios.mm
+++ b/platform/iphone/ios.mm
@@ -29,17 +29,27 @@
/*************************************************************************/
#include "ios.h"
-#include <sys/sysctl.h>
-
+#import "app_delegate.h"
#import <UIKit/UIKit.h>
+#include <sys/sysctl.h>
void iOS::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_rate_url", "app_id"), &iOS::get_rate_url);
};
void iOS::alert(const char *p_alert, const char *p_title) {
- UIAlertView *alert = [[[UIAlertView alloc] initWithTitle:[NSString stringWithUTF8String:p_title] message:[NSString stringWithUTF8String:p_alert] delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil, nil] autorelease];
- [alert show];
+ NSString *title = [NSString stringWithUTF8String:p_title];
+ NSString *message = [NSString stringWithUTF8String:p_alert];
+
+ UIAlertController *alert = [UIAlertController alertControllerWithTitle:title message:message preferredStyle:UIAlertControllerStyleAlert];
+ UIAlertAction *button = [UIAlertAction actionWithTitle:@"OK"
+ style:UIAlertActionStyleCancel
+ handler:^(id){
+ }];
+
+ [alert addAction:button];
+
+ [AppDelegate.viewController presentViewController:alert animated:YES completion:nil];
}
String iOS::get_model() const {
diff --git a/platform/iphone/joypad_iphone.h b/platform/iphone/joypad_iphone.h
new file mode 100644
index 0000000000..85e26e1dc8
--- /dev/null
+++ b/platform/iphone/joypad_iphone.h
@@ -0,0 +1,50 @@
+/*************************************************************************/
+/* joypad_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. */
+/*************************************************************************/
+
+#import <GameController/GameController.h>
+
+@interface JoypadIPhoneObserver : NSObject
+
+- (void)startObserving;
+- (void)startProcessing;
+- (void)finishObserving;
+
+@end
+
+class JoypadIPhone {
+private:
+ JoypadIPhoneObserver *observer;
+
+public:
+ JoypadIPhone();
+ ~JoypadIPhone();
+
+ void start_processing();
+};
diff --git a/platform/iphone/joypad_iphone.mm b/platform/iphone/joypad_iphone.mm
new file mode 100644
index 0000000000..6088f1c25c
--- /dev/null
+++ b/platform/iphone/joypad_iphone.mm
@@ -0,0 +1,380 @@
+/*************************************************************************/
+/* joypad_iphone.mm */
+/*************************************************************************/
+/* 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. */
+/*************************************************************************/
+
+#import "joypad_iphone.h"
+#include "core/project_settings.h"
+#include "drivers/coreaudio/audio_driver_coreaudio.h"
+#include "main/main.h"
+
+#import "godot_view.h"
+
+#include "os_iphone.h"
+
+JoypadIPhone::JoypadIPhone() {
+ observer = [[JoypadIPhoneObserver alloc] init];
+ [observer startObserving];
+}
+
+JoypadIPhone::~JoypadIPhone() {
+ if (observer) {
+ [observer finishObserving];
+ observer = nil;
+ }
+}
+
+void JoypadIPhone::start_processing() {
+ if (observer) {
+ [observer startProcessing];
+ }
+}
+
+@interface JoypadIPhoneObserver ()
+
+@property(assign, nonatomic) BOOL isObserving;
+@property(assign, nonatomic) BOOL isProcessing;
+@property(strong, nonatomic) NSMutableDictionary *connectedJoypads;
+@property(strong, nonatomic) NSMutableArray *joypadsQueue;
+
+@end
+
+@implementation JoypadIPhoneObserver
+
+- (instancetype)init {
+ self = [super init];
+
+ if (self) {
+ [self godot_commonInit];
+ }
+
+ return self;
+}
+
+- (void)godot_commonInit {
+ self.isObserving = NO;
+ self.isProcessing = NO;
+}
+
+- (void)startProcessing {
+ self.isProcessing = YES;
+
+ for (GCController *controller in self.joypadsQueue) {
+ [self addiOSJoypad:controller];
+ }
+
+ [self.joypadsQueue removeAllObjects];
+}
+
+- (void)startObserving {
+ if (self.isObserving) {
+ return;
+ }
+
+ self.isObserving = YES;
+
+ self.connectedJoypads = [NSMutableDictionary dictionary];
+ self.joypadsQueue = [NSMutableArray array];
+
+ // get told when controllers connect, this will be called right away for
+ // already connected controllers
+ [[NSNotificationCenter defaultCenter]
+ addObserver:self
+ selector:@selector(controllerWasConnected:)
+ name:GCControllerDidConnectNotification
+ object:nil];
+
+ // get told when controllers disconnect
+ [[NSNotificationCenter defaultCenter]
+ addObserver:self
+ selector:@selector(controllerWasDisconnected:)
+ name:GCControllerDidDisconnectNotification
+ object:nil];
+}
+
+- (void)finishObserving {
+ if (self.isObserving) {
+ [[NSNotificationCenter defaultCenter] removeObserver:self];
+ }
+
+ self.isObserving = NO;
+ self.isProcessing = NO;
+
+ self.connectedJoypads = nil;
+ self.joypadsQueue = nil;
+}
+
+- (void)dealloc {
+ [self finishObserving];
+
+ [super dealloc];
+}
+
+- (int)getJoyIdForController:(GCController *)controller {
+ NSArray *keys = [self.connectedJoypads allKeysForObject:controller];
+
+ for (NSNumber *key in keys) {
+ int joy_id = [key intValue];
+ return joy_id;
+ };
+
+ return -1;
+};
+
+- (void)addiOSJoypad:(GCController *)controller {
+ // get a new id for our controller
+ int joy_id = Input::get_singleton()->get_unused_joy_id();
+
+ if (joy_id == -1) {
+ printf("Couldn't retrieve new joy id\n");
+ return;
+ }
+
+ // assign our player index
+ if (controller.playerIndex == GCControllerPlayerIndexUnset) {
+ controller.playerIndex = [self getFreePlayerIndex];
+ };
+
+ // tell Godot about our new controller
+ Input::get_singleton()->joy_connection_changed(joy_id, true, [controller.vendorName UTF8String]);
+
+ // add it to our dictionary, this will retain our controllers
+ [self.connectedJoypads setObject:controller forKey:[NSNumber numberWithInt:joy_id]];
+
+ // set our input handler
+ [self setControllerInputHandler:controller];
+}
+
+- (void)controllerWasConnected:(NSNotification *)notification {
+ // get our controller
+ GCController *controller = (GCController *)notification.object;
+
+ if (!controller) {
+ printf("Couldn't retrieve new controller\n");
+ return;
+ }
+
+ if ([[self.connectedJoypads allKeysForObject:controller] count] > 0) {
+ printf("Controller is already registered\n");
+ } else if (!self.isProcessing) {
+ [self.joypadsQueue addObject:controller];
+ } else {
+ [self addiOSJoypad:controller];
+ }
+}
+
+- (void)controllerWasDisconnected:(NSNotification *)notification {
+ // find our joystick, there should be only one in our dictionary
+ GCController *controller = (GCController *)notification.object;
+
+ if (!controller) {
+ return;
+ }
+
+ NSArray *keys = [self.connectedJoypads allKeysForObject:controller];
+ for (NSNumber *key in keys) {
+ // tell Godot this joystick is no longer there
+ int joy_id = [key intValue];
+ Input::get_singleton()->joy_connection_changed(joy_id, false, "");
+
+ // and remove it from our dictionary
+ [self.connectedJoypads removeObjectForKey:key];
+ };
+};
+
+- (GCControllerPlayerIndex)getFreePlayerIndex {
+ bool have_player_1 = false;
+ bool have_player_2 = false;
+ bool have_player_3 = false;
+ bool have_player_4 = false;
+
+ if (self.connectedJoypads == nil) {
+ NSArray *keys = [self.connectedJoypads allKeys];
+ for (NSNumber *key in keys) {
+ GCController *controller = [self.connectedJoypads objectForKey:key];
+ if (controller.playerIndex == GCControllerPlayerIndex1) {
+ have_player_1 = true;
+ } else if (controller.playerIndex == GCControllerPlayerIndex2) {
+ have_player_2 = true;
+ } else if (controller.playerIndex == GCControllerPlayerIndex3) {
+ have_player_3 = true;
+ } else if (controller.playerIndex == GCControllerPlayerIndex4) {
+ have_player_4 = true;
+ };
+ };
+ };
+
+ if (!have_player_1) {
+ return GCControllerPlayerIndex1;
+ } else if (!have_player_2) {
+ return GCControllerPlayerIndex2;
+ } else if (!have_player_3) {
+ return GCControllerPlayerIndex3;
+ } else if (!have_player_4) {
+ return GCControllerPlayerIndex4;
+ } else {
+ return GCControllerPlayerIndexUnset;
+ };
+}
+
+- (void)setControllerInputHandler:(GCController *)controller {
+ // Hook in the callback handler for the correct gamepad profile.
+ // This is a bit of a weird design choice on Apples part.
+ // You need to select the most capable gamepad profile for the
+ // gamepad attached.
+ if (controller.extendedGamepad != nil) {
+ // The extended gamepad profile has all the input you could possibly find on
+ // a gamepad but will only be active if your gamepad actually has all of
+ // these...
+ controller.extendedGamepad.valueChangedHandler = ^(
+ GCExtendedGamepad *gamepad, GCControllerElement *element) {
+ int joy_id = [self getJoyIdForController:controller];
+
+ if (element == gamepad.buttonA) {
+ Input::get_singleton()->joy_button(joy_id, JOY_BUTTON_A,
+ gamepad.buttonA.isPressed);
+ } else if (element == gamepad.buttonB) {
+ Input::get_singleton()->joy_button(joy_id, JOY_BUTTON_B,
+ gamepad.buttonB.isPressed);
+ } else if (element == gamepad.buttonX) {
+ Input::get_singleton()->joy_button(joy_id, JOY_BUTTON_X,
+ gamepad.buttonX.isPressed);
+ } else if (element == gamepad.buttonY) {
+ Input::get_singleton()->joy_button(joy_id, JOY_BUTTON_Y,
+ gamepad.buttonY.isPressed);
+ } else if (element == gamepad.leftShoulder) {
+ Input::get_singleton()->joy_button(joy_id, JOY_BUTTON_LEFT_SHOULDER,
+ gamepad.leftShoulder.isPressed);
+ } else if (element == gamepad.rightShoulder) {
+ Input::get_singleton()->joy_button(joy_id, JOY_BUTTON_RIGHT_SHOULDER,
+ gamepad.rightShoulder.isPressed);
+ } else if (element == gamepad.dpad) {
+ Input::get_singleton()->joy_button(joy_id, JOY_BUTTON_DPAD_UP,
+ gamepad.dpad.up.isPressed);
+ Input::get_singleton()->joy_button(joy_id, JOY_BUTTON_DPAD_DOWN,
+ gamepad.dpad.down.isPressed);
+ Input::get_singleton()->joy_button(joy_id, JOY_BUTTON_DPAD_LEFT,
+ gamepad.dpad.left.isPressed);
+ Input::get_singleton()->joy_button(joy_id, JOY_BUTTON_DPAD_RIGHT,
+ gamepad.dpad.right.isPressed);
+ };
+
+ Input::JoyAxis jx;
+ jx.min = -1;
+ if (element == gamepad.leftThumbstick) {
+ jx.value = gamepad.leftThumbstick.xAxis.value;
+ Input::get_singleton()->joy_axis(joy_id, JOY_AXIS_LEFT_X, jx);
+ jx.value = -gamepad.leftThumbstick.yAxis.value;
+ Input::get_singleton()->joy_axis(joy_id, JOY_AXIS_LEFT_Y, jx);
+ } else if (element == gamepad.rightThumbstick) {
+ jx.value = gamepad.rightThumbstick.xAxis.value;
+ Input::get_singleton()->joy_axis(joy_id, JOY_AXIS_RIGHT_X, jx);
+ jx.value = -gamepad.rightThumbstick.yAxis.value;
+ Input::get_singleton()->joy_axis(joy_id, JOY_AXIS_RIGHT_Y, jx);
+ } else if (element == gamepad.leftTrigger) {
+ jx.value = gamepad.leftTrigger.value;
+ Input::get_singleton()->joy_axis(joy_id, JOY_AXIS_TRIGGER_LEFT, jx);
+ } else if (element == gamepad.rightTrigger) {
+ jx.value = gamepad.rightTrigger.value;
+ Input::get_singleton()->joy_axis(joy_id, JOY_AXIS_TRIGGER_RIGHT, jx);
+ };
+ };
+ }
+ // else if (controller.gamepad != nil) {
+ // // gamepad is the standard profile with 4 buttons, shoulder buttons and a
+ // // D-pad
+ // controller.gamepad.valueChangedHandler = ^(GCGamepad *gamepad,
+ // GCControllerElement *element) {
+ // int joy_id = [self getJoyIdForController:controller];
+ //
+ // if (element == gamepad.buttonA) {
+ // Input::get_singleton()->joy_button(joy_id, JOY_BUTTON_A,
+ // gamepad.buttonA.isPressed);
+ // } else if (element == gamepad.buttonB) {
+ // Input::get_singleton()->joy_button(joy_id, JOY_BUTTON_B,
+ // gamepad.buttonB.isPressed);
+ // } else if (element == gamepad.buttonX) {
+ // Input::get_singleton()->joy_button(joy_id, JOY_BUTTON_X,
+ // gamepad.buttonX.isPressed);
+ // } else if (element == gamepad.buttonY) {
+ // Input::get_singleton()->joy_button(joy_id, JOY_BUTTON_Y,
+ // gamepad.buttonY.isPressed);
+ // } else if (element == gamepad.leftShoulder) {
+ // Input::get_singleton()->joy_button(joy_id, JOY_BUTTON_LEFT_SHOULDER,
+ // gamepad.leftShoulder.isPressed);
+ // } else if (element == gamepad.rightShoulder) {
+ // Input::get_singleton()->joy_button(joy_id, JOY_BUTTON_RIGHT_SHOULDER,
+ // gamepad.rightShoulder.isPressed);
+ // } else if (element == gamepad.dpad) {
+ // Input::get_singleton()->joy_button(joy_id, JOY_BUTTON_DPAD_UP,
+ // gamepad.dpad.up.isPressed);
+ // Input::get_singleton()->joy_button(joy_id, JOY_BUTTON_DPAD_DOWN,
+ // gamepad.dpad.down.isPressed);
+ // Input::get_singleton()->joy_button(joy_id, JOY_BUTTON_DPAD_LEFT,
+ // gamepad.dpad.left.isPressed);
+ // Input::get_singleton()->joy_button(joy_id, JOY_BUTTON_DPAD_RIGHT,
+ // gamepad.dpad.right.isPressed);
+ // };
+ // };
+ //#ifdef ADD_MICRO_GAMEPAD // disabling this for now, only available on iOS 9+,
+ // // while we are setting that as the minimum, seems our
+ // // build environment doesn't like it
+ // } else if (controller.microGamepad != nil) {
+ // // micro gamepads were added in OS 9 and feature just 2 buttons and a d-pad
+ // controller.microGamepad.valueChangedHandler =
+ // ^(GCMicroGamepad *gamepad, GCControllerElement *element) {
+ // int joy_id = [self getJoyIdForController:controller];
+ //
+ // if (element == gamepad.buttonA) {
+ // Input::get_singleton()->joy_button(joy_id, JOY_BUTTON_A,
+ // gamepad.buttonA.isPressed);
+ // } else if (element == gamepad.buttonX) {
+ // Input::get_singleton()->joy_button(joy_id, JOY_BUTTON_X,
+ // gamepad.buttonX.isPressed);
+ // } else if (element == gamepad.dpad) {
+ // Input::get_singleton()->joy_button(joy_id, JOY_BUTTON_DPAD_UP,
+ // gamepad.dpad.up.isPressed);
+ // Input::get_singleton()->joy_button(joy_id, JOY_BUTTON_DPAD_DOWN,
+ // gamepad.dpad.down.isPressed);
+ // Input::get_singleton()->joy_button(joy_id, JOY_BUTTON_DPAD_LEFT,
+ // gamepad.dpad.left.isPressed);
+ // Input::get_singleton()->joy_button(joy_id, JOY_BUTTON_DPAD_RIGHT,
+ // gamepad.dpad.right.isPressed);
+ // };
+ // };
+ //#endif
+ // };
+
+ ///@TODO need to add support for controller.motion which gives us access to
+ /// the orientation of the device (if supported)
+
+ ///@TODO need to add support for controllerPausedHandler which should be a
+ /// toggle
+};
+
+@end
diff --git a/platform/iphone/main.m b/platform/iphone/main.m
index 164db2a74b..c292f02822 100644
--- a/platform/iphone/main.m
+++ b/platform/iphone/main.m
@@ -32,20 +32,25 @@
#import <UIKit/UIKit.h>
#include <stdio.h>
+#include <vulkan/vulkan.h>
int gargc;
char **gargv;
int main(int argc, char *argv[]) {
+#if defined(VULKAN_ENABLED)
+ //MoltenVK - enable full component swizzling support
+ setenv("MVK_CONFIG_FULL_IMAGE_VIEW_SWIZZLE", "1", 1);
+#endif
+
printf("*********** main.m\n");
gargc = argc;
gargv = argv;
- NSAutoreleasePool *pool = [NSAutoreleasePool new];
- AppDelegate *app = [AppDelegate alloc];
printf("running app main\n");
- UIApplicationMain(argc, argv, nil, @"AppDelegate");
- printf("main done, pool release\n");
- [pool release];
+ @autoreleasepool {
+ UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
+ }
+ printf("main done\n");
return 0;
}
diff --git a/platform/iphone/os_iphone.cpp b/platform/iphone/os_iphone.cpp
deleted file mode 100644
index 41dd623e69..0000000000
--- a/platform/iphone/os_iphone.cpp
+++ /dev/null
@@ -1,632 +0,0 @@
-/*************************************************************************/
-/* os_iphone.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. */
-/*************************************************************************/
-
-#ifdef IPHONE_ENABLED
-
-#include "os_iphone.h"
-
-#if defined(OPENGL_ENABLED)
-#include "drivers/gles2/rasterizer_gles2.h"
-#endif
-
-#if defined(VULKAN_ENABLED)
-#include "servers/rendering/rasterizer_rd/rasterizer_rd.h"
-// #import <QuartzCore/CAMetalLayer.h>
-#include <vulkan/vulkan_metal.h>
-#endif
-
-#include "servers/rendering/rendering_server_raster.h"
-#include "servers/rendering/rendering_server_wrap_mt.h"
-
-#include "main/main.h"
-
-#include "core/io/file_access_pack.h"
-#include "core/os/dir_access.h"
-#include "core/os/file_access.h"
-#include "core/project_settings.h"
-#include "drivers/unix/syslog_logger.h"
-
-#include "semaphore_iphone.h"
-
-#include <dlfcn.h>
-
-int OSIPhone::get_video_driver_count() const {
- return 2;
-};
-
-const char *OSIPhone::get_video_driver_name(int p_driver) const {
- switch (p_driver) {
- case VIDEO_DRIVER_GLES2:
- return "GLES2";
- }
- ERR_FAIL_V_MSG(nullptr, "Invalid video driver index: " + itos(p_driver) + ".");
-};
-
-OSIPhone *OSIPhone::get_singleton() {
- return (OSIPhone *)OS::get_singleton();
-};
-
-extern int gl_view_base_fb; // from gl_view.mm
-
-void OSIPhone::set_data_dir(String p_dir) {
- DirAccess *da = DirAccess::open(p_dir);
-
- data_dir = da->get_current_dir();
- printf("setting data dir to %ls from %ls\n", data_dir.c_str(), p_dir.c_str());
- memdelete(da);
-};
-
-void OSIPhone::set_unique_id(String p_id) {
- unique_id = p_id;
-};
-
-String OSIPhone::get_unique_id() const {
- return unique_id;
-};
-
-void OSIPhone::initialize_core() {
- OS_Unix::initialize_core();
-
- set_data_dir(data_dir);
-};
-
-int OSIPhone::get_current_video_driver() const {
- return video_driver_index;
-}
-
-Error OSIPhone::initialize(const VideoMode &p_desired, int p_video_driver, int p_audio_driver) {
- video_driver_index = p_video_driver;
-
-#if defined(OPENGL_ENABLED)
- bool gl_initialization_error = false;
-
- // FIXME: Add Vulkan support via MoltenVK. Add fallback code back?
-
- 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.",
- "Unable to initialize video driver");
- return ERR_UNAVAILABLE;
- }
-#endif
-
-#if defined(VULKAN_ENABLED)
- RasterizerRD::make_current();
-#endif
-
- rendering_server = memnew(RenderingServerRaster);
- // FIXME: Reimplement threaded rendering
- if (get_render_thread_mode() != RENDER_THREAD_UNSAFE) {
- rendering_server = memnew(RenderingServerWrapMT(rendering_server, false));
- }
- rendering_server->init();
- //rendering_server->cursor_set_visible(false, 0);
-
-#if defined(OPENGL_ENABLED)
- // reset this to what it should be, it will have been set to 0 after rendering_server->init() is called
- RasterizerStorageGLES2::system_fbo = gl_view_base_fb;
-#endif
-
- AudioDriverManager::initialize(p_audio_driver);
-
- input = memnew(InputDefault);
-
-#ifdef GAME_CENTER_ENABLED
- game_center = memnew(GameCenter);
- Engine::get_singleton()->add_singleton(Engine::Singleton("GameCenter", game_center));
- game_center->connect();
-#endif
-
-#ifdef STOREKIT_ENABLED
- store_kit = memnew(InAppStore);
- Engine::get_singleton()->add_singleton(Engine::Singleton("InAppStore", store_kit));
-#endif
-
-#ifdef ICLOUD_ENABLED
- icloud = memnew(ICloud);
- Engine::get_singleton()->add_singleton(Engine::Singleton("ICloud", icloud));
- //icloud->connect();
-#endif
- ios = memnew(iOS);
- Engine::get_singleton()->add_singleton(Engine::Singleton("iOS", ios));
-
- return OK;
-};
-
-MainLoop *OSIPhone::get_main_loop() const {
- return main_loop;
-};
-
-void OSIPhone::set_main_loop(MainLoop *p_main_loop) {
- main_loop = p_main_loop;
-
- if (main_loop) {
- input->set_main_loop(p_main_loop);
- main_loop->init();
- }
-};
-
-bool OSIPhone::iterate() {
- if (!main_loop)
- return true;
-
- if (main_loop) {
- for (int i = 0; i < event_count; i++) {
- input->parse_input_event(event_queue[i]);
- };
- };
- event_count = 0;
-
- return Main::iteration();
-};
-
-void OSIPhone::key(uint32_t p_key, bool p_pressed) {
- Ref<InputEventKey> ev;
- ev.instance();
- ev->set_echo(false);
- ev->set_pressed(p_pressed);
- ev->set_keycode(p_key);
- ev->set_physical_keycode(p_key);
- ev->set_unicode(p_key);
- queue_event(ev);
-};
-
-void OSIPhone::touch_press(int p_idx, int p_x, int p_y, bool p_pressed, bool p_doubleclick) {
- if (!GLOBAL_DEF("debug/disable_touch", false)) {
- Ref<InputEventScreenTouch> ev;
- ev.instance();
-
- ev->set_index(p_idx);
- ev->set_pressed(p_pressed);
- ev->set_position(Vector2(p_x, p_y));
- queue_event(ev);
- };
-
- touch_list.pressed[p_idx] = p_pressed;
-};
-
-void OSIPhone::touch_drag(int p_idx, int p_prev_x, int p_prev_y, int p_x, int p_y) {
- if (!GLOBAL_DEF("debug/disable_touch", false)) {
- Ref<InputEventScreenDrag> ev;
- ev.instance();
- ev->set_index(p_idx);
- ev->set_position(Vector2(p_x, p_y));
- ev->set_relative(Vector2(p_x - p_prev_x, p_y - p_prev_y));
- queue_event(ev);
- };
-};
-
-void OSIPhone::queue_event(const Ref<InputEvent> &p_event) {
- ERR_FAIL_INDEX(event_count, MAX_EVENTS);
-
- event_queue[event_count++] = p_event;
-};
-
-void OSIPhone::touches_cancelled() {
- for (int i = 0; i < MAX_MOUSE_COUNT; i++) {
- if (touch_list.pressed[i]) {
- // send a mouse_up outside the screen
- touch_press(i, -1, -1, false, false);
- };
- };
-};
-
-static const float ACCEL_RANGE = 1;
-
-void OSIPhone::update_gravity(float p_x, float p_y, float p_z) {
- input->set_gravity(Vector3(p_x, p_y, p_z));
-};
-
-void OSIPhone::update_accelerometer(float p_x, float p_y, float p_z) {
- // Found out the Z should not be negated! Pass as is!
- input->set_accelerometer(Vector3(p_x / (float)ACCEL_RANGE, p_y / (float)ACCEL_RANGE, p_z / (float)ACCEL_RANGE));
-
- /*
- if (p_x != last_accel.x) {
- //printf("updating accel x %f\n", p_x);
- InputEvent ev;
- ev.type = InputEvent::JOYPAD_MOTION;
- ev.device = 0;
- ev.joy_motion.axis = JOY_ANALOG_0;
- ev.joy_motion.axis_value = (p_x / (float)ACCEL_RANGE);
- last_accel.x = p_x;
- queue_event(ev);
- };
- if (p_y != last_accel.y) {
- //printf("updating accel y %f\n", p_y);
- InputEvent ev;
- ev.type = InputEvent::JOYPAD_MOTION;
- ev.device = 0;
- ev.joy_motion.axis = JOY_ANALOG_1;
- ev.joy_motion.axis_value = (p_y / (float)ACCEL_RANGE);
- last_accel.y = p_y;
- queue_event(ev);
- };
- if (p_z != last_accel.z) {
- //printf("updating accel z %f\n", p_z);
- InputEvent ev;
- ev.type = InputEvent::JOYPAD_MOTION;
- ev.device = 0;
- ev.joy_motion.axis = JOY_ANALOG_2;
- ev.joy_motion.axis_value = ( (1.0 - p_z) / (float)ACCEL_RANGE);
- last_accel.z = p_z;
- queue_event(ev);
- };
- */
-};
-
-void OSIPhone::update_magnetometer(float p_x, float p_y, float p_z) {
- input->set_magnetometer(Vector3(p_x, p_y, p_z));
-};
-
-void OSIPhone::update_gyroscope(float p_x, float p_y, float p_z) {
- input->set_gyroscope(Vector3(p_x, p_y, p_z));
-};
-
-int OSIPhone::get_unused_joy_id() {
- return input->get_unused_joy_id();
-};
-
-void OSIPhone::joy_connection_changed(int p_idx, bool p_connected, String p_name) {
- input->joy_connection_changed(p_idx, p_connected, p_name);
-};
-
-void OSIPhone::joy_button(int p_device, int p_button, bool p_pressed) {
- input->joy_button(p_device, p_button, p_pressed);
-};
-
-void OSIPhone::joy_axis(int p_device, int p_axis, const InputDefault::JoyAxis &p_value) {
- input->joy_axis(p_device, p_axis, p_value);
-};
-
-void OSIPhone::delete_main_loop() {
- if (main_loop) {
- main_loop->finish();
- memdelete(main_loop);
- };
-
- main_loop = nullptr;
-};
-
-void OSIPhone::finalize() {
- delete_main_loop();
-
- memdelete(input);
- memdelete(ios);
-
-#ifdef GAME_CENTER_ENABLED
- memdelete(game_center);
-#endif
-
-#ifdef STOREKIT_ENABLED
- memdelete(store_kit);
-#endif
-
-#ifdef ICLOUD_ENABLED
- memdelete(icloud);
-#endif
-
- rendering_server->finish();
- memdelete(rendering_server);
- // memdelete(rasterizer);
-
- // Free unhandled events before close
- for (int i = 0; i < MAX_EVENTS; i++) {
- event_queue[i].unref();
- };
- event_count = 0;
-};
-
-void OSIPhone::set_mouse_show(bool p_show) {}
-void OSIPhone::set_mouse_grab(bool p_grab) {}
-
-bool OSIPhone::is_mouse_grab_enabled() const {
- return true;
-};
-
-Point2 OSIPhone::get_mouse_position() const {
- return Point2();
-};
-
-int OSIPhone::get_mouse_button_state() const {
- return 0;
-};
-
-void OSIPhone::set_window_title(const String &p_title) {}
-
-void OSIPhone::alert(const String &p_alert, const String &p_title) {
- const CharString utf8_alert = p_alert.utf8();
- const CharString utf8_title = p_title.utf8();
- iOS::alert(utf8_alert.get_data(), utf8_title.get_data());
-}
-
-Error OSIPhone::open_dynamic_library(const String p_path, void *&p_library_handle, bool p_also_set_library_path) {
- if (p_path.length() == 0) {
- p_library_handle = RTLD_SELF;
- return OK;
- }
- return OS_Unix::open_dynamic_library(p_path, p_library_handle, p_also_set_library_path);
-}
-
-Error OSIPhone::close_dynamic_library(void *p_library_handle) {
- if (p_library_handle == RTLD_SELF) {
- return OK;
- }
- return OS_Unix::close_dynamic_library(p_library_handle);
-}
-
-HashMap<String, void *> OSIPhone::dynamic_symbol_lookup_table;
-void register_dynamic_symbol(char *name, void *address) {
- OSIPhone::dynamic_symbol_lookup_table[String(name)] = address;
-}
-
-Error OSIPhone::get_dynamic_library_symbol_handle(void *p_library_handle, const String p_name, void *&p_symbol_handle, bool p_optional) {
- if (p_library_handle == RTLD_SELF) {
- void **ptr = OSIPhone::dynamic_symbol_lookup_table.getptr(p_name);
- if (ptr) {
- p_symbol_handle = *ptr;
- return OK;
- }
- }
- return OS_Unix::get_dynamic_library_symbol_handle(p_library_handle, p_name, p_symbol_handle, p_optional);
-}
-
-void OSIPhone::set_video_mode(const VideoMode &p_video_mode, int p_screen) {
- video_mode = p_video_mode;
-};
-
-OS::VideoMode OSIPhone::get_video_mode(int p_screen) const {
- return video_mode;
-};
-
-void OSIPhone::get_fullscreen_mode_list(List<VideoMode> *p_list, int p_screen) const {
- p_list->push_back(video_mode);
-};
-
-bool OSIPhone::can_draw() const {
- if (native_video_is_playing())
- return false;
- return true;
-};
-
-int OSIPhone::set_base_framebuffer(int p_fb) {
-#if defined(OPENGL_ENABLED)
- // gl_view_base_fb has not been updated yet
- RasterizerStorageGLES2::system_fbo = p_fb;
-#endif
-
- return 0;
-};
-
-bool OSIPhone::has_virtual_keyboard() const {
- return true;
-};
-
-extern void _show_keyboard(String p_existing);
-extern void _hide_keyboard();
-extern Error _shell_open(String p_uri);
-extern void _set_keep_screen_on(bool p_enabled);
-extern void _vibrate();
-
-void OSIPhone::show_virtual_keyboard(const String &p_existing_text, const Rect2 &p_screen_rect, int p_max_input_length, int p_cursor_start, int p_cursor_end) {
- _show_keyboard(p_existing_text);
-};
-
-void OSIPhone::hide_virtual_keyboard() {
- _hide_keyboard();
-};
-
-void OSIPhone::set_virtual_keyboard_height(int p_height) {
- virtual_keyboard_height = p_height;
-}
-
-int OSIPhone::get_virtual_keyboard_height() const {
- return virtual_keyboard_height;
-}
-
-Error OSIPhone::shell_open(String p_uri) {
- return _shell_open(p_uri);
-};
-
-void OSIPhone::set_keep_screen_on(bool p_enabled) {
- OS::set_keep_screen_on(p_enabled);
- _set_keep_screen_on(p_enabled);
-};
-
-String OSIPhone::get_user_data_dir() const {
- return data_dir;
-};
-
-String OSIPhone::get_name() const {
- return "iOS";
-};
-
-String OSIPhone::get_model_name() const {
- String model = ios->get_model();
- if (model != "")
- return model;
-
- return OS_Unix::get_model_name();
-}
-
-Size2 OSIPhone::get_window_size() const {
- return Vector2(video_mode.width, video_mode.height);
-}
-
-extern Rect2 _get_ios_window_safe_area(float p_window_width, float p_window_height);
-
-Rect2 OSIPhone::get_window_safe_area() const {
- return _get_ios_window_safe_area(video_mode.width, video_mode.height);
-}
-
-bool OSIPhone::has_touchscreen_ui_hint() const {
- return true;
-}
-
-void OSIPhone::set_locale(String p_locale) {
- locale_code = p_locale;
-}
-
-String OSIPhone::get_locale() const {
- return locale_code;
-}
-
-extern bool _play_video(String p_path, float p_volume, String p_audio_track, String p_subtitle_track);
-extern bool _is_video_playing();
-extern void _pause_video();
-extern void _unpause_video();
-extern void _stop_video();
-extern void _focus_out_video();
-
-Error OSIPhone::native_video_play(String p_path, float p_volume, String p_audio_track, String p_subtitle_track) {
- FileAccess *f = FileAccess::open(p_path, FileAccess::READ);
- bool exists = f && f->is_open();
-
- String tempFile = get_user_data_dir();
- if (!exists)
- return FAILED;
-
- if (p_path.begins_with("res://")) {
- if (PackedData::get_singleton()->has_path(p_path)) {
- print("Unable to play %S using the native player as it resides in a .pck file\n", p_path.c_str());
- return ERR_INVALID_PARAMETER;
- } else {
- p_path = p_path.replace("res:/", ProjectSettings::get_singleton()->get_resource_path());
- }
- } else if (p_path.begins_with("user://"))
- p_path = p_path.replace("user:/", get_user_data_dir());
-
- memdelete(f);
-
- print("Playing video: %S\n", p_path.c_str());
- if (_play_video(p_path, p_volume, p_audio_track, p_subtitle_track))
- return OK;
- return FAILED;
-}
-
-bool OSIPhone::native_video_is_playing() const {
- return _is_video_playing();
-}
-
-void OSIPhone::native_video_pause() {
- if (native_video_is_playing())
- _pause_video();
-}
-
-void OSIPhone::native_video_unpause() {
- _unpause_video();
-};
-
-void OSIPhone::native_video_focus_out() {
- _focus_out_video();
-};
-
-void OSIPhone::native_video_stop() {
- if (native_video_is_playing())
- _stop_video();
-}
-
-void OSIPhone::vibrate_handheld(int p_duration_ms) {
- // iOS does not support duration for vibration
- _vibrate();
-}
-
-bool OSIPhone::_check_internal_feature_support(const String &p_feature) {
- return p_feature == "mobile";
-}
-
-// Initialization order between compilation units is not guaranteed,
-// so we use this as a hack to ensure certain code is called before
-// everything else, but after all units are initialized.
-typedef void (*init_callback)();
-static init_callback *ios_init_callbacks = nullptr;
-static int ios_init_callbacks_count = 0;
-static int ios_init_callbacks_capacity = 0;
-
-void add_ios_init_callback(init_callback cb) {
- if (ios_init_callbacks_count == ios_init_callbacks_capacity) {
- void *new_ptr = realloc(ios_init_callbacks, sizeof(cb) * 32);
- if (new_ptr) {
- ios_init_callbacks = (init_callback *)(new_ptr);
- ios_init_callbacks_capacity += 32;
- }
- }
- if (ios_init_callbacks_capacity > ios_init_callbacks_count) {
- ios_init_callbacks[ios_init_callbacks_count] = cb;
- ++ios_init_callbacks_count;
- }
-}
-
-OSIPhone::OSIPhone(int width, int height, String p_data_dir) {
- for (int i = 0; i < ios_init_callbacks_count; ++i) {
- ios_init_callbacks[i]();
- }
- free(ios_init_callbacks);
- ios_init_callbacks = nullptr;
- ios_init_callbacks_count = 0;
- ios_init_callbacks_capacity = 0;
-
- main_loop = nullptr;
- rendering_server = nullptr;
-
- VideoMode vm;
- vm.fullscreen = true;
- vm.width = width;
- vm.height = height;
- vm.resizable = false;
- set_video_mode(vm);
- event_count = 0;
- virtual_keyboard_height = 0;
-
- // can't call set_data_dir from here, since it requires DirAccess
- // which is initialized in initialize_core
- data_dir = p_data_dir;
-
- Vector<Logger *> loggers;
- loggers.push_back(memnew(SyslogLogger));
-#ifdef DEBUG_ENABLED
- // it seems iOS app's stdout/stderr is only obtainable if you launch it from Xcode
- loggers.push_back(memnew(StdLogger));
-#endif
- _set_logger(memnew(CompositeLogger(loggers)));
-
- AudioDriverManager::add_driver(&audio_driver);
-};
-
-OSIPhone::~OSIPhone() {
-}
-
-#endif
diff --git a/platform/iphone/os_iphone.h b/platform/iphone/os_iphone.h
index 955eb15d57..f3bde46717 100644
--- a/platform/iphone/os_iphone.h
+++ b/platform/iphone/os_iphone.h
@@ -33,16 +33,15 @@
#ifndef OS_IPHONE_H
#define OS_IPHONE_H
-#include "core/input/input.h"
#include "drivers/coreaudio/audio_driver_coreaudio.h"
#include "drivers/unix/os_unix.h"
#include "game_center.h"
#include "icloud.h"
#include "in_app_store.h"
#include "ios.h"
+#include "joypad_iphone.h"
#include "servers/audio_server.h"
#include "servers/rendering/rasterizer.h"
-#include "servers/rendering_server.h"
#if defined(VULKAN_ENABLED)
#include "drivers/vulkan/rendering_device_vulkan.h"
@@ -51,16 +50,9 @@
class OSIPhone : public OS_Unix {
private:
- enum {
- MAX_MOUSE_COUNT = 8,
- MAX_EVENTS = 64,
- };
-
static HashMap<String, void *> dynamic_symbol_lookup_table;
friend void register_dynamic_symbol(char *name, void *address);
- RenderingServer *rendering_server;
-
AudioDriverCoreAudio audio_driver;
#ifdef GAME_CENTER_ENABLED
@@ -74,139 +66,72 @@ private:
#endif
iOS *ios;
- MainLoop *main_loop;
-
-#if defined(VULKAN_ENABLED)
- VulkanContextIPhone *context_vulkan;
- RenderingDeviceVulkan *rendering_device_vulkan;
-#endif
- VideoMode video_mode;
-
- virtual int get_video_driver_count() const;
- virtual const char *get_video_driver_name(int p_driver) const;
+ JoypadIPhone *joypad_iphone;
- virtual int get_current_video_driver() const;
-
- virtual void initialize_core();
- virtual Error initialize(const VideoMode &p_desired, int p_video_driver, int p_audio_driver);
-
- virtual void set_main_loop(MainLoop *p_main_loop);
- virtual MainLoop *get_main_loop() const;
-
- virtual void delete_main_loop();
+ MainLoop *main_loop;
- virtual void finalize();
+ virtual void initialize_core() override;
+ virtual void initialize() override;
- struct MouseList {
- bool pressed[MAX_MOUSE_COUNT];
- MouseList() {
- for (int i = 0; i < MAX_MOUSE_COUNT; i++)
- pressed[i] = false;
- };
- };
+ virtual void initialize_joypads() override {
+ }
- MouseList touch_list;
+ virtual void set_main_loop(MainLoop *p_main_loop) override;
+ virtual MainLoop *get_main_loop() const override;
- Vector3 last_accel;
+ virtual void delete_main_loop() override;
- Ref<InputEvent> event_queue[MAX_EVENTS];
- int event_count;
- void queue_event(const Ref<InputEvent> &p_event);
+ virtual void finalize() override;
- String data_dir;
+ String user_data_dir;
String unique_id;
String locale_code;
- InputDefault *input;
+ bool is_focused = false;
- int virtual_keyboard_height;
-
- int video_driver_index;
+ void deinitialize_modules();
public:
- bool iterate();
-
- uint8_t get_orientations() const;
-
- void touch_press(int p_idx, int p_x, int p_y, bool p_pressed, bool p_doubleclick);
- void touch_drag(int p_idx, int p_prev_x, int p_prev_y, int p_x, int p_y);
- void touches_cancelled();
- void key(uint32_t p_key, bool p_pressed);
- void set_virtual_keyboard_height(int p_height);
-
- int set_base_framebuffer(int p_fb);
-
- void update_gravity(float p_x, float p_y, float p_z);
- void update_accelerometer(float p_x, float p_y, float p_z);
- void update_magnetometer(float p_x, float p_y, float p_z);
- void update_gyroscope(float p_x, float p_y, float p_z);
-
- int get_unused_joy_id();
- void joy_connection_changed(int p_idx, bool p_connected, String p_name);
- void joy_button(int p_device, int p_button, bool p_pressed);
- void joy_axis(int p_device, int p_axis, const InputDefault::JoyAxis &p_value);
-
static OSIPhone *get_singleton();
- virtual void set_mouse_show(bool p_show);
- virtual void set_mouse_grab(bool p_grab);
- virtual bool is_mouse_grab_enabled() const;
- virtual Point2 get_mouse_position() const;
- virtual int get_mouse_button_state() const;
- virtual void set_window_title(const String &p_title);
-
- virtual void alert(const String &p_alert, const String &p_title = "ALERT!");
+ OSIPhone(String p_data_dir);
+ ~OSIPhone();
- virtual Error open_dynamic_library(const String p_path, void *&p_library_handle, bool p_also_set_library_path = false);
- virtual Error close_dynamic_library(void *p_library_handle);
- virtual Error get_dynamic_library_symbol_handle(void *p_library_handle, const String p_name, void *&p_symbol_handle, bool p_optional = false);
+ void initialize_modules();
- virtual void set_video_mode(const VideoMode &p_video_mode, int p_screen = 0);
- virtual VideoMode get_video_mode(int p_screen = 0) const;
- virtual void get_fullscreen_mode_list(List<VideoMode> *p_list, int p_screen = 0) const;
+ bool iterate();
- virtual void set_keep_screen_on(bool p_enabled);
+ void start();
- virtual bool can_draw() const;
+ virtual Error open_dynamic_library(const String p_path, void *&p_library_handle, bool p_also_set_library_path = false) override;
+ virtual Error close_dynamic_library(void *p_library_handle) override;
+ virtual Error get_dynamic_library_symbol_handle(void *p_library_handle, const String p_name, void *&p_symbol_handle, bool p_optional = false) override;
- virtual bool has_virtual_keyboard() const;
- virtual void show_virtual_keyboard(const String &p_existing_text, const Rect2 &p_screen_rect = Rect2(), int p_max_input_length = -1, int p_cursor_start = -1, int p_cursor_end = -1);
- virtual void hide_virtual_keyboard();
- virtual int get_virtual_keyboard_height() const;
+ virtual void alert(const String &p_alert,
+ const String &p_title = "ALERT!") override;
- virtual Size2 get_window_size() const;
- virtual Rect2 get_window_safe_area() const;
+ virtual String get_name() const override;
+ virtual String get_model_name() const override;
- virtual bool has_touchscreen_ui_hint() const;
+ virtual Error shell_open(String p_uri) override;
- void set_data_dir(String p_dir);
+ void set_user_data_dir(String p_dir);
+ virtual String get_user_data_dir() const override;
- virtual String get_name() const;
- virtual String get_model_name() const;
+ void set_locale(String p_locale);
+ virtual String get_locale() const override;
- Error shell_open(String p_uri);
+ void set_unique_id(String p_id);
+ virtual String get_unique_id() const override;
- String get_user_data_dir() const;
+ virtual void vibrate_handheld(int p_duration_ms = 500) override;
- void set_locale(String p_locale);
- String get_locale() const;
+ virtual bool _check_internal_feature_support(const String &p_feature) override;
- void set_unique_id(String p_id);
- String get_unique_id() const;
-
- virtual Error native_video_play(String p_path, float p_volume, String p_audio_track, String p_subtitle_track);
- virtual bool native_video_is_playing() const;
- virtual void native_video_pause();
- virtual void native_video_unpause();
- virtual void native_video_focus_out();
- virtual void native_video_stop();
- virtual void vibrate_handheld(int p_duration_ms = 500);
-
- virtual bool _check_internal_feature_support(const String &p_feature);
- OSIPhone(int width, int height, String p_data_dir);
- ~OSIPhone();
+ void on_focus_out();
+ void on_focus_in();
};
#endif // OS_IPHONE_H
-#endif
+#endif // IPHONE_ENABLED
diff --git a/platform/iphone/os_iphone.mm b/platform/iphone/os_iphone.mm
new file mode 100644
index 0000000000..f0bbbd39ca
--- /dev/null
+++ b/platform/iphone/os_iphone.mm
@@ -0,0 +1,369 @@
+/*************************************************************************/
+/* os_iphone.mm */
+/*************************************************************************/
+/* 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. */
+/*************************************************************************/
+
+#ifdef IPHONE_ENABLED
+
+#include "os_iphone.h"
+#import "app_delegate.h"
+#include "core/io/file_access_pack.h"
+#include "core/os/dir_access.h"
+#include "core/os/file_access.h"
+#include "core/project_settings.h"
+#include "display_server_iphone.h"
+#include "drivers/unix/syslog_logger.h"
+#import "godot_view.h"
+#include "main/main.h"
+#import "view_controller.h"
+
+#import <AudioToolbox/AudioServices.h>
+#import <UIKit/UIKit.h>
+#import <dlfcn.h>
+
+#if defined(OPENGL_ENABLED)
+#include "drivers/gles2/rasterizer_gles2.h"
+#endif
+
+#if defined(VULKAN_ENABLED)
+#include "servers/rendering/rasterizer_rd/rasterizer_rd.h"
+#import <QuartzCore/CAMetalLayer.h>
+#include <vulkan/vulkan_metal.h>
+#endif
+
+// Initialization order between compilation units is not guaranteed,
+// so we use this as a hack to ensure certain code is called before
+// everything else, but after all units are initialized.
+typedef void (*init_callback)();
+static init_callback *ios_init_callbacks = nullptr;
+static int ios_init_callbacks_count = 0;
+static int ios_init_callbacks_capacity = 0;
+HashMap<String, void *> OSIPhone::dynamic_symbol_lookup_table;
+
+void add_ios_init_callback(init_callback cb) {
+ if (ios_init_callbacks_count == ios_init_callbacks_capacity) {
+ void *new_ptr = realloc(ios_init_callbacks, sizeof(cb) * 32);
+ if (new_ptr) {
+ ios_init_callbacks = (init_callback *)(new_ptr);
+ ios_init_callbacks_capacity += 32;
+ }
+ }
+ if (ios_init_callbacks_capacity > ios_init_callbacks_count) {
+ ios_init_callbacks[ios_init_callbacks_count] = cb;
+ ++ios_init_callbacks_count;
+ }
+}
+
+void register_dynamic_symbol(char *name, void *address) {
+ OSIPhone::dynamic_symbol_lookup_table[String(name)] = address;
+}
+
+OSIPhone *OSIPhone::get_singleton() {
+ return (OSIPhone *)OS::get_singleton();
+}
+
+OSIPhone::OSIPhone(String p_data_dir) {
+ for (int i = 0; i < ios_init_callbacks_count; ++i) {
+ ios_init_callbacks[i]();
+ }
+ free(ios_init_callbacks);
+ ios_init_callbacks = nullptr;
+ ios_init_callbacks_count = 0;
+ ios_init_callbacks_capacity = 0;
+
+ main_loop = nullptr;
+
+ // can't call set_data_dir from here, since it requires DirAccess
+ // which is initialized in initialize_core
+ user_data_dir = p_data_dir;
+
+ Vector<Logger *> loggers;
+ loggers.push_back(memnew(SyslogLogger));
+#ifdef DEBUG_ENABLED
+ // it seems iOS app's stdout/stderr is only obtainable if you launch it from
+ // Xcode
+ loggers.push_back(memnew(StdLogger));
+#endif
+ _set_logger(memnew(CompositeLogger(loggers)));
+
+ AudioDriverManager::add_driver(&audio_driver);
+
+ DisplayServerIPhone::register_iphone_driver();
+}
+
+OSIPhone::~OSIPhone() {}
+
+void OSIPhone::initialize_core() {
+ OS_Unix::initialize_core();
+
+ set_user_data_dir(user_data_dir);
+}
+
+void OSIPhone::initialize() {
+ initialize_core();
+}
+
+void OSIPhone::initialize_modules() {
+#ifdef GAME_CENTER_ENABLED
+ game_center = memnew(GameCenter);
+ Engine::get_singleton()->add_singleton(Engine::Singleton("GameCenter", game_center));
+ game_center->connect();
+#endif
+
+#ifdef STOREKIT_ENABLED
+ store_kit = memnew(InAppStore);
+ Engine::get_singleton()->add_singleton(Engine::Singleton("InAppStore", store_kit));
+#endif
+
+#ifdef ICLOUD_ENABLED
+ icloud = memnew(ICloud);
+ Engine::get_singleton()->add_singleton(Engine::Singleton("ICloud", icloud));
+#endif
+
+ ios = memnew(iOS);
+ Engine::get_singleton()->add_singleton(Engine::Singleton("iOS", ios));
+
+ joypad_iphone = memnew(JoypadIPhone);
+}
+
+void OSIPhone::deinitialize_modules() {
+ if (joypad_iphone) {
+ memdelete(joypad_iphone);
+ }
+
+ if (ios) {
+ memdelete(ios);
+ }
+
+#ifdef GAME_CENTER_ENABLED
+ if (game_center) {
+ memdelete(game_center);
+ }
+#endif
+
+#ifdef STOREKIT_ENABLED
+ if (store_kit) {
+ memdelete(store_kit);
+ }
+#endif
+
+#ifdef ICLOUD_ENABLED
+ if (icloud) {
+ memdelete(icloud);
+ }
+#endif
+}
+
+void OSIPhone::set_main_loop(MainLoop *p_main_loop) {
+ main_loop = p_main_loop;
+
+ if (main_loop) {
+ main_loop->init();
+ }
+}
+
+MainLoop *OSIPhone::get_main_loop() const {
+ return main_loop;
+}
+
+void OSIPhone::delete_main_loop() {
+ if (main_loop) {
+ main_loop->finish();
+ memdelete(main_loop);
+ };
+
+ main_loop = nullptr;
+}
+
+bool OSIPhone::iterate() {
+ if (!main_loop) {
+ return true;
+ }
+
+ if (DisplayServer::get_singleton()) {
+ DisplayServer::get_singleton()->process_events();
+ }
+
+ return Main::iteration();
+}
+
+void OSIPhone::start() {
+ Main::start();
+
+ if (joypad_iphone) {
+ joypad_iphone->start_processing();
+ }
+}
+
+void OSIPhone::finalize() {
+ deinitialize_modules();
+
+ // Already gets called
+ // delete_main_loop();
+}
+
+// MARK: Dynamic Libraries
+
+Error OSIPhone::open_dynamic_library(const String p_path, void *&p_library_handle, bool p_also_set_library_path) {
+ if (p_path.length() == 0) {
+ p_library_handle = RTLD_SELF;
+ return OK;
+ }
+ return OS_Unix::open_dynamic_library(p_path, p_library_handle, p_also_set_library_path);
+}
+
+Error OSIPhone::close_dynamic_library(void *p_library_handle) {
+ if (p_library_handle == RTLD_SELF) {
+ return OK;
+ }
+ return OS_Unix::close_dynamic_library(p_library_handle);
+}
+
+Error OSIPhone::get_dynamic_library_symbol_handle(void *p_library_handle, const String p_name, void *&p_symbol_handle, bool p_optional) {
+ if (p_library_handle == RTLD_SELF) {
+ void **ptr = OSIPhone::dynamic_symbol_lookup_table.getptr(p_name);
+ if (ptr) {
+ p_symbol_handle = *ptr;
+ return OK;
+ }
+ }
+ return OS_Unix::get_dynamic_library_symbol_handle(p_library_handle, p_name, p_symbol_handle, p_optional);
+}
+
+void OSIPhone::alert(const String &p_alert, const String &p_title) {
+ const CharString utf8_alert = p_alert.utf8();
+ const CharString utf8_title = p_title.utf8();
+ iOS::alert(utf8_alert.get_data(), utf8_title.get_data());
+}
+
+String OSIPhone::get_name() const {
+ return "iOS";
+};
+
+String OSIPhone::get_model_name() const {
+ String model = ios->get_model();
+ if (model != "")
+ return model;
+
+ return OS_Unix::get_model_name();
+}
+
+Error OSIPhone::shell_open(String p_uri) {
+ NSString *urlPath = [[NSString alloc] initWithUTF8String:p_uri.utf8().get_data()];
+ NSURL *url = [NSURL URLWithString:urlPath];
+ [urlPath release];
+
+ if (![[UIApplication sharedApplication] canOpenURL:url]) {
+ return ERR_CANT_OPEN;
+ }
+
+ printf("opening url %ls\n", p_uri.c_str());
+
+ // if (@available(iOS 10, *)) {
+ [[UIApplication sharedApplication] openURL:url options:@{} completionHandler:nil];
+ // } else {
+ // [[UIApplication sharedApplication] openURL:url];
+ // }
+
+ return OK;
+};
+
+void OSIPhone::set_user_data_dir(String p_dir) {
+ DirAccess *da = DirAccess::open(p_dir);
+
+ user_data_dir = da->get_current_dir();
+ printf("setting data dir to %ls from %ls\n", user_data_dir.c_str(), p_dir.c_str());
+ memdelete(da);
+}
+
+String OSIPhone::get_user_data_dir() const {
+ return user_data_dir;
+}
+
+void OSIPhone::set_locale(String p_locale) {
+ locale_code = p_locale;
+}
+
+String OSIPhone::get_locale() const {
+ return locale_code;
+}
+
+void OSIPhone::set_unique_id(String p_id) {
+ unique_id = p_id;
+}
+
+String OSIPhone::get_unique_id() const {
+ return unique_id;
+}
+
+void OSIPhone::vibrate_handheld(int p_duration_ms) {
+ // iOS does not support duration for vibration
+ AudioServicesPlaySystemSound(kSystemSoundID_Vibrate);
+}
+
+bool OSIPhone::_check_internal_feature_support(const String &p_feature) {
+ return p_feature == "mobile";
+}
+
+void OSIPhone::on_focus_out() {
+ if (is_focused) {
+ is_focused = false;
+
+ if (DisplayServerIPhone::get_singleton()) {
+ DisplayServerIPhone::get_singleton()->send_window_event(DisplayServer::WINDOW_EVENT_FOCUS_OUT);
+ }
+
+ [AppDelegate.viewController.godotView stopRendering];
+
+ if (DisplayServerIPhone::get_singleton() && DisplayServerIPhone::get_singleton()->native_video_is_playing()) {
+ DisplayServerIPhone::get_singleton()->native_video_pause();
+ }
+
+ audio_driver.stop();
+ }
+}
+
+void OSIPhone::on_focus_in() {
+ if (!is_focused) {
+ is_focused = true;
+
+ if (DisplayServerIPhone::get_singleton()) {
+ DisplayServerIPhone::get_singleton()->send_window_event(DisplayServer::WINDOW_EVENT_FOCUS_IN);
+ }
+
+ [AppDelegate.viewController.godotView startRendering];
+
+ if (DisplayServerIPhone::get_singleton() && DisplayServerIPhone::get_singleton()->native_video_is_playing()) {
+ DisplayServerIPhone::get_singleton()->native_video_unpause();
+ }
+
+ audio_driver.start();
+ }
+}
+
+#endif // IPHONE_ENABLED
diff --git a/platform/iphone/view_controller.h b/platform/iphone/view_controller.h
index f6bbe11d97..dffdc01d4a 100644
--- a/platform/iphone/view_controller.h
+++ b/platform/iphone/view_controller.h
@@ -31,20 +31,18 @@
#import <GameKit/GameKit.h>
#import <UIKit/UIKit.h>
-@interface ViewController : UIViewController <GKGameCenterControllerDelegate> {
-};
+@class GodotView;
-- (BOOL)shouldAutorotateToInterfaceOrientation:
- (UIInterfaceOrientation)p_orientation;
+@interface ViewController : UIViewController <GKGameCenterControllerDelegate>
-- (void)didReceiveMemoryWarning;
+- (GodotView *)godotView;
-- (void)viewDidLoad;
+// MARK: Native Video Player
-- (UIRectEdge)preferredScreenEdgesDeferringSystemGestures;
-
-- (BOOL)prefersStatusBarHidden;
-
-- (BOOL)prefersHomeIndicatorAutoHidden;
+- (BOOL)playVideoAtPath:(NSString *)filePath volume:(float)videoVolume audio:(NSString *)audioTrack subtitle:(NSString *)subtitleTrack;
+- (BOOL)isVideoPlaying;
+- (void)pauseVideo;
+- (void)unpauseVideo;
+- (void)stopVideo;
@end
diff --git a/platform/iphone/view_controller.mm b/platform/iphone/view_controller.mm
index 279bcc1226..31597f7856 100644
--- a/platform/iphone/view_controller.mm
+++ b/platform/iphone/view_controller.mm
@@ -29,96 +29,174 @@
/*************************************************************************/
#import "view_controller.h"
-
+#include "core/project_settings.h"
+#include "display_server_iphone.h"
+#import "godot_view.h"
+#import "godot_view_renderer.h"
#include "os_iphone.h"
-#include "core/project_settings.h"
+#import <GameController/GameController.h>
-extern "C" {
+@interface ViewController ()
-int add_path(int, char **);
-int add_cmdline(int, char **);
+@property(strong, nonatomic) GodotViewRenderer *renderer;
-int add_path(int p_argc, char **p_args) {
- NSString *str = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"godot_path"];
- if (!str)
- return p_argc;
+// TODO: separate view to handle video
+// AVPlayer-related properties
+@property(strong, nonatomic) AVAsset *avAsset;
+@property(strong, nonatomic) AVPlayerItem *avPlayerItem;
+@property(strong, nonatomic) AVPlayer *avPlayer;
+@property(strong, nonatomic) AVPlayerLayer *avPlayerLayer;
+@property(assign, nonatomic) CMTime videoCurrentTime;
+@property(assign, nonatomic) BOOL isVideoCurrentlyPlaying;
+@property(assign, nonatomic) BOOL videoHasFoundError;
- p_args[p_argc++] = "--path";
- [str retain]; // memory leak lol (maybe make it static here and delete it in ViewController destructor? @todo
- p_args[p_argc++] = (char *)[str cString];
- p_args[p_argc] = NULL;
+@end
- return p_argc;
-};
+@implementation ViewController
-int add_cmdline(int p_argc, char **p_args) {
- NSArray *arr = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"godot_cmdline"];
- if (!arr)
- return p_argc;
+- (GodotView *)godotView {
+ return (GodotView *)self.view;
+}
- for (int i = 0; i < [arr count]; i++) {
- NSString *str = [arr objectAtIndex:i];
- if (!str)
- continue;
- [str retain]; // @todo delete these at some point
- p_args[p_argc++] = (char *)[str cString];
- };
+- (void)loadView {
+ GodotView *view = [[GodotView alloc] init];
+ GodotViewRenderer *renderer = [[GodotViewRenderer alloc] init];
- p_args[p_argc] = NULL;
+ self.renderer = renderer;
+ self.view = view;
- return p_argc;
-};
-}; // extern "C"
+ view.renderer = self.renderer;
-@interface ViewController ()
+ [renderer release];
+ [view release];
+}
-@end
+- (instancetype)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
+ self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
-@implementation ViewController
+ if (self) {
+ [self godot_commonInit];
+ }
+
+ return self;
+}
+
+- (instancetype)initWithCoder:(NSCoder *)coder {
+ self = [super initWithCoder:coder];
+
+ if (self) {
+ [self godot_commonInit];
+ }
+
+ return self;
+}
+
+- (void)godot_commonInit {
+ self.isVideoCurrentlyPlaying = NO;
+ self.videoCurrentTime = kCMTimeZero;
+ self.videoHasFoundError = false;
+}
- (void)didReceiveMemoryWarning {
+ [super didReceiveMemoryWarning];
printf("*********** did receive memory warning!\n");
-};
+}
- (void)viewDidLoad {
[super viewDidLoad];
+ [self observeKeyboard];
+ [self observeAudio];
+
if (@available(iOS 11.0, *)) {
[self setNeedsUpdateOfScreenEdgesDeferringSystemGestures];
}
}
+- (void)observeKeyboard {
+ printf("******** adding observer for keyboard show/hide\n");
+ [[NSNotificationCenter defaultCenter]
+ addObserver:self
+ selector:@selector(keyboardOnScreen:)
+ name:UIKeyboardDidShowNotification
+ object:nil];
+ [[NSNotificationCenter defaultCenter]
+ addObserver:self
+ selector:@selector(keyboardHidden:)
+ name:UIKeyboardDidHideNotification
+ object:nil];
+}
+
+- (void)observeAudio {
+ printf("******** adding observer for sound routing changes\n");
+ [[NSNotificationCenter defaultCenter]
+ addObserver:self
+ selector:@selector(audioRouteChangeListenerCallback:)
+ name:AVAudioSessionRouteChangeNotification
+ object:nil];
+}
+
+- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
+ if (object == self.avPlayerItem && [keyPath isEqualToString:@"status"]) {
+ [self handleVideoOrPlayerStatus];
+ }
+
+ if (object == self.avPlayer && [keyPath isEqualToString:@"rate"]) {
+ [self handleVideoPlayRate];
+ }
+}
+
+- (void)dealloc {
+ [self stopVideo];
+
+ self.renderer = nil;
+
+ [[NSNotificationCenter defaultCenter] removeObserver:self];
+
+ [super dealloc];
+}
+
+// MARK: Orientation
+
- (UIRectEdge)preferredScreenEdgesDeferringSystemGestures {
return UIRectEdgeAll;
}
- (BOOL)shouldAutorotate {
- switch (OS::get_singleton()->get_screen_orientation()) {
- case OS::SCREEN_SENSOR:
- case OS::SCREEN_SENSOR_LANDSCAPE:
- case OS::SCREEN_SENSOR_PORTRAIT:
+ if (!DisplayServerIPhone::get_singleton()) {
+ return NO;
+ }
+
+ switch (DisplayServerIPhone::get_singleton()->screen_get_orientation(DisplayServer::SCREEN_OF_MAIN_WINDOW)) {
+ case DisplayServer::SCREEN_SENSOR:
+ case DisplayServer::SCREEN_SENSOR_LANDSCAPE:
+ case DisplayServer::SCREEN_SENSOR_PORTRAIT:
return YES;
default:
return NO;
}
-};
+}
- (UIInterfaceOrientationMask)supportedInterfaceOrientations {
- switch (OS::get_singleton()->get_screen_orientation()) {
- case OS::SCREEN_PORTRAIT:
+ if (!DisplayServerIPhone::get_singleton()) {
+ return UIInterfaceOrientationMaskAll;
+ }
+
+ switch (DisplayServerIPhone::get_singleton()->screen_get_orientation(DisplayServer::SCREEN_OF_MAIN_WINDOW)) {
+ case DisplayServer::SCREEN_PORTRAIT:
return UIInterfaceOrientationMaskPortrait;
- case OS::SCREEN_REVERSE_LANDSCAPE:
+ case DisplayServer::SCREEN_REVERSE_LANDSCAPE:
return UIInterfaceOrientationMaskLandscapeRight;
- case OS::SCREEN_REVERSE_PORTRAIT:
+ case DisplayServer::SCREEN_REVERSE_PORTRAIT:
return UIInterfaceOrientationMaskPortraitUpsideDown;
- case OS::SCREEN_SENSOR_LANDSCAPE:
+ case DisplayServer::SCREEN_SENSOR_LANDSCAPE:
return UIInterfaceOrientationMaskLandscape;
- case OS::SCREEN_SENSOR_PORTRAIT:
+ case DisplayServer::SCREEN_SENSOR_PORTRAIT:
return UIInterfaceOrientationMaskPortrait | UIInterfaceOrientationMaskPortraitUpsideDown;
- case OS::SCREEN_SENSOR:
+ case DisplayServer::SCREEN_SENSOR:
return UIInterfaceOrientationMaskAll;
- case OS::SCREEN_LANDSCAPE:
+ case DisplayServer::SCREEN_LANDSCAPE:
return UIInterfaceOrientationMaskLandscapeLeft;
}
};
@@ -135,6 +213,190 @@ int add_cmdline(int p_argc, char **p_args) {
}
}
+// MARK: Keyboard
+
+- (void)keyboardOnScreen:(NSNotification *)notification {
+ NSDictionary *info = notification.userInfo;
+ NSValue *value = info[UIKeyboardFrameEndUserInfoKey];
+
+ CGRect rawFrame = [value CGRectValue];
+ CGRect keyboardFrame = [self.view convertRect:rawFrame fromView:nil];
+
+ if (DisplayServerIPhone::get_singleton()) {
+ DisplayServerIPhone::get_singleton()->virtual_keyboard_set_height(keyboardFrame.size.height);
+ }
+}
+
+- (void)keyboardHidden:(NSNotification *)notification {
+ if (DisplayServerIPhone::get_singleton()) {
+ DisplayServerIPhone::get_singleton()->virtual_keyboard_set_height(0);
+ }
+}
+
+// MARK: Audio
+
+- (void)audioRouteChangeListenerCallback:(NSNotification *)notification {
+ printf("*********** route changed!\n");
+ NSDictionary *interuptionDict = notification.userInfo;
+
+ NSInteger routeChangeReason = [[interuptionDict valueForKey:AVAudioSessionRouteChangeReasonKey] integerValue];
+
+ switch (routeChangeReason) {
+ case AVAudioSessionRouteChangeReasonNewDeviceAvailable: {
+ NSLog(@"AVAudioSessionRouteChangeReasonNewDeviceAvailable");
+ NSLog(@"Headphone/Line plugged in");
+ } break;
+ case AVAudioSessionRouteChangeReasonOldDeviceUnavailable: {
+ NSLog(@"AVAudioSessionRouteChangeReasonOldDeviceUnavailable");
+ NSLog(@"Headphone/Line was pulled. Resuming video play....");
+ if ([self isVideoPlaying]) {
+ dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 0.5f * NSEC_PER_SEC), dispatch_get_main_queue(), ^{
+ [self.avPlayer play]; // NOTE: change this line according your current player implementation
+ NSLog(@"resumed play");
+ });
+ }
+ } break;
+ case AVAudioSessionRouteChangeReasonCategoryChange: {
+ // called at start - also when other audio wants to play
+ NSLog(@"AVAudioSessionRouteChangeReasonCategoryChange");
+ } break;
+ }
+}
+
+// MARK: Native Video Player
+
+- (void)handleVideoOrPlayerStatus {
+ if (self.avPlayerItem.status == AVPlayerItemStatusFailed || self.avPlayer.status == AVPlayerStatusFailed) {
+ [self stopVideo];
+ self.videoHasFoundError = true;
+ }
+
+ if (self.avPlayer.status == AVPlayerStatusReadyToPlay && self.avPlayerItem.status == AVPlayerItemStatusReadyToPlay && CMTimeCompare(self.videoCurrentTime, kCMTimeZero) == 0) {
+ // NSLog(@"time: %@", self.video_current_time);
+ [self.avPlayer seekToTime:self.videoCurrentTime];
+ self.videoCurrentTime = kCMTimeZero;
+ }
+}
+
+- (void)handleVideoPlayRate {
+ NSLog(@"Player playback rate changed: %.5f", self.avPlayer.rate);
+ if ([self isVideoPlaying] && self.avPlayer.rate == 0.0 && !self.avPlayer.error) {
+ dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 0.5f * NSEC_PER_SEC), dispatch_get_main_queue(), ^{
+ [self.avPlayer play]; // NOTE: change this line according your current player implementation
+ NSLog(@"resumed play");
+ });
+
+ NSLog(@" . . . PAUSED (or just started)");
+ }
+}
+
+- (BOOL)playVideoAtPath:(NSString *)filePath volume:(float)videoVolume audio:(NSString *)audioTrack subtitle:(NSString *)subtitleTrack {
+ self.avAsset = [AVAsset assetWithURL:[NSURL fileURLWithPath:filePath]];
+
+ self.avPlayerItem = [AVPlayerItem playerItemWithAsset:self.avAsset];
+ [self.avPlayerItem addObserver:self forKeyPath:@"status" options:0 context:nil];
+
+ self.avPlayer = [AVPlayer playerWithPlayerItem:self.avPlayerItem];
+ self.avPlayerLayer = [AVPlayerLayer playerLayerWithPlayer:self.avPlayer];
+
+ [self.avPlayer addObserver:self forKeyPath:@"status" options:0 context:nil];
+ [[NSNotificationCenter defaultCenter]
+ addObserver:self
+ selector:@selector(playerItemDidReachEnd:)
+ name:AVPlayerItemDidPlayToEndTimeNotification
+ object:[self.avPlayer currentItem]];
+
+ [self.avPlayer addObserver:self forKeyPath:@"rate" options:NSKeyValueObservingOptionNew context:0];
+
+ [self.avPlayerLayer setFrame:self.view.bounds];
+ [self.view.layer addSublayer:self.avPlayerLayer];
+ [self.avPlayer play];
+
+ AVMediaSelectionGroup *audioGroup = [self.avAsset mediaSelectionGroupForMediaCharacteristic:AVMediaCharacteristicAudible];
+
+ NSMutableArray *allAudioParams = [NSMutableArray array];
+ for (id track in audioGroup.options) {
+ NSString *language = [[track locale] localeIdentifier];
+ NSLog(@"subtitle lang: %@", language);
+
+ if ([language isEqualToString:audioTrack]) {
+ AVMutableAudioMixInputParameters *audioInputParams = [AVMutableAudioMixInputParameters audioMixInputParameters];
+ [audioInputParams setVolume:videoVolume atTime:kCMTimeZero];
+ [audioInputParams setTrackID:[track trackID]];
+ [allAudioParams addObject:audioInputParams];
+
+ AVMutableAudioMix *audioMix = [AVMutableAudioMix audioMix];
+ [audioMix setInputParameters:allAudioParams];
+
+ [self.avPlayer.currentItem selectMediaOption:track inMediaSelectionGroup:audioGroup];
+ [self.avPlayer.currentItem setAudioMix:audioMix];
+
+ break;
+ }
+ }
+
+ AVMediaSelectionGroup *subtitlesGroup = [self.avAsset mediaSelectionGroupForMediaCharacteristic:AVMediaCharacteristicLegible];
+ NSArray *useableTracks = [AVMediaSelectionGroup mediaSelectionOptionsFromArray:subtitlesGroup.options withoutMediaCharacteristics:[NSArray arrayWithObject:AVMediaCharacteristicContainsOnlyForcedSubtitles]];
+
+ for (id track in useableTracks) {
+ NSString *language = [[track locale] localeIdentifier];
+ NSLog(@"subtitle lang: %@", language);
+
+ if ([language isEqualToString:subtitleTrack]) {
+ [self.avPlayer.currentItem selectMediaOption:track inMediaSelectionGroup:subtitlesGroup];
+ break;
+ }
+ }
+
+ self.isVideoCurrentlyPlaying = YES;
+
+ return true;
+}
+
+- (BOOL)isVideoPlaying {
+ if (self.avPlayer.error) {
+ printf("Error during playback\n");
+ }
+ return (self.avPlayer.rate > 0 && !self.avPlayer.error);
+}
+
+- (void)pauseVideo {
+ self.videoCurrentTime = self.avPlayer.currentTime;
+ [self.avPlayer pause];
+ self.isVideoCurrentlyPlaying = NO;
+}
+
+- (void)unpauseVideo {
+ [self.avPlayer play];
+ self.isVideoCurrentlyPlaying = YES;
+}
+
+- (void)playerItemDidReachEnd:(NSNotification *)notification {
+ [self stopVideo];
+}
+
+- (void)stopVideo {
+ [self.avPlayer pause];
+ [self.avPlayerLayer removeFromSuperlayer];
+ self.avPlayerLayer = nil;
+
+ if (self.avPlayerItem) {
+ [self.avPlayerItem removeObserver:self forKeyPath:@"status"];
+ self.avPlayerItem = nil;
+ }
+
+ if (self.avPlayer) {
+ [self.avPlayer removeObserver:self forKeyPath:@"status"];
+ self.avPlayer = nil;
+ }
+
+ self.avAsset = nil;
+
+ self.isVideoCurrentlyPlaying = NO;
+}
+
+// MARK: Delegates
+
#ifdef GAME_CENTER_ENABLED
- (void)gameCenterViewControllerDidFinish:(GKGameCenterViewController *)gameCenterViewController {
//[gameCenterViewController dismissViewControllerAnimated:YES completion:^{GameCenter::get_singleton()->game_center_closed();}];//version for signaling when overlay is completely gone
diff --git a/platform/iphone/vulkan_context_iphone.h b/platform/iphone/vulkan_context_iphone.h
index cadd701636..5c3d5fe33e 100644
--- a/platform/iphone/vulkan_context_iphone.h
+++ b/platform/iphone/vulkan_context_iphone.h
@@ -32,13 +32,14 @@
#define VULKAN_CONTEXT_IPHONE_H
#include "drivers/vulkan/vulkan_context.h"
-// #import <UIKit/UIKit.h>
+
+#import <UIKit/UIKit.h>
class VulkanContextIPhone : public VulkanContext {
virtual const char *_get_platform_surface_extension() const;
public:
- int window_create(void *p_window, int p_width, int p_height);
+ Error window_create(DisplayServer::WindowID p_window_id, CALayer *p_metal_layer, int p_width, int p_height);
VulkanContextIPhone();
~VulkanContextIPhone();
diff --git a/platform/iphone/vulkan_context_iphone.mm b/platform/iphone/vulkan_context_iphone.mm
index 44c940dc3a..cb4dbe7f85 100644
--- a/platform/iphone/vulkan_context_iphone.mm
+++ b/platform/iphone/vulkan_context_iphone.mm
@@ -35,21 +35,23 @@ const char *VulkanContextIPhone::_get_platform_surface_extension() const {
return VK_MVK_IOS_SURFACE_EXTENSION_NAME;
}
-int VulkanContextIPhone::window_create(void *p_window, int p_width, int p_height) {
+Error VulkanContextIPhone::window_create(DisplayServer::WindowID p_window_id,
+ CALayer *p_metal_layer, int p_width,
+ int p_height) {
VkIOSSurfaceCreateInfoMVK createInfo;
- createInfo.sType = VK_STRUCTURE_TYPE_MACOS_SURFACE_CREATE_INFO_MVK;
+ createInfo.sType = VK_STRUCTURE_TYPE_IOS_SURFACE_CREATE_INFO_MVK;
createInfo.pNext = NULL;
createInfo.flags = 0;
- createInfo.pView = p_window;
+ createInfo.pView = p_metal_layer;
VkSurfaceKHR surface;
- VkResult err = vkCreateIOSSurfaceMVK(_get_instance(), &createInfo, NULL, &surface);
- ERR_FAIL_COND_V(err, -1);
- return _window_create(surface, p_width, p_height);
-}
+ VkResult err =
+ vkCreateIOSSurfaceMVK(_get_instance(), &createInfo, NULL, &surface);
+ ERR_FAIL_COND_V(err, ERR_CANT_CREATE);
-VulkanContextIPhone::VulkanContextIPhone() {
+ return _window_create(p_window_id, surface, p_width, p_height);
}
-VulkanContextIPhone::~VulkanContextIPhone() {
-}
+VulkanContextIPhone::VulkanContextIPhone() {}
+
+VulkanContextIPhone::~VulkanContextIPhone() {}
diff --git a/platform/osx/display_server_osx.mm b/platform/osx/display_server_osx.mm
index 5e209a6e6b..ffe60ad582 100644
--- a/platform/osx/display_server_osx.mm
+++ b/platform/osx/display_server_osx.mm
@@ -2793,7 +2793,9 @@ void DisplayServerOSX::window_set_flag(WindowFlags p_flag, bool p_enabled, Windo
} break;
case WINDOW_FLAG_BORDERLESS: {
// OrderOut prevents a lose focus bug with the window
- [wd.window_object orderOut:nil];
+ if ([wd.window_object isVisible]) {
+ [wd.window_object orderOut:nil];
+ }
wd.borderless = p_enabled;
if (p_enabled) {
[wd.window_object setStyleMask:NSWindowStyleMaskBorderless];
@@ -2807,7 +2809,13 @@ void DisplayServerOSX::window_set_flag(WindowFlags p_flag, bool p_enabled, Windo
[wd.window_object setFrame:frameRect display:NO];
}
_update_window(wd);
- [wd.window_object makeKeyAndOrderFront:nil];
+ if ([wd.window_object isVisible]) {
+ if (wd.no_focus) {
+ [wd.window_object orderFront:nil];
+ } else {
+ [wd.window_object makeKeyAndOrderFront:nil];
+ }
+ }
} break;
case WINDOW_FLAG_ALWAYS_ON_TOP: {
wd.on_top = p_enabled;
@@ -2875,7 +2883,11 @@ void DisplayServerOSX::window_move_to_foreground(WindowID p_window) {
const WindowData &wd = windows[p_window];
[[NSApplication sharedApplication] activateIgnoringOtherApps:YES];
- [wd.window_object makeKeyAndOrderFront:nil];
+ if (wd.no_focus) {
+ [wd.window_object orderFront:nil];
+ } else {
+ [wd.window_object makeKeyAndOrderFront:nil];
+ }
}
bool DisplayServerOSX::window_can_draw(WindowID p_window) const {
diff --git a/scene/3d/node_3d.cpp b/scene/3d/node_3d.cpp
index 73f17060df..bd9e4f5bde 100644
--- a/scene/3d/node_3d.cpp
+++ b/scene/3d/node_3d.cpp
@@ -174,7 +174,7 @@ void Node3D::_notification(int p_what) {
ERR_FAIL_COND(!data.viewport);
if (get_script_instance()) {
- get_script_instance()->call_multilevel(SceneStringNames::get_singleton()->_enter_world, nullptr, 0);
+ get_script_instance()->call(SceneStringNames::get_singleton()->_enter_world);
}
#ifdef TOOLS_ENABLED
if (Engine::get_singleton()->is_editor_hint() && get_tree()->is_node_being_edited(this)) {
@@ -202,7 +202,7 @@ void Node3D::_notification(int p_what) {
#endif
if (get_script_instance()) {
- get_script_instance()->call_multilevel(SceneStringNames::get_singleton()->_exit_world, nullptr, 0);
+ get_script_instance()->call(SceneStringNames::get_singleton()->_exit_world);
}
data.viewport = nullptr;
diff --git a/scene/gui/scroll_bar.cpp b/scene/gui/scroll_bar.cpp
index 4db6ca2949..0e9ef71892 100644
--- a/scene/gui/scroll_bar.cpp
+++ b/scene/gui/scroll_bar.cpp
@@ -522,7 +522,7 @@ void ScrollBar::_drag_node_input(const Ref<InputEvent> &p_input) {
drag_node_accum = Vector2();
last_drag_node_accum = Vector2();
drag_node_from = Vector2(orientation == HORIZONTAL ? get_value() : 0, orientation == VERTICAL ? get_value() : 0);
- drag_node_touching = !DisplayServer::get_singleton()->screen_is_touchscreen(DisplayServer::get_singleton()->window_get_current_screen(get_viewport()->get_window_id()));
+ drag_node_touching = DisplayServer::get_singleton()->screen_is_touchscreen(DisplayServer::get_singleton()->window_get_current_screen(get_viewport()->get_window_id()));
drag_node_touching_deaccel = false;
time_since_motion = 0;
diff --git a/scene/main/canvas_item.cpp b/scene/main/canvas_item.cpp
index d1bf038b8d..d6d1134cc9 100644
--- a/scene/main/canvas_item.cpp
+++ b/scene/main/canvas_item.cpp
@@ -434,7 +434,7 @@ void CanvasItem::_update_callback() {
notification(NOTIFICATION_DRAW);
emit_signal(SceneStringNames::get_singleton()->draw);
if (get_script_instance()) {
- get_script_instance()->call_multilevel_reversed(SceneStringNames::get_singleton()->_draw, nullptr, 0);
+ get_script_instance()->call(SceneStringNames::get_singleton()->_draw);
}
current_item_drawn = nullptr;
drawing = false;
diff --git a/scene/main/node.cpp b/scene/main/node.cpp
index 88f9730f78..cdc06c7c5d 100644
--- a/scene/main/node.cpp
+++ b/scene/main/node.cpp
@@ -55,15 +55,13 @@ void Node::_notification(int p_notification) {
case NOTIFICATION_PROCESS: {
if (get_script_instance()) {
Variant time = get_process_delta_time();
- const Variant *ptr[1] = { &time };
- get_script_instance()->call_multilevel(SceneStringNames::get_singleton()->_process, ptr, 1);
+ get_script_instance()->call(SceneStringNames::get_singleton()->_process, time);
}
} break;
case NOTIFICATION_PHYSICS_PROCESS: {
if (get_script_instance()) {
Variant time = get_physics_process_delta_time();
- const Variant *ptr[1] = { &time };
- get_script_instance()->call_multilevel(SceneStringNames::get_singleton()->_physics_process, ptr, 1);
+ get_script_instance()->call(SceneStringNames::get_singleton()->_physics_process, time);
}
} break;
@@ -146,7 +144,7 @@ void Node::_notification(int p_notification) {
set_physics_process(true);
}
- get_script_instance()->call_multilevel_reversed(SceneStringNames::get_singleton()->_ready, nullptr, 0);
+ get_script_instance()->call(SceneStringNames::get_singleton()->_ready);
}
} break;
@@ -216,7 +214,7 @@ void Node::_propagate_enter_tree() {
notification(NOTIFICATION_ENTER_TREE);
if (get_script_instance()) {
- get_script_instance()->call_multilevel_reversed(SceneStringNames::get_singleton()->_enter_tree, nullptr, 0);
+ get_script_instance()->call(SceneStringNames::get_singleton()->_enter_tree);
}
emit_signal(SceneStringNames::get_singleton()->tree_entered);
@@ -264,7 +262,7 @@ void Node::_propagate_exit_tree() {
data.blocked--;
if (get_script_instance()) {
- get_script_instance()->call_multilevel(SceneStringNames::get_singleton()->_exit_tree, nullptr, 0);
+ get_script_instance()->call(SceneStringNames::get_singleton()->_exit_tree);
}
emit_signal(SceneStringNames::get_singleton()->tree_exiting);
diff --git a/scene/main/scene_tree.cpp b/scene/main/scene_tree.cpp
index d6159e089b..75b3d7a73d 100644
--- a/scene/main/scene_tree.cpp
+++ b/scene/main/scene_tree.cpp
@@ -250,11 +250,7 @@ void SceneTree::call_group_flags(uint32_t p_call_flags, const StringName &p_grou
}
if (p_call_flags & GROUP_CALL_REALTIME) {
- if (p_call_flags & GROUP_CALL_MULTILEVEL) {
- nodes[i]->call_multilevel(p_function, VARIANT_ARG_PASS);
- } else {
- nodes[i]->call(p_function, VARIANT_ARG_PASS);
- }
+ nodes[i]->call(p_function, VARIANT_ARG_PASS);
} else {
MessageQueue::get_singleton()->push_call(nodes[i], p_function, VARIANT_ARG_PASS);
}
@@ -267,11 +263,7 @@ void SceneTree::call_group_flags(uint32_t p_call_flags, const StringName &p_grou
}
if (p_call_flags & GROUP_CALL_REALTIME) {
- if (p_call_flags & GROUP_CALL_MULTILEVEL) {
- nodes[i]->call_multilevel(p_function, VARIANT_ARG_PASS);
- } else {
- nodes[i]->call(p_function, VARIANT_ARG_PASS);
- }
+ nodes[i]->call(p_function, VARIANT_ARG_PASS);
} else {
MessageQueue::get_singleton()->push_call(nodes[i], p_function, VARIANT_ARG_PASS);
}
@@ -883,7 +875,7 @@ void SceneTree::_call_input_pause(const StringName &p_group, const StringName &p
continue;
}
- n->call_multilevel(p_method, (const Variant **)v, 1);
+ n->call(p_method, (const Variant **)v, 1);
//ERR_FAIL_COND(node_count != g.nodes.size());
}
diff --git a/scene/main/scene_tree.h b/scene/main/scene_tree.h
index 41dc49bc64..0f74f2e973 100644
--- a/scene/main/scene_tree.h
+++ b/scene/main/scene_tree.h
@@ -223,7 +223,6 @@ public:
GROUP_CALL_REVERSE = 1,
GROUP_CALL_REALTIME = 2,
GROUP_CALL_UNIQUE = 4,
- GROUP_CALL_MULTILEVEL = 8,
};
_FORCE_INLINE_ Window *get_root() const { return root; }
diff --git a/scene/main/viewport.cpp b/scene/main/viewport.cpp
index 1c259b7d32..16d0325881 100644
--- a/scene/main/viewport.cpp
+++ b/scene/main/viewport.cpp
@@ -1607,7 +1607,7 @@ void Viewport::_gui_call_input(Control *p_control, const Ref<InputEvent> &p_inpu
}
if (control->data.mouse_filter != Control::MOUSE_FILTER_IGNORE) {
- control->call_multilevel(SceneStringNames::get_singleton()->_gui_input, ev);
+ control->call(SceneStringNames::get_singleton()->_gui_input, ev);
}
if (!control->is_inside_tree() || control->is_set_as_toplevel()) {
@@ -2306,7 +2306,7 @@ void Viewport::_gui_input_event(Ref<InputEvent> p_event) {
if (gui.key_focus) {
gui.key_event_accepted = false;
if (gui.key_focus->can_process()) {
- gui.key_focus->call_multilevel(SceneStringNames::get_singleton()->_gui_input, p_event);
+ gui.key_focus->call(SceneStringNames::get_singleton()->_gui_input, p_event);
if (gui.key_focus) { //maybe lost it
gui.key_focus->emit_signal(SceneStringNames::get_singleton()->gui_input, p_event);
}
@@ -2516,7 +2516,7 @@ void Viewport::_drop_mouse_focus() {
mb->set_global_position(c->get_local_mouse_position());
mb->set_button_index(i + 1);
mb->set_pressed(false);
- c->call_multilevel(SceneStringNames::get_singleton()->_gui_input, mb);
+ c->call(SceneStringNames::get_singleton()->_gui_input, mb);
}
}
}
@@ -2581,7 +2581,7 @@ void Viewport::_post_gui_grab_click_focus() {
mb->set_position(click);
mb->set_button_index(i + 1);
mb->set_pressed(false);
- gui.mouse_focus->call_multilevel(SceneStringNames::get_singleton()->_gui_input, mb);
+ gui.mouse_focus->call(SceneStringNames::get_singleton()->_gui_input, mb);
}
}
diff --git a/scene/register_scene_types.cpp b/scene/register_scene_types.cpp
index 3cbc64c075..db80dbe814 100644
--- a/scene/register_scene_types.cpp
+++ b/scene/register_scene_types.cpp
@@ -372,7 +372,7 @@ void register_scene_types() {
OS::get_singleton()->yield(); //may take time to init
- AcceptDialog::set_swap_cancel_ok(GLOBAL_DEF("gui/common/swap_cancel_ok", bool(DisplayServer::get_singleton()->get_swap_cancel_ok())));
+ AcceptDialog::set_swap_cancel_ok(GLOBAL_DEF_NOVAL("gui/common/swap_cancel_ok", bool(DisplayServer::get_singleton()->get_swap_cancel_ok())));
#endif
/* REGISTER 3D */
diff --git a/scene/resources/visual_shader.cpp b/scene/resources/visual_shader.cpp
index 8236f9a9e3..792e1ac2d7 100644
--- a/scene/resources/visual_shader.cpp
+++ b/scene/resources/visual_shader.cpp
@@ -63,6 +63,21 @@ bool VisualShaderNode::is_port_separator(int p_index) const {
return false;
}
+bool VisualShaderNode::is_output_port_connected(int p_port) const {
+ if (connected_output_ports.has(p_port)) {
+ return connected_output_ports[p_port];
+ }
+ return false;
+}
+
+void VisualShaderNode::set_output_port_connected(int p_port, bool p_connected) {
+ connected_output_ports[p_port] = p_connected;
+}
+
+bool VisualShaderNode::is_code_generated() const {
+ return true;
+}
+
Vector<VisualShader::DefaultTextureParam> VisualShaderNode::get_default_texture_parameters(VisualShader::Type p_type, int p_id) const {
return Vector<VisualShader::DefaultTextureParam>();
}
@@ -526,6 +541,7 @@ void VisualShader::connect_nodes_forced(Type p_type, int p_from_node, int p_from
c.to_port = p_to_port;
g->connections.push_back(c);
g->nodes[p_to_node].prev_connected_nodes.push_back(p_from_node);
+ g->nodes[p_from_node].node->set_output_port_connected(p_from_port, true);
_queue_update();
}
@@ -557,6 +573,7 @@ Error VisualShader::connect_nodes(Type p_type, int p_from_node, int p_from_port,
c.to_port = p_to_port;
g->connections.push_back(c);
g->nodes[p_to_node].prev_connected_nodes.push_back(p_from_node);
+ g->nodes[p_from_node].node->set_output_port_connected(p_from_port, true);
_queue_update();
return OK;
@@ -570,6 +587,7 @@ void VisualShader::disconnect_nodes(Type p_type, int p_from_node, int p_from_por
if (E->get().from_node == p_from_node && E->get().from_port == p_from_port && E->get().to_node == p_to_node && E->get().to_port == p_to_port) {
g->connections.erase(E);
g->nodes[p_to_node].prev_connected_nodes.erase(p_from_node);
+ g->nodes[p_from_node].node->set_output_port_connected(p_from_port, false);
_queue_update();
return;
}
@@ -1105,6 +1123,35 @@ Error VisualShader::_write_node(Type type, StringBuilder &global_code, StringBui
// then this node
+ Vector<VisualShader::DefaultTextureParam> params = vsnode->get_default_texture_parameters(type, node);
+ for (int i = 0; i < params.size(); i++) {
+ def_tex_params.push_back(params[i]);
+ }
+
+ Ref<VisualShaderNodeInput> input = vsnode;
+ bool skip_global = input.is_valid() && for_preview;
+
+ if (!skip_global) {
+ global_code += vsnode->generate_global(get_mode(), type, node);
+
+ String class_name = vsnode->get_class_name();
+ if (class_name == "VisualShaderNodeCustom") {
+ class_name = vsnode->get_script_instance()->get_script()->get_path();
+ }
+ if (!r_classes.has(class_name)) {
+ global_code_per_node += vsnode->generate_global_per_node(get_mode(), type, node);
+ for (int i = 0; i < TYPE_MAX; i++) {
+ global_code_per_func[Type(i)] += vsnode->generate_global_per_func(get_mode(), Type(i), node);
+ }
+ r_classes.insert(class_name);
+ }
+ }
+
+ if (!vsnode->is_code_generated()) { // just generate globals and ignore locals
+ processed.insert(node);
+ return OK;
+ }
+
code += "// " + vsnode->get_caption() + ":" + itos(node) + "\n";
Vector<String> input_vars;
@@ -1255,30 +1302,6 @@ Error VisualShader::_write_node(Type type, StringBuilder &global_code, StringBui
}
}
- Vector<VisualShader::DefaultTextureParam> params = vsnode->get_default_texture_parameters(type, node);
- for (int i = 0; i < params.size(); i++) {
- def_tex_params.push_back(params[i]);
- }
-
- Ref<VisualShaderNodeInput> input = vsnode;
- bool skip_global = input.is_valid() && for_preview;
-
- if (!skip_global) {
- global_code += vsnode->generate_global(get_mode(), type, node);
-
- String class_name = vsnode->get_class_name();
- if (class_name == "VisualShaderNodeCustom") {
- class_name = vsnode->get_script_instance()->get_script()->get_path();
- }
- if (!r_classes.has(class_name)) {
- global_code_per_node += vsnode->generate_global_per_node(get_mode(), type, node);
- for (int i = 0; i < TYPE_MAX; i++) {
- global_code_per_func[Type(i)] += vsnode->generate_global_per_func(get_mode(), Type(i), node);
- }
- r_classes.insert(class_name);
- }
- }
-
code += vsnode->generate_code(get_mode(), type, node, inputs, outputs, for_preview);
code += "\n"; //
diff --git a/scene/resources/visual_shader.h b/scene/resources/visual_shader.h
index dbb8d1d28c..cad567d32f 100644
--- a/scene/resources/visual_shader.h
+++ b/scene/resources/visual_shader.h
@@ -182,6 +182,7 @@ class VisualShaderNode : public Resource {
int port_preview;
Map<int, Variant> default_input_values;
+ Map<int, bool> connected_output_ports;
protected:
bool simple_decl;
@@ -222,6 +223,11 @@ public:
virtual bool is_port_separator(int p_index) const;
+ bool is_output_port_connected(int p_port) const;
+ void set_output_port_connected(int p_port, bool p_connected);
+
+ virtual bool is_code_generated() const;
+
virtual Vector<StringName> get_editable_properties() const;
virtual Vector<VisualShader::DefaultTextureParam> get_default_texture_parameters(VisualShader::Type p_type, int p_id) const;
diff --git a/scene/resources/visual_shader_nodes.cpp b/scene/resources/visual_shader_nodes.cpp
index 88f5287831..4cf382a933 100644
--- a/scene/resources/visual_shader_nodes.cpp
+++ b/scene/resources/visual_shader_nodes.cpp
@@ -3915,6 +3915,10 @@ String VisualShaderNodeTextureUniform::generate_global(Shader::Mode p_mode, Visu
return code;
}
+bool VisualShaderNodeTextureUniform::is_code_generated() const {
+ return is_output_port_connected(0) || is_output_port_connected(1); // rgb or alpha
+}
+
String VisualShaderNodeTextureUniform::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview) const {
String id = get_uniform_name();
String code = "\t{\n";
diff --git a/scene/resources/visual_shader_nodes.h b/scene/resources/visual_shader_nodes.h
index 13a132c60e..b82d5c65c9 100644
--- a/scene/resources/visual_shader_nodes.h
+++ b/scene/resources/visual_shader_nodes.h
@@ -1715,6 +1715,8 @@ public:
virtual String generate_global(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const override;
virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview = false) const override; //if no output is connected, the output var passed will be empty. if no input is connected and input is NIL, the input var passed will be empty
+ virtual bool is_code_generated() const override;
+
Vector<StringName> get_editable_properties() const override;
void set_texture_type(TextureType p_type);
diff --git a/tests/SCsub b/tests/SCsub
new file mode 100644
index 0000000000..9c32eb8ec3
--- /dev/null
+++ b/tests/SCsub
@@ -0,0 +1,15 @@
+#!/usr/bin/python
+
+Import("env")
+
+env.tests_sources = []
+
+env_tests = env.Clone()
+
+# Enable test framework and inform it of configuration method.
+env_tests.Append(CPPDEFINES=["DOCTEST_CONFIG_IMPLEMENT"])
+
+env_tests.add_source_files(env.tests_sources, "*.cpp")
+
+lib = env_tests.add_library("tests", env.tests_sources)
+env.Prepend(LIBS=[lib])
diff --git a/main/tests/test_astar.cpp b/tests/test_astar.cpp
index cb5fcfe37b..cb5fcfe37b 100644
--- a/main/tests/test_astar.cpp
+++ b/tests/test_astar.cpp
diff --git a/main/tests/test_astar.h b/tests/test_astar.h
index 0992812c18..0992812c18 100644
--- a/main/tests/test_astar.h
+++ b/tests/test_astar.h
diff --git a/main/tests/test_basis.cpp b/tests/test_basis.cpp
index 5904fc386a..5904fc386a 100644
--- a/main/tests/test_basis.cpp
+++ b/tests/test_basis.cpp
diff --git a/main/tests/test_basis.h b/tests/test_basis.h
index 63297bd3b8..63297bd3b8 100644
--- a/main/tests/test_basis.h
+++ b/tests/test_basis.h
diff --git a/main/tests/test_class_db.cpp b/tests/test_class_db.cpp
index 3171091402..3171091402 100644
--- a/main/tests/test_class_db.cpp
+++ b/tests/test_class_db.cpp
diff --git a/main/tests/test_class_db.h b/tests/test_class_db.h
index 1a31cfb01b..1a31cfb01b 100644
--- a/main/tests/test_class_db.h
+++ b/tests/test_class_db.h
diff --git a/main/tests/test_gdscript.cpp b/tests/test_gdscript.cpp
index a50311972f..a50311972f 100644
--- a/main/tests/test_gdscript.cpp
+++ b/tests/test_gdscript.cpp
diff --git a/main/tests/test_gdscript.h b/tests/test_gdscript.h
index 6595da1430..6595da1430 100644
--- a/main/tests/test_gdscript.h
+++ b/tests/test_gdscript.h
diff --git a/main/tests/test_gui.cpp b/tests/test_gui.cpp
index d46a13d2c0..d46a13d2c0 100644
--- a/main/tests/test_gui.cpp
+++ b/tests/test_gui.cpp
diff --git a/main/tests/test_gui.h b/tests/test_gui.h
index 5a23179eee..5a23179eee 100644
--- a/main/tests/test_gui.h
+++ b/tests/test_gui.h
diff --git a/main/tests/test_main.cpp b/tests/test_main.cpp
index 91eff28f86..0fb9f2fcda 100644
--- a/main/tests/test_main.cpp
+++ b/tests/test_main.cpp
@@ -49,6 +49,8 @@
#include "test_string.h"
#include "test_validate_testing.h"
+#include "modules/modules_tests.gen.h"
+
#include "thirdparty/doctest/doctest.h"
const char **tests_get_names() {
diff --git a/main/tests/test_main.h b/tests/test_main.h
index 8273b74eac..8273b74eac 100644
--- a/main/tests/test_main.h
+++ b/tests/test_main.h
diff --git a/main/tests/test_math.cpp b/tests/test_math.cpp
index 5f84bad4e9..5f84bad4e9 100644
--- a/main/tests/test_math.cpp
+++ b/tests/test_math.cpp
diff --git a/main/tests/test_math.h b/tests/test_math.h
index 77bce8dd66..77bce8dd66 100644
--- a/main/tests/test_math.h
+++ b/tests/test_math.h
diff --git a/main/tests/test_oa_hash_map.cpp b/tests/test_oa_hash_map.cpp
index 9182f66b61..9182f66b61 100644
--- a/main/tests/test_oa_hash_map.cpp
+++ b/tests/test_oa_hash_map.cpp
diff --git a/main/tests/test_oa_hash_map.h b/tests/test_oa_hash_map.h
index eb2b3d1e99..eb2b3d1e99 100644
--- a/main/tests/test_oa_hash_map.h
+++ b/tests/test_oa_hash_map.h
diff --git a/main/tests/test_ordered_hash_map.cpp b/tests/test_ordered_hash_map.cpp
index d18a3784be..d18a3784be 100644
--- a/main/tests/test_ordered_hash_map.cpp
+++ b/tests/test_ordered_hash_map.cpp
diff --git a/main/tests/test_ordered_hash_map.h b/tests/test_ordered_hash_map.h
index f251da0ba2..f251da0ba2 100644
--- a/main/tests/test_ordered_hash_map.h
+++ b/tests/test_ordered_hash_map.h
diff --git a/main/tests/test_physics_2d.cpp b/tests/test_physics_2d.cpp
index c82ae920bc..c82ae920bc 100644
--- a/main/tests/test_physics_2d.cpp
+++ b/tests/test_physics_2d.cpp
diff --git a/main/tests/test_physics_2d.h b/tests/test_physics_2d.h
index 517d324f3b..517d324f3b 100644
--- a/main/tests/test_physics_2d.h
+++ b/tests/test_physics_2d.h
diff --git a/main/tests/test_physics_3d.cpp b/tests/test_physics_3d.cpp
index 72de2041e4..72de2041e4 100644
--- a/main/tests/test_physics_3d.cpp
+++ b/tests/test_physics_3d.cpp
diff --git a/main/tests/test_physics_3d.h b/tests/test_physics_3d.h
index d03f2c6573..d03f2c6573 100644
--- a/main/tests/test_physics_3d.h
+++ b/tests/test_physics_3d.h
diff --git a/main/tests/test_render.cpp b/tests/test_render.cpp
index d936dd72e7..d936dd72e7 100644
--- a/main/tests/test_render.cpp
+++ b/tests/test_render.cpp
diff --git a/main/tests/test_render.h b/tests/test_render.h
index 4a6340c443..4a6340c443 100644
--- a/main/tests/test_render.h
+++ b/tests/test_render.h
diff --git a/main/tests/test_shader_lang.cpp b/tests/test_shader_lang.cpp
index 34ee3e3210..34ee3e3210 100644
--- a/main/tests/test_shader_lang.cpp
+++ b/tests/test_shader_lang.cpp
diff --git a/main/tests/test_shader_lang.h b/tests/test_shader_lang.h
index 2811c5f46e..2811c5f46e 100644
--- a/main/tests/test_shader_lang.h
+++ b/tests/test_shader_lang.h
diff --git a/main/tests/test_string.h b/tests/test_string.h
index 25fd513a1a..25fd513a1a 100644
--- a/main/tests/test_string.h
+++ b/tests/test_string.h
diff --git a/main/tests/test_validate_testing.h b/tests/test_validate_testing.h
index 5be7d45185..5be7d45185 100644
--- a/main/tests/test_validate_testing.h
+++ b/tests/test_validate_testing.h
diff --git a/thirdparty/vulkan/patches/VMA-assert-remove.patch b/thirdparty/vulkan/patches/VMA-assert-remove.patch
new file mode 100644
index 0000000000..3d57ab7d42
--- /dev/null
+++ b/thirdparty/vulkan/patches/VMA-assert-remove.patch
@@ -0,0 +1,29 @@
+diff --git a/thirdparty/vulkan/vk_mem_alloc.h b/thirdparty/vulkan/vk_mem_alloc.h
+index 0dfb66efc6..8a42699e7f 100644
+--- a/thirdparty/vulkan/vk_mem_alloc.h
++++ b/thirdparty/vulkan/vk_mem_alloc.h
+@@ -17508,24 +17508,6 @@ VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateBuffer(
+ allocator->GetBufferMemoryRequirements(*pBuffer, vkMemReq,
+ requiresDedicatedAllocation, prefersDedicatedAllocation);
+
+- // Make sure alignment requirements for specific buffer usages reported
+- // in Physical Device Properties are included in alignment reported by memory requirements.
+- if((pBufferCreateInfo->usage & VK_BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT) != 0)
+- {
+- VMA_ASSERT(vkMemReq.alignment %
+- allocator->m_PhysicalDeviceProperties.limits.minTexelBufferOffsetAlignment == 0);
+- }
+- if((pBufferCreateInfo->usage & VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT) != 0)
+- {
+- VMA_ASSERT(vkMemReq.alignment %
+- allocator->m_PhysicalDeviceProperties.limits.minUniformBufferOffsetAlignment == 0);
+- }
+- if((pBufferCreateInfo->usage & VK_BUFFER_USAGE_STORAGE_BUFFER_BIT) != 0)
+- {
+- VMA_ASSERT(vkMemReq.alignment %
+- allocator->m_PhysicalDeviceProperties.limits.minStorageBufferOffsetAlignment == 0);
+- }
+-
+ // 3. Allocate memory using allocator.
+ res = allocator->AllocateMemory(
+ vkMemReq,
diff --git a/thirdparty/vulkan/vk_mem_alloc.h b/thirdparty/vulkan/vk_mem_alloc.h
index 0dfb66efc6..8a42699e7f 100644
--- a/thirdparty/vulkan/vk_mem_alloc.h
+++ b/thirdparty/vulkan/vk_mem_alloc.h
@@ -17508,24 +17508,6 @@ VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateBuffer(
allocator->GetBufferMemoryRequirements(*pBuffer, vkMemReq,
requiresDedicatedAllocation, prefersDedicatedAllocation);
- // Make sure alignment requirements for specific buffer usages reported
- // in Physical Device Properties are included in alignment reported by memory requirements.
- if((pBufferCreateInfo->usage & VK_BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT) != 0)
- {
- VMA_ASSERT(vkMemReq.alignment %
- allocator->m_PhysicalDeviceProperties.limits.minTexelBufferOffsetAlignment == 0);
- }
- if((pBufferCreateInfo->usage & VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT) != 0)
- {
- VMA_ASSERT(vkMemReq.alignment %
- allocator->m_PhysicalDeviceProperties.limits.minUniformBufferOffsetAlignment == 0);
- }
- if((pBufferCreateInfo->usage & VK_BUFFER_USAGE_STORAGE_BUFFER_BIT) != 0)
- {
- VMA_ASSERT(vkMemReq.alignment %
- allocator->m_PhysicalDeviceProperties.limits.minStorageBufferOffsetAlignment == 0);
- }
-
// 3. Allocate memory using allocator.
res = allocator->AllocateMemory(
vkMemReq,