summaryrefslogtreecommitdiff
path: root/platform
diff options
context:
space:
mode:
Diffstat (limited to 'platform')
-rw-r--r--platform/android/SCsub1
-rw-r--r--platform/android/android_keys_utils.h4
-rw-r--r--platform/android/api/api.cpp6
-rw-r--r--platform/android/api/java_class_wrapper.h10
-rw-r--r--platform/android/audio_driver_jandroid.cpp185
-rw-r--r--platform/android/detect.py14
-rw-r--r--platform/android/dir_access_jandroid.cpp3
-rw-r--r--platform/android/dir_access_jandroid.h9
-rw-r--r--platform/android/display_server_android.cpp173
-rw-r--r--platform/android/display_server_android.h49
-rw-r--r--platform/android/export/export.cpp298
-rw-r--r--platform/android/export/gradle_export_util.h62
-rw-r--r--platform/android/file_access_android.cpp13
-rw-r--r--platform/android/file_access_android.h14
-rw-r--r--platform/android/java/app/AndroidManifest.xml14
-rw-r--r--platform/android/java/app/assets/.gitignore2
-rw-r--r--platform/android/java/app/build.gradle24
-rw-r--r--platform/android/java/app/config.gradle56
-rw-r--r--platform/android/java/app/res/drawable-nodpi/splash.png (renamed from platform/android/java/app/res/drawable/splash.png)bin14766 -> 14766 bytes
-rw-r--r--platform/android/java/app/res/drawable-nodpi/splash_bg_color.png (renamed from platform/android/java/app/res/drawable/splash_bg_color.png)bin1360 -> 1360 bytes
-rw-r--r--platform/android/java/build.gradle45
-rw-r--r--platform/android/java/gradle.properties2
-rw-r--r--platform/android/java/gradle/wrapper/gradle-wrapper.properties6
-rw-r--r--platform/android/java/gradlew.bat4
-rw-r--r--platform/android/java/lib/build.gradle6
-rw-r--r--platform/android/java/lib/res/values/strings.xml2
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/Dictionary.java16
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/FullScreenGodotApp.java30
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/Godot.java228
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/GodotGLRenderView.java45
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/GodotHost.java (renamed from platform/javascript/api/javascript_eval.h)51
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/GodotIO.java109
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/GodotLib.java6
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/GodotRenderView.java16
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/GodotRenderer.java1
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/GodotVulkanRenderView.java47
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/input/GodotGestureHandler.java14
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/input/GodotInputHandler.java117
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/input/GodotTextInputWrapper.java58
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/input/InputManagerCompat.java18
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/input/InputManagerV16.java2
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/input/Joystick.java4
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/plugin/GodotPlugin.java92
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/plugin/GodotPluginInfoProvider.java23
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/plugin/UsedByGodot.java (renamed from platform/server/godot_server.cpp)32
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/utils/Crypt.java4
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/vulkan/VkSurfaceView.kt2
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/vulkan/VkThread.kt6
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/xr/regular/RegularContextFactory.java1
-rw-r--r--platform/android/java/nativeSrcsConfigs/README.md2
-rw-r--r--platform/android/java/nativeSrcsConfigs/build.gradle3
-rw-r--r--platform/android/java_class_wrapper.cpp7
-rw-r--r--platform/android/java_godot_io_wrapper.cpp24
-rw-r--r--platform/android/java_godot_io_wrapper.h2
-rw-r--r--platform/android/java_godot_lib_jni.cpp33
-rw-r--r--platform/android/java_godot_lib_jni.h1
-rw-r--r--platform/android/java_godot_view_wrapper.cpp17
-rw-r--r--platform/android/java_godot_view_wrapper.h4
-rw-r--r--platform/android/java_godot_wrapper.cpp63
-rw-r--r--platform/android/java_godot_wrapper.h2
-rw-r--r--platform/android/net_socket_android.cpp4
-rw-r--r--platform/android/net_socket_android.h4
-rw-r--r--platform/android/os_android.cpp43
-rw-r--r--platform/android/os_android.h3
-rw-r--r--platform/android/plugin/godot_plugin_config.h6
-rw-r--r--platform/android/plugin/godot_plugin_jni.cpp2
-rw-r--r--platform/android/thread_jandroid.cpp27
-rw-r--r--platform/android/vulkan/vulkan_context_android.cpp14
-rw-r--r--platform/android/vulkan/vulkan_context_android.h11
-rw-r--r--platform/iphone/SCsub1
-rw-r--r--platform/iphone/detect.py2
-rw-r--r--platform/iphone/display_server_iphone.h15
-rw-r--r--platform/iphone/display_server_iphone.mm101
-rw-r--r--platform/iphone/export/export.cpp126
-rw-r--r--platform/iphone/godot_app_delegate.m2
-rw-r--r--platform/iphone/godot_iphone.mm4
-rw-r--r--platform/iphone/godot_view.mm22
-rw-r--r--platform/iphone/ios.mm8
-rw-r--r--platform/iphone/joypad_iphone.mm2
-rw-r--r--platform/iphone/keyboard_input_view.mm6
-rw-r--r--platform/iphone/native_video_view.m266
-rw-r--r--platform/iphone/os_iphone.mm12
-rw-r--r--platform/iphone/plugin/godot_plugin_config.h92
-rw-r--r--platform/iphone/view_controller.h5
-rw-r--r--platform/iphone/view_controller.mm24
-rw-r--r--platform/iphone/vulkan_context_iphone.h2
-rw-r--r--platform/iphone/vulkan_context_iphone.mm8
-rw-r--r--platform/javascript/SCsub49
-rw-r--r--platform/javascript/api/api.cpp41
-rw-r--r--platform/javascript/api/javascript_singleton.h (renamed from platform/android/audio_driver_jandroid.h)68
-rw-r--r--platform/javascript/api/javascript_tools_editor_plugin.cpp35
-rw-r--r--platform/javascript/detect.py43
-rw-r--r--platform/javascript/display_server_javascript.cpp135
-rw-r--r--platform/javascript/display_server_javascript.h9
-rw-r--r--platform/javascript/dom_keys.inc4
-rw-r--r--platform/javascript/emscripten_helpers.py89
-rw-r--r--platform/javascript/export/export.cpp684
-rw-r--r--platform/javascript/godot_js.h12
-rw-r--r--platform/javascript/http_client.h.inc53
-rw-r--r--platform/javascript/http_client_javascript.cpp214
-rw-r--r--platform/javascript/http_client_javascript.h108
-rw-r--r--platform/javascript/http_request.h73
-rw-r--r--platform/javascript/javascript_eval.cpp79
-rw-r--r--platform/javascript/javascript_main.cpp5
-rw-r--r--platform/javascript/javascript_singleton.cpp357
-rw-r--r--platform/javascript/js/engine/config.js54
-rw-r--r--platform/javascript/js/engine/engine.externs.js1
-rw-r--r--platform/javascript/js/engine/engine.js41
-rw-r--r--platform/javascript/js/engine/preloader.js108
-rw-r--r--platform/javascript/js/libs/library_godot_audio.js7
-rw-r--r--platform/javascript/js/libs/library_godot_display.js235
-rw-r--r--platform/javascript/js/libs/library_godot_editor_tools.js57
-rw-r--r--platform/javascript/js/libs/library_godot_eval.js86
-rw-r--r--platform/javascript/js/libs/library_godot_fetch.js247
-rw-r--r--platform/javascript/js/libs/library_godot_http_request.js142
-rw-r--r--platform/javascript/js/libs/library_godot_javascript_singleton.js346
-rw-r--r--platform/javascript/js/libs/library_godot_os.js26
-rw-r--r--platform/javascript/js/libs/library_godot_runtime.js16
-rw-r--r--platform/javascript/os_javascript.cpp5
-rw-r--r--platform/javascript/package-lock.json1017
-rw-r--r--platform/javascript/package.json8
-rw-r--r--platform/linuxbsd/SCsub23
-rw-r--r--platform/linuxbsd/detect.py81
-rw-r--r--platform/linuxbsd/display_server_x11.cpp154
-rw-r--r--platform/linuxbsd/display_server_x11.h31
-rw-r--r--platform/linuxbsd/export/export.cpp6
-rw-r--r--platform/linuxbsd/freedesktop_screensaver.cpp125
-rw-r--r--platform/linuxbsd/freedesktop_screensaver.h (renamed from platform/iphone/native_video_view.h)24
-rw-r--r--platform/linuxbsd/joypad_linux.cpp37
-rw-r--r--platform/linuxbsd/joypad_linux.h4
-rw-r--r--platform/linuxbsd/key_mapping_x11.cpp8
-rw-r--r--platform/linuxbsd/os_linuxbsd.cpp29
-rw-r--r--platform/linuxbsd/vulkan_context_x11.cpp4
-rw-r--r--platform/linuxbsd/vulkan_context_x11.h2
-rw-r--r--platform/osx/crash_handler_osx.mm10
-rw-r--r--platform/osx/detect.py17
-rw-r--r--platform/osx/dir_access_osx.h2
-rw-r--r--platform/osx/display_server_osx.h15
-rw-r--r--platform/osx/display_server_osx.mm176
-rw-r--r--platform/osx/export/export.cpp313
-rw-r--r--platform/osx/joypad_osx.cpp30
-rw-r--r--platform/osx/os_osx.mm42
-rw-r--r--platform/osx/vulkan_context_osx.h2
-rw-r--r--platform/osx/vulkan_context_osx.mm8
-rw-r--r--platform/server/SCsub16
-rw-r--r--platform/server/detect.py277
-rw-r--r--platform/server/logo.pngbin2016 -> 0 bytes
-rw-r--r--platform/server/os_server.cpp267
-rw-r--r--platform/server/os_server.h116
-rw-r--r--platform/server/platform_config.h49
-rw-r--r--platform/uwp/SCsub1
-rw-r--r--platform/uwp/app.cpp44
-rw-r--r--platform/uwp/detect.py11
-rw-r--r--platform/uwp/export/export.cpp51
-rw-r--r--platform/uwp/joypad_uwp.cpp4
-rw-r--r--platform/uwp/joypad_uwp.h2
-rw-r--r--platform/uwp/os_uwp.cpp26
-rw-r--r--platform/uwp/os_uwp.h4
-rw-r--r--platform/windows/context_gl_windows.cpp36
-rw-r--r--platform/windows/context_gl_windows.h3
-rw-r--r--platform/windows/crash_handler_windows.cpp2
-rw-r--r--platform/windows/detect.py17
-rw-r--r--platform/windows/display_server_windows.cpp239
-rw-r--r--platform/windows/display_server_windows.h18
-rw-r--r--platform/windows/export/export.cpp18
-rw-r--r--platform/windows/godot.natvis12
-rw-r--r--platform/windows/joypad_windows.cpp43
-rw-r--r--platform/windows/joypad_windows.h2
-rw-r--r--platform/windows/key_mapping_windows.cpp8
-rw-r--r--platform/windows/os_windows.cpp52
-rw-r--r--platform/windows/vulkan_context_win.cpp5
-rw-r--r--platform/windows/vulkan_context_win.h2
-rw-r--r--platform/windows/windows_terminal_logger.cpp29
173 files changed, 5403 insertions, 4442 deletions
diff --git a/platform/android/SCsub b/platform/android/SCsub
index 7e9dac926c..56fbd2f7e4 100644
--- a/platform/android/SCsub
+++ b/platform/android/SCsub
@@ -9,7 +9,6 @@ android_files = [
"dir_access_jandroid.cpp",
"thread_jandroid.cpp",
"net_socket_android.cpp",
- "audio_driver_jandroid.cpp",
"java_godot_lib_jni.cpp",
"java_class_wrapper.cpp",
"java_godot_wrapper.cpp",
diff --git a/platform/android/android_keys_utils.h b/platform/android/android_keys_utils.h
index e0ee2888c0..6d25a366a4 100644
--- a/platform/android/android_keys_utils.h
+++ b/platform/android/android_keys_utils.h
@@ -127,8 +127,8 @@ static _WinTranslatePair _ak_to_keycode[] = {
{ KEY_BACKSLASH, AKEYCODE_BACKSLASH },
{ KEY_BRACKETLEFT, AKEYCODE_LEFT_BRACKET },
{ KEY_BRACKETRIGHT, AKEYCODE_RIGHT_BRACKET },
- { KEY_CONTROL, AKEYCODE_CTRL_LEFT },
- { KEY_CONTROL, AKEYCODE_CTRL_RIGHT },
+ { KEY_CTRL, AKEYCODE_CTRL_LEFT },
+ { KEY_CTRL, AKEYCODE_CTRL_RIGHT },
{ KEY_UNKNOWN, 0 }
};
/*
diff --git a/platform/android/api/api.cpp b/platform/android/api/api.cpp
index d3c49c6eb7..03355e4815 100644
--- a/platform/android/api/api.cpp
+++ b/platform/android/api/api.cpp
@@ -44,11 +44,11 @@ void register_android_api() {
// `JNISingleton` registration occurs in
// `platform/android/java_godot_lib_jni.cpp#Java_org_godotengine_godot_GodotLib_setup`
java_class_wrapper = memnew(JavaClassWrapper); // Dummy
- ClassDB::register_class<JNISingleton>();
+ GDREGISTER_CLASS(JNISingleton);
#endif
- ClassDB::register_class<JavaClass>();
- ClassDB::register_class<JavaClassWrapper>();
+ GDREGISTER_CLASS(JavaClass);
+ GDREGISTER_CLASS(JavaClassWrapper);
Engine::get_singleton()->add_singleton(Engine::Singleton("JavaClassWrapper", JavaClassWrapper::get_singleton()));
}
diff --git a/platform/android/api/java_class_wrapper.h b/platform/android/api/java_class_wrapper.h
index d6c7a1abe5..ff7bf43573 100644
--- a/platform/android/api/java_class_wrapper.h
+++ b/platform/android/api/java_class_wrapper.h
@@ -31,7 +31,7 @@
#ifndef JAVA_CLASS_WRAPPER_H
#define JAVA_CLASS_WRAPPER_H
-#include "core/object/reference.h"
+#include "core/object/ref_counted.h"
#ifdef ANDROID_ENABLED
#include <android/log.h>
@@ -42,8 +42,8 @@
class JavaObject;
#endif
-class JavaClass : public Reference {
- GDCLASS(JavaClass, Reference);
+class JavaClass : public RefCounted {
+ GDCLASS(JavaClass, RefCounted);
#ifdef ANDROID_ENABLED
enum ArgumentType{
@@ -184,8 +184,8 @@ public:
JavaClass();
};
-class JavaObject : public Reference {
- GDCLASS(JavaObject, Reference);
+class JavaObject : public RefCounted {
+ GDCLASS(JavaObject, RefCounted);
#ifdef ANDROID_ENABLED
Ref<JavaClass> base_class;
diff --git a/platform/android/audio_driver_jandroid.cpp b/platform/android/audio_driver_jandroid.cpp
deleted file mode 100644
index 3a2ccac481..0000000000
--- a/platform/android/audio_driver_jandroid.cpp
+++ /dev/null
@@ -1,185 +0,0 @@
-/*************************************************************************/
-/* audio_driver_jandroid.cpp */
-/*************************************************************************/
-/* This file is part of: */
-/* GODOT ENGINE */
-/* https://godotengine.org */
-/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 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 "audio_driver_jandroid.h"
-
-#include "core/config/project_settings.h"
-#include "core/os/os.h"
-#include "thread_jandroid.h"
-
-AudioDriverAndroid *AudioDriverAndroid::s_ad = nullptr;
-
-jobject AudioDriverAndroid::io;
-jmethodID AudioDriverAndroid::_init_audio;
-jmethodID AudioDriverAndroid::_write_buffer;
-jmethodID AudioDriverAndroid::_quit;
-jmethodID AudioDriverAndroid::_pause;
-bool AudioDriverAndroid::active = false;
-jclass AudioDriverAndroid::cls;
-int AudioDriverAndroid::audioBufferFrames = 0;
-int AudioDriverAndroid::mix_rate = 44100;
-bool AudioDriverAndroid::quit = false;
-jobject AudioDriverAndroid::audioBuffer = nullptr;
-void *AudioDriverAndroid::audioBufferPinned = nullptr;
-Mutex AudioDriverAndroid::mutex;
-int32_t *AudioDriverAndroid::audioBuffer32 = nullptr;
-
-const char *AudioDriverAndroid::get_name() const {
- return "Android";
-}
-
-Error AudioDriverAndroid::init() {
- /*
- // TODO: pass in/return a (Java) device ID, also whether we're opening for input or output
- this->spec.samples = Android_JNI_OpenAudioDevice(this->spec.freq, this->spec.format == AUDIO_U8 ? 0 : 1, this->spec.channels, this->spec.samples);
- SDL_CalculateAudioSpec(&this->spec);
-
- if (this->spec.samples == 0) {
- // Init failed?
- SDL_SetError("Java-side initialization failed!");
- return 0;
- }
-*/
-
- //Android_JNI_SetupThread();
-
- // __android_log_print(ANDROID_LOG_VERBOSE, "SDL", "SDL audio: opening device");
-
- JNIEnv *env = get_jni_env();
- int mix_rate = GLOBAL_GET("audio/driver/mix_rate");
-
- int latency = GLOBAL_GET("audio/driver/output_latency");
- unsigned int buffer_size = next_power_of_2(latency * mix_rate / 1000);
- print_verbose("Audio buffer size: " + itos(buffer_size));
-
- audioBuffer = env->CallObjectMethod(io, _init_audio, mix_rate, buffer_size);
-
- ERR_FAIL_COND_V(audioBuffer == nullptr, ERR_INVALID_PARAMETER);
-
- audioBuffer = env->NewGlobalRef(audioBuffer);
-
- jboolean isCopy = JNI_FALSE;
- audioBufferPinned = env->GetShortArrayElements((jshortArray)audioBuffer, &isCopy);
- audioBufferFrames = env->GetArrayLength((jshortArray)audioBuffer);
- audioBuffer32 = memnew_arr(int32_t, audioBufferFrames);
-
- return OK;
-}
-
-void AudioDriverAndroid::start() {
- active = true;
-}
-
-void AudioDriverAndroid::setup(jobject p_io) {
- JNIEnv *env = get_jni_env();
- io = p_io;
-
- jclass c = env->GetObjectClass(io);
- cls = (jclass)env->NewGlobalRef(c);
-
- _init_audio = env->GetMethodID(cls, "audioInit", "(II)Ljava/lang/Object;");
- _write_buffer = env->GetMethodID(cls, "audioWriteShortBuffer", "([S)V");
- _quit = env->GetMethodID(cls, "audioQuit", "()V");
- _pause = env->GetMethodID(cls, "audioPause", "(Z)V");
-}
-
-void AudioDriverAndroid::thread_func(JNIEnv *env) {
- jclass cls = env->FindClass("org/godotengine/godot/Godot");
- if (cls) {
- cls = (jclass)env->NewGlobalRef(cls);
- }
- jfieldID fid = env->GetStaticFieldID(cls, "io", "Lorg/godotengine/godot/GodotIO;");
- jobject ob = env->GetStaticObjectField(cls, fid);
- jobject gob = env->NewGlobalRef(ob);
- jclass c = env->GetObjectClass(gob);
- jclass lcls = (jclass)env->NewGlobalRef(c);
- _write_buffer = env->GetMethodID(lcls, "audioWriteShortBuffer", "([S)V");
-
- while (!quit) {
- int16_t *ptr = (int16_t *)audioBufferPinned;
- int fc = audioBufferFrames;
-
- if (!s_ad->active || mutex.try_lock() != OK) {
- for (int i = 0; i < fc; i++) {
- ptr[i] = 0;
- }
-
- } else {
- s_ad->audio_server_process(fc / 2, audioBuffer32);
-
- mutex.unlock();
-
- for (int i = 0; i < fc; i++) {
- ptr[i] = audioBuffer32[i] >> 16;
- }
- }
- env->ReleaseShortArrayElements((jshortArray)audioBuffer, (jshort *)ptr, JNI_COMMIT);
- env->CallVoidMethod(gob, _write_buffer, (jshortArray)audioBuffer);
- }
-}
-
-int AudioDriverAndroid::get_mix_rate() const {
- return mix_rate;
-}
-
-AudioDriver::SpeakerMode AudioDriverAndroid::get_speaker_mode() const {
- return SPEAKER_MODE_STEREO;
-}
-
-void AudioDriverAndroid::lock() {
- mutex.lock();
-}
-
-void AudioDriverAndroid::unlock() {
- mutex.unlock();
-}
-
-void AudioDriverAndroid::finish() {
- JNIEnv *env = get_jni_env();
- env->CallVoidMethod(io, _quit);
-
- if (audioBuffer) {
- env->DeleteGlobalRef(audioBuffer);
- audioBuffer = nullptr;
- audioBufferPinned = nullptr;
- }
-
- active = false;
-}
-
-void AudioDriverAndroid::set_pause(bool p_pause) {
- JNIEnv *env = get_jni_env();
- env->CallVoidMethod(io, _pause, p_pause);
-}
-
-AudioDriverAndroid::AudioDriverAndroid() {
- s_ad = this;
- active = false;
-}
diff --git a/platform/android/detect.py b/platform/android/detect.py
index 5f0fcc9b77..1b6af8662e 100644
--- a/platform/android/detect.py
+++ b/platform/android/detect.py
@@ -197,12 +197,11 @@ def configure(env):
if env["optimize"] == "speed": # optimize for speed (default)
env.Append(LINKFLAGS=["-O2"])
env.Append(CCFLAGS=["-O2", "-fomit-frame-pointer"])
- env.Append(CPPDEFINES=["NDEBUG"])
- else: # optimize for size
+ elif env["optimize"] == "size": # optimize for size
env.Append(CCFLAGS=["-Os"])
- env.Append(CPPDEFINES=["NDEBUG"])
env.Append(LINKFLAGS=["-Os"])
+ env.Append(CPPDEFINES=["NDEBUG"])
if can_vectorize:
env.Append(CCFLAGS=["-ftree-vectorize"])
if env["target"] == "release_debug":
@@ -251,7 +250,7 @@ def configure(env):
env["RANLIB"] = tools_path + "/ranlib"
env["AS"] = tools_path + "/as"
- common_opts = ["-fno-integrated-as", "-gcc-toolchain", gcc_toolchain_path]
+ common_opts = ["-gcc-toolchain", gcc_toolchain_path]
# Compile flags
@@ -259,8 +258,10 @@ def configure(env):
env.Append(CPPFLAGS=["-isystem", env["ANDROID_NDK_ROOT"] + "/sources/cxx-stl/llvm-libc++abi/include"])
# Disable exceptions and rtti on non-tools (template) builds
- if env["tools"] or env["builtin_icu"]:
+ if env["tools"]:
env.Append(CXXFLAGS=["-frtti"])
+ elif env["builtin_icu"]:
+ env.Append(CXXFLAGS=["-frtti", "-fno-exceptions"])
else:
env.Append(CXXFLAGS=["-fno-rtti", "-fno-exceptions"])
# Don't use dynamic_cast, necessary with no-rtti.
@@ -284,6 +285,9 @@ def configure(env):
)
env.Append(CPPDEFINES=["NO_STATVFS", "GLES_ENABLED"])
+ if get_platform(env["ndk_platform"]) >= 24:
+ env.Append(CPPDEFINES=[("_FILE_OFFSET_BITS", 64)])
+
env["neon_enabled"] = False
if env["android_arch"] == "x86":
target_opts = ["-target", "i686-none-linux-android"]
diff --git a/platform/android/dir_access_jandroid.cpp b/platform/android/dir_access_jandroid.cpp
index f8ac29c738..0bae090702 100644
--- a/platform/android/dir_access_jandroid.cpp
+++ b/platform/android/dir_access_jandroid.cpp
@@ -201,8 +201,7 @@ String DirAccessJAndroid::get_filesystem_type() const {
return "APK";
}
-//FileType get_file_type() const;
-size_t DirAccessJAndroid::get_space_left() {
+uint64_t DirAccessJAndroid::get_space_left() {
return 0;
}
diff --git a/platform/android/dir_access_jandroid.h b/platform/android/dir_access_jandroid.h
index fed468d051..cdf98187ed 100644
--- a/platform/android/dir_access_jandroid.h
+++ b/platform/android/dir_access_jandroid.h
@@ -31,7 +31,7 @@
#ifndef DIR_ACCESS_JANDROID_H
#define DIR_ACCESS_JANDROID_H
-#include "core/os/dir_access.h"
+#include "core/io/dir_access.h"
#include "java_godot_lib_jni.h"
#include <stdio.h>
@@ -74,10 +74,13 @@ public:
virtual Error rename(String p_from, String p_to);
virtual Error remove(String p_name);
+ virtual bool is_link(String p_file) { return false; }
+ virtual String read_link(String p_file) { return p_file; }
+ virtual Error create_link(String p_source, String p_target) { return FAILED; }
+
virtual String get_filesystem_type() const;
- //virtual FileType get_file_type() const;
- size_t get_space_left();
+ uint64_t get_space_left();
static void setup(jobject p_io);
diff --git a/platform/android/display_server_android.cpp b/platform/android/display_server_android.cpp
index 5f7e5eaa83..1fcc3d4a5c 100644
--- a/platform/android/display_server_android.cpp
+++ b/platform/android/display_server_android.cpp
@@ -36,8 +36,6 @@
#include "java_godot_wrapper.h"
#include "os_android.h"
-#include <android/input.h>
-
#if defined(VULKAN_ENABLED)
#include "drivers/vulkan/rendering_device_vulkan.h"
#include "platform/android/vulkan/vulkan_context_android.h"
@@ -51,7 +49,7 @@ DisplayServerAndroid *DisplayServerAndroid::get_singleton() {
bool DisplayServerAndroid::has_feature(Feature p_feature) const {
switch (p_feature) {
//case FEATURE_CONSOLE_WINDOW:
- //case FEATURE_CURSOR_SHAPE:
+ case FEATURE_CURSOR_SHAPE:
//case FEATURE_CUSTOM_CURSOR_SHAPE:
//case FEATURE_GLOBAL_MENU:
//case FEATURE_HIDPI:
@@ -61,7 +59,6 @@ bool DisplayServerAndroid::has_feature(Feature p_feature) const {
//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:
@@ -199,7 +196,7 @@ void DisplayServerAndroid::window_set_input_text_callback(const Callable &p_call
}
void DisplayServerAndroid::window_set_rect_changed_callback(const Callable &p_callable, DisplayServer::WindowID p_window) {
- // Not supported on Android.
+ rect_changed_callback = p_callable;
}
void DisplayServerAndroid::window_set_drop_files_callback(const Callable &p_callable, DisplayServer::WindowID p_window) {
@@ -361,8 +358,8 @@ Vector<String> DisplayServerAndroid::get_rendering_drivers_func() {
return drivers;
}
-DisplayServer *DisplayServerAndroid::create_func(const String &p_rendering_driver, DisplayServer::WindowMode p_mode, uint32_t p_flags, const Vector2i &p_resolution, Error &r_error) {
- DisplayServer *ds = memnew(DisplayServerAndroid(p_rendering_driver, p_mode, p_flags, p_resolution, r_error));
+DisplayServer *DisplayServerAndroid::create_func(const String &p_rendering_driver, DisplayServer::WindowMode p_mode, DisplayServer::VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i &p_resolution, Error &r_error) {
+ DisplayServer *ds = memnew(DisplayServerAndroid(p_rendering_driver, p_mode, p_vsync_mode, p_flags, p_resolution, r_error));
if (r_error != OK) {
ds->alert("Your video card driver does not support any of the supported Vulkan versions.", "Unable to initialize Video driver");
}
@@ -380,10 +377,11 @@ void DisplayServerAndroid::reset_window() {
ERR_FAIL_COND(!native_window);
ERR_FAIL_COND(!context_vulkan);
+ VSyncMode last_vsync_mode = context_vulkan->get_vsync_mode(MAIN_WINDOW_ID);
context_vulkan->window_destroy(MAIN_WINDOW_ID);
Size2i display_size = OS_Android::get_singleton()->get_display_size();
- if (context_vulkan->window_create(native_window, display_size.width, display_size.height) == -1) {
+ if (context_vulkan->window_create(native_window, last_vsync_mode, display_size.width, display_size.height) == -1) {
memdelete(context_vulkan);
context_vulkan = nullptr;
ERR_FAIL_MSG("Failed to reset Vulkan window.");
@@ -392,7 +390,20 @@ void DisplayServerAndroid::reset_window() {
#endif
}
-DisplayServerAndroid::DisplayServerAndroid(const String &p_rendering_driver, DisplayServer::WindowMode p_mode, uint32_t p_flags, const Vector2i &p_resolution, Error &r_error) {
+void DisplayServerAndroid::notify_surface_changed(int p_width, int p_height) {
+ if (rect_changed_callback.is_null()) {
+ return;
+ }
+
+ const Variant size = Rect2i(0, 0, p_width, p_height);
+ const Variant *sizep = &size;
+ Variant ret;
+ Callable::CallError ce;
+
+ rect_changed_callback.call(reinterpret_cast<const Variant **>(&sizep), 1, ret, ce);
+}
+
+DisplayServerAndroid::DisplayServerAndroid(const String &p_rendering_driver, DisplayServer::WindowMode p_mode, DisplayServer::VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i &p_resolution, Error &r_error) {
rendering_driver = p_rendering_driver;
// TODO: rendering_driver is broken, change when different drivers are supported again
@@ -400,8 +411,6 @@ DisplayServerAndroid::DisplayServerAndroid(const String &p_rendering_driver, Dis
keep_screen_on = GLOBAL_GET("display/window/energy_saving/keep_screen_on");
- buttons_state = 0;
-
#if defined(OPENGL_ENABLED)
if (rendering_driver == "opengl") {
bool gl_initialization_error = false;
@@ -438,7 +447,7 @@ DisplayServerAndroid::DisplayServerAndroid(const String &p_rendering_driver, Dis
}
Size2i display_size = OS_Android::get_singleton()->get_display_size();
- if (context_vulkan->window_create(native_window, display_size.width, display_size.height) == -1) {
+ if (context_vulkan->window_create(native_window, p_vsync_mode, display_size.width, display_size.height) == -1) {
memdelete(context_vulkan);
context_vulkan = nullptr;
ERR_FAIL_MSG("Failed to create Vulkan window.");
@@ -474,16 +483,16 @@ DisplayServerAndroid::~DisplayServerAndroid() {
void DisplayServerAndroid::process_joy_event(DisplayServerAndroid::JoypadEvent p_event) {
switch (p_event.type) {
case JOY_EVENT_BUTTON:
- Input::get_singleton()->joy_button(p_event.device, p_event.index, p_event.pressed);
+ Input::get_singleton()->joy_button(p_event.device, (JoyButton)p_event.index, p_event.pressed);
break;
case JOY_EVENT_AXIS:
- Input::JoyAxis value;
+ Input::JoyAxisValue value;
value.min = -1;
value.value = p_event.value;
- Input::get_singleton()->joy_axis(p_event.device, p_event.index, value);
+ Input::get_singleton()->joy_axis(p_event.device, (JoyAxis)p_event.index, value);
break;
case JOY_EVENT_HAT:
- Input::get_singleton()->joy_hat(p_event.device, p_event.hat);
+ Input::get_singleton()->joy_hat(p_event.device, (HatMask)p_event.hat);
break;
default:
return;
@@ -491,10 +500,10 @@ void DisplayServerAndroid::process_joy_event(DisplayServerAndroid::JoypadEvent p
}
void DisplayServerAndroid::_set_key_modifier_state(Ref<InputEventWithModifiers> ev) {
- ev->set_shift(shift_mem);
- ev->set_alt(alt_mem);
- ev->set_metakey(meta_mem);
- ev->set_control(control_mem);
+ ev->set_shift_pressed(shift_mem);
+ ev->set_alt_pressed(alt_mem);
+ ev->set_meta_pressed(meta_mem);
+ ev->set_ctrl_pressed(control_mem);
}
void DisplayServerAndroid::process_key_event(int p_keycode, int p_scancode, int p_unicode_char, bool p_pressed) {
@@ -518,7 +527,7 @@ void DisplayServerAndroid::process_key_event(int p_keycode, int p_scancode, int
}
Ref<InputEventKey> ev;
- ev.instance();
+ ev.instantiate();
int val = unicode;
int keycode = android_get_keysym(p_keycode);
int phy_keycode = android_get_keysym(p_scancode);
@@ -529,7 +538,7 @@ void DisplayServerAndroid::process_key_event(int p_keycode, int p_scancode, int
if (keycode == KEY_ALT) {
alt_mem = p_pressed;
}
- if (keycode == KEY_CONTROL) {
+ if (keycode == KEY_CTRL) {
control_mem = p_pressed;
}
if (keycode == KEY_META) {
@@ -565,7 +574,7 @@ void DisplayServerAndroid::process_touch(int p_event, int p_pointer, const Vecto
//end all if exist
for (int i = 0; i < touch.size(); i++) {
Ref<InputEventScreenTouch> ev;
- ev.instance();
+ ev.instantiate();
ev->set_index(touch[i].id);
ev->set_pressed(false);
ev->set_position(touch[i].pos);
@@ -582,7 +591,7 @@ void DisplayServerAndroid::process_touch(int p_event, int p_pointer, const Vecto
//send touch
for (int i = 0; i < touch.size(); i++) {
Ref<InputEventScreenTouch> ev;
- ev.instance();
+ ev.instantiate();
ev->set_index(touch[i].id);
ev->set_pressed(true);
ev->set_position(touch[i].pos);
@@ -608,7 +617,7 @@ void DisplayServerAndroid::process_touch(int p_event, int p_pointer, const Vecto
continue; //no move unncesearily
Ref<InputEventScreenDrag> ev;
- ev.instance();
+ ev.instantiate();
ev->set_index(touch[i].id);
ev->set_position(p_points[idx].pos);
ev->set_relative(p_points[idx].pos - touch[i].pos);
@@ -623,7 +632,7 @@ void DisplayServerAndroid::process_touch(int p_event, int p_pointer, const Vecto
//end all if exist
for (int i = 0; i < touch.size(); i++) {
Ref<InputEventScreenTouch> ev;
- ev.instance();
+ ev.instantiate();
ev->set_index(touch[i].id);
ev->set_pressed(false);
ev->set_position(touch[i].pos);
@@ -639,7 +648,7 @@ void DisplayServerAndroid::process_touch(int p_event, int p_pointer, const Vecto
touch.push_back(tp);
Ref<InputEventScreenTouch> ev;
- ev.instance();
+ ev.instantiate();
ev->set_index(tp.id);
ev->set_pressed(true);
@@ -654,7 +663,7 @@ void DisplayServerAndroid::process_touch(int p_event, int p_pointer, const Vecto
for (int i = 0; i < touch.size(); i++) {
if (touch[i].id == p_pointer) {
Ref<InputEventScreenTouch> ev;
- ev.instance();
+ ev.instantiate();
ev->set_index(touch[i].id);
ev->set_pressed(false);
ev->set_position(touch[i].pos);
@@ -675,7 +684,7 @@ void DisplayServerAndroid::process_hover(int p_type, Point2 p_pos) {
case AMOTION_EVENT_ACTION_HOVER_ENTER: // hover enter
case AMOTION_EVENT_ACTION_HOVER_EXIT: { // hover exit
Ref<InputEventMouseMotion> ev;
- ev.instance();
+ ev.instantiate();
_set_key_modifier_state(ev);
ev->set_position(p_pos);
ev->set_global_position(p_pos);
@@ -687,12 +696,12 @@ void DisplayServerAndroid::process_hover(int p_type, Point2 p_pos) {
}
void DisplayServerAndroid::process_mouse_event(int input_device, int event_action, int event_android_buttons_mask, Point2 event_pos, float event_vertical_factor, float event_horizontal_factor) {
- int event_buttons_mask = _android_button_mask_to_godot_button_mask(event_android_buttons_mask);
+ MouseButton event_buttons_mask = _android_button_mask_to_godot_button_mask(event_android_buttons_mask);
switch (event_action) {
case AMOTION_EVENT_ACTION_BUTTON_PRESS:
case AMOTION_EVENT_ACTION_BUTTON_RELEASE: {
Ref<InputEventMouseButton> ev;
- ev.instance();
+ ev.instantiate();
_set_key_modifier_state(ev);
if ((input_device & AINPUT_SOURCE_MOUSE) == AINPUT_SOURCE_MOUSE) {
ev->set_position(event_pos);
@@ -702,7 +711,7 @@ void DisplayServerAndroid::process_mouse_event(int input_device, int event_actio
ev->set_global_position(hover_prev_pos);
}
ev->set_pressed(event_action == AMOTION_EVENT_ACTION_BUTTON_PRESS);
- int changed_button_mask = buttons_state ^ event_buttons_mask;
+ MouseButton changed_button_mask = MouseButton(buttons_state ^ event_buttons_mask);
buttons_state = event_buttons_mask;
@@ -713,7 +722,7 @@ void DisplayServerAndroid::process_mouse_event(int input_device, int event_actio
case AMOTION_EVENT_ACTION_MOVE: {
Ref<InputEventMouseMotion> ev;
- ev.instance();
+ ev.instantiate();
_set_key_modifier_state(ev);
if ((input_device & AINPUT_SOURCE_MOUSE) == AINPUT_SOURCE_MOUSE) {
ev->set_position(event_pos);
@@ -730,7 +739,7 @@ void DisplayServerAndroid::process_mouse_event(int input_device, int event_actio
} break;
case AMOTION_EVENT_ACTION_SCROLL: {
Ref<InputEventMouseButton> ev;
- ev.instance();
+ ev.instantiate();
if ((input_device & AINPUT_SOURCE_MOUSE) == AINPUT_SOURCE_MOUSE) {
ev->set_position(event_pos);
ev->set_global_position(event_pos);
@@ -741,25 +750,25 @@ void DisplayServerAndroid::process_mouse_event(int input_device, int event_actio
ev->set_pressed(true);
buttons_state = event_buttons_mask;
if (event_vertical_factor > 0) {
- _wheel_button_click(event_buttons_mask, ev, BUTTON_WHEEL_UP, event_vertical_factor);
+ _wheel_button_click(event_buttons_mask, ev, MOUSE_BUTTON_WHEEL_UP, event_vertical_factor);
} else if (event_vertical_factor < 0) {
- _wheel_button_click(event_buttons_mask, ev, BUTTON_WHEEL_DOWN, -event_vertical_factor);
+ _wheel_button_click(event_buttons_mask, ev, MOUSE_BUTTON_WHEEL_DOWN, -event_vertical_factor);
}
if (event_horizontal_factor > 0) {
- _wheel_button_click(event_buttons_mask, ev, BUTTON_WHEEL_RIGHT, event_horizontal_factor);
+ _wheel_button_click(event_buttons_mask, ev, MOUSE_BUTTON_WHEEL_RIGHT, event_horizontal_factor);
} else if (event_horizontal_factor < 0) {
- _wheel_button_click(event_buttons_mask, ev, BUTTON_WHEEL_LEFT, -event_horizontal_factor);
+ _wheel_button_click(event_buttons_mask, ev, MOUSE_BUTTON_WHEEL_LEFT, -event_horizontal_factor);
}
} break;
}
}
-void DisplayServerAndroid::_wheel_button_click(int event_buttons_mask, const Ref<InputEventMouseButton> &ev, int wheel_button, float factor) {
+void DisplayServerAndroid::_wheel_button_click(MouseButton event_buttons_mask, const Ref<InputEventMouseButton> &ev, MouseButton wheel_button, float factor) {
Ref<InputEventMouseButton> evd = ev->duplicate();
_set_key_modifier_state(evd);
evd->set_button_index(wheel_button);
- evd->set_button_mask(event_buttons_mask ^ (1 << (wheel_button - 1)));
+ evd->set_button_mask(MouseButton(event_buttons_mask ^ (1 << (wheel_button - 1))));
evd->set_factor(factor);
Input::get_singleton()->accumulate_input_event(evd);
Ref<InputEventMouseButton> evdd = evd->duplicate();
@@ -769,39 +778,39 @@ void DisplayServerAndroid::_wheel_button_click(int event_buttons_mask, const Ref
}
void DisplayServerAndroid::process_double_tap(int event_android_button_mask, Point2 p_pos) {
- int event_button_mask = _android_button_mask_to_godot_button_mask(event_android_button_mask);
+ MouseButton event_button_mask = _android_button_mask_to_godot_button_mask(event_android_button_mask);
Ref<InputEventMouseButton> ev;
- ev.instance();
+ ev.instantiate();
_set_key_modifier_state(ev);
ev->set_position(p_pos);
ev->set_global_position(p_pos);
ev->set_pressed(event_button_mask != 0);
ev->set_button_index(_button_index_from_mask(event_button_mask));
ev->set_button_mask(event_button_mask);
- ev->set_doubleclick(true);
+ ev->set_double_click(true);
Input::get_singleton()->accumulate_input_event(ev);
}
-int DisplayServerAndroid::_button_index_from_mask(int button_mask) {
+MouseButton DisplayServerAndroid::_button_index_from_mask(MouseButton button_mask) {
switch (button_mask) {
- case BUTTON_MASK_LEFT:
- return BUTTON_LEFT;
- case BUTTON_MASK_RIGHT:
- return BUTTON_RIGHT;
- case BUTTON_MASK_MIDDLE:
- return BUTTON_MIDDLE;
- case BUTTON_MASK_XBUTTON1:
- return BUTTON_XBUTTON1;
- case BUTTON_MASK_XBUTTON2:
- return BUTTON_XBUTTON2;
+ case MOUSE_BUTTON_MASK_LEFT:
+ return MOUSE_BUTTON_LEFT;
+ case MOUSE_BUTTON_MASK_RIGHT:
+ return MOUSE_BUTTON_RIGHT;
+ case MOUSE_BUTTON_MASK_MIDDLE:
+ return MOUSE_BUTTON_MIDDLE;
+ case MOUSE_BUTTON_MASK_XBUTTON1:
+ return MOUSE_BUTTON_XBUTTON1;
+ case MOUSE_BUTTON_MASK_XBUTTON2:
+ return MOUSE_BUTTON_XBUTTON2;
default:
- return 0;
+ return MOUSE_BUTTON_NONE;
}
}
void DisplayServerAndroid::process_scroll(Point2 p_pos) {
Ref<InputEventPanGesture> ev;
- ev.instance();
+ ev.instantiate();
_set_key_modifier_state(ev);
ev->set_position(p_pos);
ev->set_delta(p_pos - scroll_prev_pos);
@@ -830,6 +839,12 @@ void DisplayServerAndroid::mouse_set_mode(MouseMode p_mode) {
return;
}
+ if (p_mode == MouseMode::MOUSE_MODE_HIDDEN) {
+ OS_Android::get_singleton()->get_godot_java()->get_godot_view()->set_pointer_icon(CURSOR_TYPE_NULL);
+ } else {
+ cursor_set_shape(cursor_shape);
+ }
+
if (p_mode == MouseMode::MOUSE_MODE_CAPTURED) {
OS_Android::get_singleton()->get_godot_java()->get_godot_view()->request_pointer_capture();
} else {
@@ -847,27 +862,57 @@ Point2i DisplayServerAndroid::mouse_get_position() const {
return hover_prev_pos;
}
-int DisplayServerAndroid::mouse_get_button_state() const {
+MouseButton DisplayServerAndroid::mouse_get_button_state() const {
return buttons_state;
}
-int DisplayServerAndroid::_android_button_mask_to_godot_button_mask(int android_button_mask) {
- int godot_button_mask = 0;
+MouseButton DisplayServerAndroid::_android_button_mask_to_godot_button_mask(int android_button_mask) {
+ MouseButton godot_button_mask = MOUSE_BUTTON_NONE;
if (android_button_mask & AMOTION_EVENT_BUTTON_PRIMARY) {
- godot_button_mask |= BUTTON_MASK_LEFT;
+ godot_button_mask |= MOUSE_BUTTON_MASK_LEFT;
}
if (android_button_mask & AMOTION_EVENT_BUTTON_SECONDARY) {
- godot_button_mask |= BUTTON_MASK_RIGHT;
+ godot_button_mask |= MOUSE_BUTTON_MASK_RIGHT;
}
if (android_button_mask & AMOTION_EVENT_BUTTON_TERTIARY) {
- godot_button_mask |= BUTTON_MASK_MIDDLE;
+ godot_button_mask |= MOUSE_BUTTON_MASK_MIDDLE;
}
if (android_button_mask & AMOTION_EVENT_BUTTON_BACK) {
- godot_button_mask |= BUTTON_MASK_XBUTTON1;
+ godot_button_mask |= MOUSE_BUTTON_MASK_XBUTTON1;
}
if (android_button_mask & AMOTION_EVENT_BUTTON_SECONDARY) {
- godot_button_mask |= BUTTON_MASK_XBUTTON2;
+ godot_button_mask |= MOUSE_BUTTON_MASK_XBUTTON2;
}
return godot_button_mask;
}
+
+void DisplayServerAndroid::cursor_set_shape(DisplayServer::CursorShape p_shape) {
+ if (cursor_shape == p_shape) {
+ return;
+ }
+
+ cursor_shape = p_shape;
+
+ if (mouse_mode == MouseMode::MOUSE_MODE_VISIBLE || mouse_mode == MouseMode::MOUSE_MODE_CONFINED) {
+ OS_Android::get_singleton()->get_godot_java()->get_godot_view()->set_pointer_icon(android_cursors[cursor_shape]);
+ }
+}
+
+DisplayServer::CursorShape DisplayServerAndroid::cursor_get_shape() const {
+ return cursor_shape;
+}
+
+void DisplayServerAndroid::window_set_vsync_mode(DisplayServer::VSyncMode p_vsync_mode, WindowID p_window) {
+#if defined(VULKAN_ENABLED)
+ context_vulkan->set_vsync_mode(p_window, p_vsync_mode);
+#endif
+}
+
+DisplayServer::VSyncMode DisplayServerAndroid::window_get_vsync_mode(WindowID p_window) const {
+#if defined(VULKAN_ENABLED)
+ return context_vulkan->get_vsync_mode(p_window);
+#else
+ return DisplayServer::VSYNC_ENABLED;
+#endif
+}
diff --git a/platform/android/display_server_android.h b/platform/android/display_server_android.h
index b9d1641656..bd5bed31cd 100644
--- a/platform/android/display_server_android.h
+++ b/platform/android/display_server_android.h
@@ -68,9 +68,31 @@ private:
bool control_mem = false;
bool meta_mem = false;
- int buttons_state;
-
- MouseMode mouse_mode;
+ MouseButton buttons_state = MOUSE_BUTTON_NONE;
+
+ // https://developer.android.com/reference/android/view/PointerIcon
+ // mapping between Godot's cursor shape to Android's'
+ int android_cursors[CURSOR_MAX] = {
+ 1000, //CURSOR_ARROW
+ 1008, //CURSOR_IBEAM
+ 1002, //CURSOR_POINTIN
+ 1007, //CURSOR_CROSS
+ 1004, //CURSOR_WAIT
+ 1004, //CURSOR_BUSY
+ 1021, //CURSOR_DRAG
+ 1021, //CURSOR_CAN_DRO
+ 1000, //CURSOR_FORBIDD (no corresponding icon in Android's icon so fallback to default)
+ 1015, //CURSOR_VSIZE
+ 1014, //CURSOR_HSIZE
+ 1017, //CURSOR_BDIAGSI
+ 1016, //CURSOR_FDIAGSI
+ 1020, //CURSOR_MOVE
+ 1015, //CURSOR_VSPLIT
+ 1014, //CURSOR_HSPLIT
+ 1003, //CURSOR_HELP
+ };
+ const int CURSOR_TYPE_NULL = 0;
+ MouseMode mouse_mode = MouseMode::MOUSE_MODE_VISIBLE;
bool keep_screen_on;
@@ -78,6 +100,8 @@ private:
Point2 hover_prev_pos; // needed to calculate the relative position on hover events
Point2 scroll_prev_pos; // needed to calculate the relative position on scroll events
+ CursorShape cursor_shape = CursorShape::CURSOR_ARROW;
+
#if defined(VULKAN_ENABLED)
VulkanContextAndroid *context_vulkan;
RenderingDeviceVulkan *rendering_device_vulkan;
@@ -88,6 +112,7 @@ private:
Callable window_event_callback;
Callable input_event_callback;
Callable input_text_callback;
+ Callable rect_changed_callback;
void _window_callback(const Callable &p_callable, const Variant &p_arg) const;
@@ -95,11 +120,11 @@ private:
void _set_key_modifier_state(Ref<InputEventWithModifiers> ev);
- static int _button_index_from_mask(int button_mask);
+ static MouseButton _button_index_from_mask(MouseButton button_mask);
- static int _android_button_mask_to_godot_button_mask(int android_button_mask);
+ static MouseButton _android_button_mask_to_godot_button_mask(int android_button_mask);
- void _wheel_button_click(int event_buttons_mask, const Ref<InputEventMouseButton> &ev, int wheel_button, float factor);
+ void _wheel_button_click(MouseButton event_buttons_mask, const Ref<InputEventMouseButton> &ev, MouseButton wheel_button, float factor);
public:
static DisplayServerAndroid *get_singleton();
@@ -163,6 +188,8 @@ public:
virtual void window_move_to_foreground(WindowID p_window = MAIN_WINDOW_ID);
virtual bool window_can_draw(WindowID p_window = MAIN_WINDOW_ID) const;
virtual bool can_any_window_draw() const;
+ virtual void window_set_vsync_mode(DisplayServer::VSyncMode p_vsync_mode, WindowID p_window = MAIN_WINDOW_ID);
+ virtual DisplayServer::VSyncMode window_get_vsync_mode(WindowID p_vsync_mode) const;
virtual void alert(const String &p_alert, const String &p_title);
@@ -180,19 +207,23 @@ public:
void process_joy_event(JoypadEvent p_event);
void process_key_event(int p_keycode, int p_scancode, int p_unicode_char, bool p_pressed);
+ virtual void cursor_set_shape(CursorShape p_shape);
+ virtual CursorShape cursor_get_shape() const;
+
void mouse_set_mode(MouseMode p_mode);
MouseMode mouse_get_mode() const;
- static DisplayServer *create_func(const String &p_rendering_driver, WindowMode p_mode, uint32_t p_flags, const Vector2i &p_resolution, Error &r_error);
+ static DisplayServer *create_func(const String &p_rendering_driver, WindowMode p_mode, DisplayServer::VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i &p_resolution, Error &r_error);
static Vector<String> get_rendering_drivers_func();
static void register_android_driver();
void reset_window();
+ void notify_surface_changed(int p_width, int p_height);
virtual Point2i mouse_get_position() const;
- virtual int mouse_get_button_state() const;
+ virtual MouseButton mouse_get_button_state() const;
- DisplayServerAndroid(const String &p_rendering_driver, WindowMode p_mode, uint32_t p_flags, const Vector2i &p_resolution, Error &r_error);
+ DisplayServerAndroid(const String &p_rendering_driver, WindowMode p_mode, DisplayServer::VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i &p_resolution, Error &r_error);
~DisplayServerAndroid();
};
diff --git a/platform/android/export/export.cpp b/platform/android/export/export.cpp
index 326e513261..956b59ce80 100644
--- a/platform/android/export/export.cpp
+++ b/platform/android/export/export.cpp
@@ -31,11 +31,12 @@
#include "export.h"
#include "core/config/project_settings.h"
+#include "core/io/dir_access.h"
+#include "core/io/file_access.h"
#include "core/io/image_loader.h"
+#include "core/io/json.h"
#include "core/io/marshalls.h"
#include "core/io/zip_io.h"
-#include "core/os/dir_access.h"
-#include "core/os/file_access.h"
#include "core/os/os.h"
#include "core/templates/safe_refcount.h"
#include "core/version.h"
@@ -201,16 +202,19 @@ static const char *android_perms[] = {
nullptr
};
-static const char *SPLASH_IMAGE_EXPORT_PATH = "res/drawable/splash.png";
-static const char *SPLASH_BG_COLOR_PATH = "res/drawable/splash_bg_color.png";
+static const char *SPLASH_IMAGE_EXPORT_PATH = "res/drawable-nodpi/splash.png";
+static const char *LEGACY_BUILD_SPLASH_IMAGE_EXPORT_PATH = "res/drawable-nodpi-v4/splash.png";
+static const char *SPLASH_BG_COLOR_PATH = "res/drawable-nodpi/splash_bg_color.png";
+static const char *LEGACY_BUILD_SPLASH_BG_COLOR_PATH = "res/drawable-nodpi-v4/splash_bg_color.png";
static const char *SPLASH_CONFIG_PATH = "res://android/build/res/drawable/splash_drawable.xml";
+static const char *GDNATIVE_LIBS_PATH = "res://android/build/libs/gdnativelibs.json";
const String SPLASH_CONFIG_XML_CONTENT = R"SPLASH(<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@drawable/splash_bg_color" />
<item>
<bitmap
- android:gravity="%s"
+ android:gravity="center"
android:filter="%s"
android:src="@drawable/splash" />
</item>
@@ -275,6 +279,11 @@ class EditorExportPlatformAndroid : public EditorExportPlatform {
EditorProgress *ep = nullptr;
};
+ struct CustomExportData {
+ bool debug;
+ Vector<String> libs;
+ };
+
Vector<PluginConfigAndroid> plugins;
String last_plugin_names;
uint64_t last_custom_build_time = 0;
@@ -607,9 +616,9 @@ class EditorExportPlatformAndroid : public EditorExportPlatform {
zip_fileinfo zipfi;
zipfi.tmz_date.tm_hour = time.hour;
zipfi.tmz_date.tm_mday = date.day;
- zipfi.tmz_date.tm_min = time.min;
+ zipfi.tmz_date.tm_min = time.minute;
zipfi.tmz_date.tm_mon = date.month - 1; // tm_mon is zero indexed
- zipfi.tmz_date.tm_sec = time.sec;
+ zipfi.tmz_date.tm_sec = time.second;
zipfi.tmz_date.tm_year = date.year;
zipfi.dosDate = 0;
zipfi.external_fa = 0;
@@ -755,6 +764,33 @@ class EditorExportPlatformAndroid : public EditorExportPlatform {
return OK;
}
+ static Error copy_gradle_so(void *p_userdata, const SharedObject &p_so) {
+ ERR_FAIL_COND_V_MSG(!p_so.path.get_file().begins_with("lib"), FAILED,
+ "Android .so file names must start with \"lib\", but got: " + p_so.path);
+ Vector<String> abis = get_abis();
+ CustomExportData *export_data = (CustomExportData *)p_userdata;
+ bool exported = false;
+ for (int i = 0; i < p_so.tags.size(); ++i) {
+ int abi_index = abis.find(p_so.tags[i]);
+ if (abi_index != -1) {
+ exported = true;
+ String base = "res://android/build/libs";
+ String type = export_data->debug ? "debug" : "release";
+ String abi = abis[abi_index];
+ String filename = p_so.path.get_file();
+ String dst_path = base.plus_file(type).plus_file(abi).plus_file(filename);
+ Vector<uint8_t> data = FileAccess::get_file_as_array(p_so.path);
+ print_verbose("Copying .so file from " + p_so.path + " to " + dst_path);
+ Error err = store_file_at_path(dst_path, data);
+ ERR_FAIL_COND_V_MSG(err, err, "Failed to copy .so file from " + p_so.path + " to " + dst_path);
+ export_data->libs.push_back(dst_path);
+ }
+ }
+ ERR_FAIL_COND_V_MSG(!exported, FAILED,
+ "Cannot determine ABI for library \"" + p_so.path + "\". One of the supported ABIs must be used as a tag: " + String(" ").join(abis));
+ return OK;
+ }
+
void _get_permissions(const Ref<EditorExportPreset> &p_preset, bool p_give_internet, Vector<String> &r_permissions) {
const char **aperms = android_perms;
while (*aperms) {
@@ -845,7 +881,8 @@ class EditorExportPlatformAndroid : public EditorExportPlatform {
int version_code = p_preset->get("version/code");
String package_name = p_preset->get("package/unique_name");
- const int screen_orientation = _get_android_orientation_value(_get_screen_orientation());
+ const int screen_orientation =
+ _get_android_orientation_value(DisplayServer::ScreenOrientation(int(GLOBAL_GET("display/window/handheld/orientation"))));
bool screen_support_small = p_preset->get("screen/support_small");
bool screen_support_normal = p_preset->get("screen/support_normal");
@@ -853,7 +890,9 @@ class EditorExportPlatformAndroid : public EditorExportPlatform {
bool screen_support_xlarge = p_preset->get("screen/support_xlarge");
int xr_mode_index = p_preset->get("xr_features/xr_mode");
- bool focus_awareness = p_preset->get("xr_features/focus_awareness");
+
+ bool backup_allowed = p_preset->get("user_data_backup/allow");
+ bool classify_as_game = p_preset->get("package/classify_as_game");
Vector<String> perms;
// Write permissions into the perms variable.
@@ -919,7 +958,6 @@ class EditorExportPlatformAndroid : public EditorExportPlatform {
String tname = string_table[name];
uint32_t attrcount = decode_uint32(&p_manifest[iofs + 20]);
iofs += 28;
- bool is_focus_aware_metadata = false;
for (uint32_t i = 0; i < attrcount; i++) {
uint32_t attr_nspace = decode_uint32(&p_manifest[iofs]);
@@ -948,6 +986,14 @@ class EditorExportPlatformAndroid : public EditorExportPlatform {
}
}
+ if (tname == "application" && attrname == "allowBackup") {
+ encode_uint32(backup_allowed, &p_manifest.write[iofs + 16]);
+ }
+
+ if (tname == "application" && attrname == "isGame") {
+ encode_uint32(classify_as_game, &p_manifest.write[iofs + 16]);
+ }
+
if (tname == "instrumentation" && attrname == "targetPackage") {
string_table.write[attr_value] = get_package_name(package_name);
}
@@ -971,28 +1017,6 @@ class EditorExportPlatformAndroid : public EditorExportPlatform {
}
}
- // FIXME: `attr_value != 0xFFFFFFFF` below added as a stopgap measure for GH-32553,
- // but the issue should be debugged further and properly addressed.
- if (tname == "meta-data" && attrname == "name" && value == "xr_mode_metadata_name") {
- // Update the meta-data 'android:name' attribute based on the selected XR mode.
- if (xr_mode_index == 1 /* XRMode.OVR */) {
- string_table.write[attr_value] = "com.samsung.android.vr.application.mode";
- }
- }
-
- if (tname == "meta-data" && attrname == "value" && value == "xr_mode_metadata_value") {
- // Update the meta-data 'android:value' attribute based on the selected XR mode.
- if (xr_mode_index == 1 /* XRMode.OVR */) {
- string_table.write[attr_value] = "vr_only";
- }
- }
-
- if (tname == "meta-data" && attrname == "value" && is_focus_aware_metadata) {
- // Update the focus awareness meta-data value
- encode_uint32(xr_mode_index == /* XRMode.OVR */ 1 && focus_awareness ? 0xFFFFFFFF : 0, &p_manifest.write[iofs + 16]);
- }
-
- is_focus_aware_metadata = tname == "meta-data" && attrname == "name" && value == "com.oculus.vr.focusaware";
iofs += 20;
}
@@ -1008,15 +1032,6 @@ class EditorExportPlatformAndroid : public EditorExportPlatform {
Vector<int> feature_versions;
if (xr_mode_index == 1 /* XRMode.OVR */) {
- // Check for degrees of freedom
- int dof_index = p_preset->get("xr_features/degrees_of_freedom"); // 0: none, 1: 3dof and 6dof, 2: 6dof
-
- if (dof_index > 0) {
- feature_names.push_back("android.hardware.vr.headtracking");
- feature_required_list.push_back(dof_index == 2);
- feature_versions.push_back(1);
- }
-
// Check for hand tracking
int hand_tracking_index = p_preset->get("xr_features/hand_tracking"); // 0: none, 1: optional, 2: required
if (hand_tracking_index > 0) {
@@ -1485,7 +1500,7 @@ class EditorExportPlatformAndroid : public EditorExportPlatform {
String project_splash_path = ProjectSettings::get_singleton()->get("application/boot_splash/image");
if (!project_splash_path.is_empty()) {
- splash_image.instance();
+ splash_image.instantiate();
print_verbose("Loading splash image: " + project_splash_path);
const Error err = ImageLoader::load_image(project_splash_path, splash_image);
if (err) {
@@ -1502,6 +1517,21 @@ class EditorExportPlatformAndroid : public EditorExportPlatform {
splash_image = Ref<Image>(memnew(Image(boot_splash_png)));
}
+ if (scale_splash) {
+ Size2 screen_size = Size2(ProjectSettings::get_singleton()->get("display/window/size/width"), ProjectSettings::get_singleton()->get("display/window/size/height"));
+ int width, height;
+ if (screen_size.width > screen_size.height) {
+ // scale horizontally
+ height = screen_size.height;
+ width = splash_image->get_width() * screen_size.height / splash_image->get_height();
+ } else {
+ // scale vertically
+ width = screen_size.width;
+ height = splash_image->get_height() * screen_size.width / splash_image->get_width();
+ }
+ splash_image->resize(width, height);
+ }
+
// Setup the splash bg color
bool bg_color_valid;
Color bg_color = ProjectSettings::get_singleton()->get("application/boot_splash/bg_color", &bg_color_valid);
@@ -1510,21 +1540,20 @@ class EditorExportPlatformAndroid : public EditorExportPlatform {
}
print_verbose("Creating splash background color image.");
- splash_bg_color_image.instance();
+ splash_bg_color_image.instantiate();
splash_bg_color_image->create(splash_image->get_width(), splash_image->get_height(), false, splash_image->get_format());
splash_bg_color_image->fill(bg_color);
- String gravity = scale_splash ? "fill" : "center";
- String processed_splash_config_xml = vformat(SPLASH_CONFIG_XML_CONTENT, gravity, bool_to_string(apply_filter));
+ String processed_splash_config_xml = vformat(SPLASH_CONFIG_XML_CONTENT, bool_to_string(apply_filter));
return processed_splash_config_xml;
}
void load_icon_refs(const Ref<EditorExportPreset> &p_preset, Ref<Image> &icon, Ref<Image> &foreground, Ref<Image> &background) {
String project_icon_path = ProjectSettings::get_singleton()->get("application/config/icon");
- icon.instance();
- foreground.instance();
- background.instance();
+ icon.instantiate();
+ foreground.instantiate();
+ background.instantiate();
// Regular icon: user selection -> project icon -> default.
String path = static_cast<String>(p_preset->get(launcher_icon_option)).strip_edges();
@@ -1683,6 +1712,7 @@ public:
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "package/unique_name", PROPERTY_HINT_PLACEHOLDER_TEXT, "ext.domain.name"), "org.godotengine.$genname"));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "package/name", PROPERTY_HINT_PLACEHOLDER_TEXT, "Game Name [default if blank]"), ""));
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "package/signed"), true));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "package/classify_as_game"), true));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, launcher_icon_option, PROPERTY_HINT_FILE, "*.png"), ""));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, launcher_adaptive_icon_foreground_option, PROPERTY_HINT_FILE, "*.png"), ""));
@@ -1692,9 +1722,7 @@ public:
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "graphics/opengl_debug"), false));
r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "xr_features/xr_mode", PROPERTY_HINT_ENUM, "Regular,Oculus Mobile VR"), 0));
- r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "xr_features/degrees_of_freedom", PROPERTY_HINT_ENUM, "None,3DOF and 6DOF,6DOF"), 0));
r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "xr_features/hand_tracking", PROPERTY_HINT_ENUM, "None,Optional,Required"), 0));
- r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "xr_features/focus_awareness"), false));
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "screen/immersive_mode"), true));
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "screen/support_small"), true));
@@ -1702,6 +1730,8 @@ public:
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "screen/support_large"), true));
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "screen/support_xlarge"), true));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "user_data_backup/allow"), false));
+
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "command_line/extra_args"), ""));
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "apk_expansion/enable"), false));
@@ -1803,7 +1833,7 @@ public:
p_debug_flags |= DEBUG_FLAG_REMOTE_DEBUG_LOCALHOST;
}
- String tmp_export_path = EditorSettings::get_singleton()->get_cache_dir().plus_file("tmpexport.apk");
+ String tmp_export_path = EditorPaths::get_singleton()->get_cache_dir().plus_file("tmpexport." + uitos(OS::get_singleton()->get_unix_time()) + ".apk");
#define CLEANUP_AND_RETURN(m_err) \
{ \
@@ -1820,6 +1850,7 @@ public:
List<String> args;
int rv;
+ String output;
bool remove_prev = p_preset->get("one_click_deploy/clear_previous_install");
String version_name = p_preset->get("version/name");
@@ -1837,7 +1868,9 @@ public:
args.push_back("uninstall");
args.push_back(get_package_name(package_name));
- err = OS::get_singleton()->execute(adb, args, nullptr, &rv);
+ output.clear();
+ err = OS::get_singleton()->execute(adb, args, &output, &rv, true);
+ print_verbose(output);
}
print_line("Installing to device (please wait...): " + devices[p_device].name);
@@ -1852,9 +1885,11 @@ public:
args.push_back("-r");
args.push_back(tmp_export_path);
- err = OS::get_singleton()->execute(adb, args, nullptr, &rv);
+ output.clear();
+ err = OS::get_singleton()->execute(adb, args, &output, &rv, true);
+ print_verbose(output);
if (err || rv != 0) {
- EditorNode::add_io_error("Could not install to device.");
+ EditorNode::add_io_error("Could not install to device: " + output);
CLEANUP_AND_RETURN(ERR_CANT_CREATE);
}
@@ -1869,7 +1904,9 @@ public:
args.push_back(devices[p_device].id);
args.push_back("reverse");
args.push_back("--remove-all");
- OS::get_singleton()->execute(adb, args, nullptr, &rv);
+ output.clear();
+ OS::get_singleton()->execute(adb, args, &output, &rv, true);
+ print_verbose(output);
if (p_debug_flags & DEBUG_FLAG_REMOTE_DEBUG) {
int dbg_port = EditorSettings::get_singleton()->get("network/debug/remote_port");
@@ -1880,7 +1917,9 @@ public:
args.push_back("tcp:" + itos(dbg_port));
args.push_back("tcp:" + itos(dbg_port));
- OS::get_singleton()->execute(adb, args, nullptr, &rv);
+ output.clear();
+ OS::get_singleton()->execute(adb, args, &output, &rv, true);
+ print_verbose(output);
print_line("Reverse result: " + itos(rv));
}
@@ -1894,7 +1933,9 @@ public:
args.push_back("tcp:" + itos(fs_port));
args.push_back("tcp:" + itos(fs_port));
- err = OS::get_singleton()->execute(adb, args, nullptr, &rv);
+ output.clear();
+ err = OS::get_singleton()->execute(adb, args, &output, &rv, true);
+ print_verbose(output);
print_line("Reverse result2: " + itos(rv));
}
} else {
@@ -1922,7 +1963,9 @@ public:
args.push_back("-n");
args.push_back(get_package_name(package_name) + "/com.godot.game.GodotApp");
- err = OS::get_singleton()->execute(adb, args, nullptr, &rv);
+ output.clear();
+ err = OS::get_singleton()->execute(adb, args, &output, &rv, true);
+ print_verbose(output);
if (err || rv != 0) {
EditorNode::add_io_error("Could not execute on device.");
CLEANUP_AND_RETURN(ERR_CANT_CREATE);
@@ -2021,10 +2064,9 @@ public:
err += template_err;
}
} else {
- r_missing_templates = !exists_export_template("android_source.zip", &err);
-
bool installed_android_build_template = FileAccess::exists("res://android/build/build.gradle");
if (!installed_android_build_template) {
+ r_missing_templates = !exists_export_template("android_source.zip", &err);
err += TTR("Android build template not installed in the project. Install it from the Project menu.") + "\n";
}
@@ -2034,6 +2076,13 @@ public:
// Validate the rest of the configuration.
String dk = p_preset->get("keystore/debug");
+ String dk_user = p_preset->get("keystore/debug_user");
+ String dk_password = p_preset->get("keystore/debug_password");
+
+ if ((dk.is_empty() || dk_user.is_empty() || dk_password.is_empty()) && (!dk.is_empty() || !dk_user.is_empty() || !dk_password.is_empty())) {
+ valid = false;
+ err += TTR("Either Debug Keystore, Debug User AND Debug Password settings must be configured OR none of them.") + "\n";
+ }
if (!FileAccess::exists(dk)) {
dk = EditorSettings::get_singleton()->get("export/android/debug_keystore");
@@ -2044,6 +2093,13 @@ public:
}
String rk = p_preset->get("keystore/release");
+ String rk_user = p_preset->get("keystore/release_user");
+ String rk_password = p_preset->get("keystore/release_password");
+
+ if ((rk.is_empty() || rk_user.is_empty() || rk_password.is_empty()) && (!rk.is_empty() || !rk_user.is_empty() || !rk_password.is_empty())) {
+ valid = false;
+ err += TTR("Either Release Keystore, Release User AND Release Password settings must be configured OR none of them.") + "\n";
+ }
if (!rk.is_empty() && !FileAccess::exists(rk)) {
valid = false;
@@ -2130,27 +2186,13 @@ public:
// Validate the Xr features are properly populated
int xr_mode_index = p_preset->get("xr_features/xr_mode");
- int degrees_of_freedom = p_preset->get("xr_features/degrees_of_freedom");
int hand_tracking = p_preset->get("xr_features/hand_tracking");
- bool focus_awareness = p_preset->get("xr_features/focus_awareness");
if (xr_mode_index != /* XRMode.OVR*/ 1) {
- if (degrees_of_freedom > 0) {
- valid = false;
- err += TTR("\"Degrees Of Freedom\" is only valid when \"Xr Mode\" is \"Oculus Mobile VR\".");
- err += "\n";
- }
-
if (hand_tracking > 0) {
valid = false;
err += TTR("\"Hand Tracking\" is only valid when \"Xr Mode\" is \"Oculus Mobile VR\".");
err += "\n";
}
-
- if (focus_awareness) {
- valid = false;
- err += TTR("\"Focus Awareness\" is only valid when \"Xr Mode\" is \"Oculus Mobile VR\".");
- err += "\n";
- }
}
if (int(p_preset->get("custom_template/export_format")) == EXPORT_FORMAT_AAB &&
@@ -2262,11 +2304,12 @@ public:
CharString command_line_argument = command_line_strings[i].utf8();
int base = r_command_line_flags.size();
int length = command_line_argument.length();
- if (length == 0)
+ if (length == 0) {
continue;
+ }
r_command_line_flags.resize(base + 4 + length);
encode_uint32(length, &r_command_line_flags.write[base]);
- copymem(&r_command_line_flags.write[base + 4], command_line_argument.ptr(), length);
+ memcpy(&r_command_line_flags.write[base + 4], command_line_argument.ptr(), length);
}
}
}
@@ -2318,6 +2361,7 @@ public:
return ERR_FILE_CANT_OPEN;
}
+ String output;
List<String> args;
args.push_back("sign");
args.push_back("--verbose");
@@ -2333,7 +2377,9 @@ public:
print_verbose("Signing debug binary using: " + String("\n") + apksigner + " " + join_list(args, String(" ")));
}
int retval;
- OS::get_singleton()->execute(apksigner, args, nullptr, &retval);
+ output.clear();
+ OS::get_singleton()->execute(apksigner, args, &output, &retval, true);
+ print_verbose(output);
if (retval) {
EditorNode::add_io_error("'apksigner' returned with error #" + itos(retval));
return ERR_CANT_CREATE;
@@ -2351,7 +2397,9 @@ public:
print_verbose("Verifying signed build using: " + String("\n") + apksigner + " " + join_list(args, String(" ")));
}
- OS::get_singleton()->execute(apksigner, args, nullptr, &retval);
+ output.clear();
+ OS::get_singleton()->execute(apksigner, args, &output, &retval, true);
+ print_verbose(output);
if (retval) {
EditorNode::add_io_error("'apksigner' verification of " + export_label + " failed.");
return ERR_CANT_CREATE;
@@ -2371,6 +2419,28 @@ public:
}
}
+ void _remove_copied_libs() {
+ print_verbose("Removing previously installed libraries...");
+ Error error;
+ String libs_json = FileAccess::get_file_as_string(GDNATIVE_LIBS_PATH, &error);
+ if (error || libs_json.is_empty()) {
+ print_verbose("No previously installed libraries found");
+ return;
+ }
+
+ JSON json;
+ error = json.parse(libs_json);
+ ERR_FAIL_COND_MSG(error, "Error parsing \"" + libs_json + "\" on line " + itos(json.get_error_line()) + ": " + json.get_error_message());
+
+ Vector<String> libs = json.get_data();
+ DirAccessRef da = DirAccess::create(DirAccess::ACCESS_RESOURCES);
+ for (int i = 0; i < libs.size(); i++) {
+ print_verbose("Removing previously installed library " + libs[i]);
+ da->remove(libs[i]);
+ }
+ da->remove(GDNATIVE_LIBS_PATH);
+ }
+
String join_list(List<String> parts, const String &separator) const {
String ret;
for (int i = 0; i < parts.size(); ++i) {
@@ -2482,13 +2552,22 @@ public:
//stores all the project files inside the Gradle project directory. Also includes all ABIs
_clear_assets_directory();
+ _remove_copied_libs();
if (!apk_expansion) {
print_verbose("Exporting project files..");
- err = export_project_files(p_preset, rename_and_store_file_in_gradle_project, NULL, ignore_so_file);
+ CustomExportData user_data;
+ user_data.debug = p_debug;
+ err = export_project_files(p_preset, rename_and_store_file_in_gradle_project, &user_data, copy_gradle_so);
if (err != OK) {
EditorNode::add_io_error("Could not export project files to gradle project\n");
return err;
}
+ if (user_data.libs.size() > 0) {
+ FileAccessRef fa = FileAccess::open(GDNATIVE_LIBS_PATH, FileAccess::WRITE);
+ JSON json;
+ fa->store_string(json.stringify(user_data.libs, "\t"));
+ fa->close();
+ }
} else {
print_verbose("Saving apk expansion file..");
err = save_apk_expansion_file(p_preset, p_path);
@@ -2559,19 +2638,35 @@ public:
// Sensitive additions must be done below the logging statement.
print_verbose("Build Android project using gradle command: " + String("\n") + build_command + " " + join_list(cmdline, String(" ")));
- if (should_sign && !p_debug) {
- // Pass the release keystore info as well
- String release_keystore = p_preset->get("keystore/release");
- String release_username = p_preset->get("keystore/release_user");
- String release_password = p_preset->get("keystore/release_password");
- if (!FileAccess::exists(release_keystore)) {
- EditorNode::add_io_error("Could not find keystore, unable to export.");
- return ERR_FILE_CANT_OPEN;
- }
+ if (should_sign) {
+ if (p_debug) {
+ String debug_keystore = p_preset->get("keystore/debug");
+ String debug_password = p_preset->get("keystore/debug_password");
+ String debug_user = p_preset->get("keystore/debug_user");
- cmdline.push_back("-Prelease_keystore_file=" + release_keystore); // argument to specify the release keystore file.
- cmdline.push_back("-Prelease_keystore_alias=" + release_username); // argument to specify the release keystore alias.
- cmdline.push_back("-Prelease_keystore_password=" + release_password); // argument to specity the release keystore password.
+ if (debug_keystore.is_empty()) {
+ debug_keystore = EditorSettings::get_singleton()->get("export/android/debug_keystore");
+ debug_password = EditorSettings::get_singleton()->get("export/android/debug_keystore_pass");
+ debug_user = EditorSettings::get_singleton()->get("export/android/debug_keystore_user");
+ }
+
+ cmdline.push_back("-Pdebug_keystore_file=" + debug_keystore); // argument to specify the debug keystore file.
+ cmdline.push_back("-Pdebug_keystore_alias=" + debug_user); // argument to specify the debug keystore alias.
+ cmdline.push_back("-Pdebug_keystore_password=" + debug_password); // argument to specify the debug keystore password.
+ } else {
+ // Pass the release keystore info as well
+ String release_keystore = p_preset->get("keystore/release");
+ String release_username = p_preset->get("keystore/release_user");
+ String release_password = p_preset->get("keystore/release_password");
+ if (!FileAccess::exists(release_keystore)) {
+ EditorNode::add_io_error("Could not find keystore, unable to export.");
+ return ERR_FILE_CANT_OPEN;
+ }
+
+ cmdline.push_back("-Prelease_keystore_file=" + release_keystore); // argument to specify the release keystore file.
+ cmdline.push_back("-Prelease_keystore_alias=" + release_username); // argument to specify the release keystore alias.
+ cmdline.push_back("-Prelease_keystore_password=" + release_password); // argument to specify the release keystore password.
+ }
}
int result = EditorNode::get_singleton()->execute_and_show_output(TTR("Building Android Project (gradle)"), build_command, cmdline);
@@ -2615,10 +2710,11 @@ public:
}
// This is the start of the Legacy build system
print_verbose("Starting legacy build system..");
- if (p_debug)
+ if (p_debug) {
src_apk = p_preset->get("custom_template/debug");
- else
+ } else {
src_apk = p_preset->get("custom_template/release");
+ }
src_apk = src_apk.strip_edges();
if (src_apk == "") {
if (p_debug) {
@@ -2655,7 +2751,7 @@ public:
FileAccess *dst_f = nullptr;
io2.opaque = &dst_f;
- String tmp_unaligned_path = EditorSettings::get_singleton()->get_cache_dir().plus_file("tmpexport-unaligned.apk");
+ String tmp_unaligned_path = EditorPaths::get_singleton()->get_cache_dir().plus_file("tmpexport-unaligned." + uitos(OS::get_singleton()->get_unix_time()) + ".apk");
#define CLEANUP_AND_RETURN(m_err) \
{ \
@@ -2700,12 +2796,12 @@ public:
}
// Process the splash image
- if (file == SPLASH_IMAGE_EXPORT_PATH && splash_image.is_valid() && !splash_image->is_empty()) {
+ if ((file == SPLASH_IMAGE_EXPORT_PATH || file == LEGACY_BUILD_SPLASH_IMAGE_EXPORT_PATH) && splash_image.is_valid() && !splash_image->is_empty()) {
_load_image_data(splash_image, data);
}
// Process the splash bg color image
- if (file == SPLASH_BG_COLOR_PATH && splash_bg_color_image.is_valid() && !splash_bg_color_image->is_empty()) {
+ if ((file == SPLASH_BG_COLOR_PATH || file == LEGACY_BUILD_SPLASH_BG_COLOR_PATH) && splash_bg_color_image.is_valid() && !splash_bg_color_image->is_empty()) {
_load_image_data(splash_bg_color_image, data);
}
@@ -2813,11 +2909,11 @@ public:
zipOpenNewFileInZip(unaligned_apk,
"assets/_cl_",
&zipfi,
- NULL,
+ nullptr,
0,
- NULL,
+ nullptr,
0,
- NULL,
+ nullptr,
0, // No compress (little size gain and potentially slower startup)
Z_DEFAULT_COMPRESSION);
zipWriteInFileInZip(unaligned_apk, command_line_flags.ptr(), command_line_flags.size());
@@ -2931,11 +3027,11 @@ public:
EditorExportPlatformAndroid() {
Ref<Image> img = memnew(Image(_android_logo));
- logo.instance();
+ logo.instantiate();
logo->create_from_image(img);
img = Ref<Image>(memnew(Image(_android_run_icon)));
- run_icon.instance();
+ run_icon.instantiate();
run_icon->create_from_image(img);
devices_changed.set();
diff --git a/platform/android/export/gradle_export_util.h b/platform/android/export/gradle_export_util.h
index 097a2391ee..52a7e4c5cf 100644
--- a/platform/android/export/gradle_export_util.h
+++ b/platform/android/export/gradle_export_util.h
@@ -31,9 +31,9 @@
#ifndef GODOT_GRADLE_EXPORT_UTIL_H
#define GODOT_GRADLE_EXPORT_UTIL_H
+#include "core/io/dir_access.h"
+#include "core/io/file_access.h"
#include "core/io/zip_io.h"
-#include "core/os/dir_access.h"
-#include "core/os/file_access.h"
#include "core/os/os.h"
#include "editor/editor_export.h"
@@ -44,27 +44,6 @@ const String godot_project_name_xml_string = R"(<?xml version="1.0" encoding="ut
</resources>
)";
-DisplayServer::ScreenOrientation _get_screen_orientation() {
- String orientation_settings = ProjectSettings::get_singleton()->get("display/window/handheld/orientation");
- DisplayServer::ScreenOrientation screen_orientation;
- if (orientation_settings == "portrait")
- screen_orientation = DisplayServer::SCREEN_PORTRAIT;
- else if (orientation_settings == "reverse_landscape")
- screen_orientation = DisplayServer::SCREEN_REVERSE_LANDSCAPE;
- else if (orientation_settings == "reverse_portrait")
- screen_orientation = DisplayServer::SCREEN_REVERSE_PORTRAIT;
- else if (orientation_settings == "sensor_landscape")
- screen_orientation = DisplayServer::SCREEN_SENSOR_LANDSCAPE;
- else if (orientation_settings == "sensor_portrait")
- screen_orientation = DisplayServer::SCREEN_SENSOR_PORTRAIT;
- else if (orientation_settings == "sensor")
- screen_orientation = DisplayServer::SCREEN_SENSOR;
- else
- screen_orientation = DisplayServer::SCREEN_LANDSCAPE;
-
- return screen_orientation;
-}
-
int _get_android_orientation_value(DisplayServer::ScreenOrientation screen_orientation) {
switch (screen_orientation) {
case DisplayServer::SCREEN_PORTRAIT:
@@ -117,14 +96,6 @@ Error create_directory(const String &p_dir) {
return OK;
}
-// Implementation of EditorExportSaveSharedObject.
-// This method will only be called as an input to export_project_files.
-// This method lets the .so files for all ABIs to be copied
-// into the gradle project from the .AAR file
-Error ignore_so_file(void *p_userdata, const SharedObject &p_so) {
- return OK;
-}
-
// Writes p_data into a file at p_path, creating directories if necessary.
// Note: this will overwrite the file at p_path if it already exists.
Error store_file_at_path(const String &p_path, const Vector<uint8_t> &p_data) {
@@ -240,12 +211,6 @@ String _get_xr_features_tag(const Ref<EditorExportPreset> &p_preset) {
String manifest_xr_features;
bool uses_xr = (int)(p_preset->get("xr_features/xr_mode")) == 1;
if (uses_xr) {
- int dof_index = p_preset->get("xr_features/degrees_of_freedom"); // 0: none, 1: 3dof and 6dof, 2: 6dof
- if (dof_index == 1) {
- manifest_xr_features += " <uses-feature tools:node=\"replace\" android:name=\"android.hardware.vr.headtracking\" android:required=\"false\" android:version=\"1\" />\n";
- } else if (dof_index == 2) {
- manifest_xr_features += " <uses-feature tools:node=\"replace\" android:name=\"android.hardware.vr.headtracking\" android:required=\"true\" android:version=\"1\" />\n";
- }
int hand_tracking_index = p_preset->get("xr_features/hand_tracking"); // 0: none, 1: optional, 2: required
if (hand_tracking_index == 1) {
manifest_xr_features += " <uses-feature tools:node=\"replace\" android:name=\"oculus.software.handtracking\" android:required=\"false\" />\n";
@@ -271,16 +236,13 @@ String _get_instrumentation_tag(const Ref<EditorExportPreset> &p_preset) {
String _get_activity_tag(const Ref<EditorExportPreset> &p_preset) {
bool uses_xr = (int)(p_preset->get("xr_features/xr_mode")) == 1;
- String orientation = _get_android_orientation_label(_get_screen_orientation());
+ String orientation = _get_android_orientation_label(DisplayServer::ScreenOrientation(int(GLOBAL_GET("display/window/handheld/orientation"))));
String manifest_activity_text = vformat(
" <activity android:name=\"com.godot.game.GodotApp\" "
"tools:replace=\"android:screenOrientation\" "
"android:screenOrientation=\"%s\">\n",
orientation);
- if (uses_xr) {
- String focus_awareness = bool_to_string(p_preset->get("xr_features/focus_awareness"));
- manifest_activity_text += vformat(" <meta-data tools:node=\"replace\" android:name=\"com.oculus.vr.focusaware\" android:value=\"%s\" />\n", focus_awareness);
- } else {
+ if (!uses_xr) {
manifest_activity_text += " <meta-data tools:node=\"remove\" android:name=\"com.oculus.vr.focusaware\" />\n";
}
manifest_activity_text += " </activity>\n";
@@ -288,16 +250,16 @@ String _get_activity_tag(const Ref<EditorExportPreset> &p_preset) {
}
String _get_application_tag(const Ref<EditorExportPreset> &p_preset) {
- bool uses_xr = (int)(p_preset->get("xr_features/xr_mode")) == 1;
- String manifest_application_text =
+ String manifest_application_text = vformat(
" <application android:label=\"@string/godot_project_name_string\"\n"
- " android:allowBackup=\"false\" tools:ignore=\"GoogleAppIndexingWarning\"\n"
- " android:icon=\"@mipmap/icon\">\n\n"
- " <meta-data tools:node=\"remove\" android:name=\"xr_mode_metadata_name\" />\n";
+ " android:allowBackup=\"%s\"\n"
+ " android:icon=\"@mipmap/icon\"\n"
+ " android:isGame=\"%s\"\n"
+ " tools:replace=\"android:allowBackup,android:isGame\"\n"
+ " tools:ignore=\"GoogleAppIndexingWarning\">\n\n",
+ bool_to_string(p_preset->get("user_data_backup/allow")),
+ bool_to_string(p_preset->get("package/classify_as_game")));
- if (uses_xr) {
- manifest_application_text += " <meta-data tools:node=\"replace\" android:name=\"com.samsung.android.vr.application.mode\" android:value=\"vr_only\" />\n";
- }
manifest_application_text += _get_activity_tag(p_preset);
manifest_application_text += " </application>\n";
return manifest_application_text;
diff --git a/platform/android/file_access_android.cpp b/platform/android/file_access_android.cpp
index 165d5da3ae..90370878b7 100644
--- a/platform/android/file_access_android.cpp
+++ b/platform/android/file_access_android.cpp
@@ -71,8 +71,9 @@ bool FileAccessAndroid::is_open() const {
return a != nullptr;
}
-void FileAccessAndroid::seek(size_t p_position) {
+void FileAccessAndroid::seek(uint64_t p_position) {
ERR_FAIL_COND(!a);
+
AAsset_seek(a, p_position, SEEK_SET);
pos = p_position;
if (pos > len) {
@@ -89,11 +90,11 @@ void FileAccessAndroid::seek_end(int64_t p_position) {
pos = len + p_position;
}
-size_t FileAccessAndroid::get_position() const {
+uint64_t FileAccessAndroid::get_position() const {
return pos;
}
-size_t FileAccessAndroid::get_len() const {
+uint64_t FileAccessAndroid::get_length() const {
return len;
}
@@ -113,8 +114,10 @@ uint8_t FileAccessAndroid::get_8() const {
return byte;
}
-int FileAccessAndroid::get_buffer(uint8_t *p_dst, int p_length) const {
- off_t r = AAsset_read(a, p_dst, p_length);
+uint64_t FileAccessAndroid::get_buffer(uint8_t *p_dst, uint64_t p_length) const {
+ ERR_FAIL_COND_V(!p_dst && p_length > 0, -1);
+
+ int r = AAsset_read(a, p_dst, p_length);
if (pos + p_length > len) {
eof = true;
diff --git a/platform/android/file_access_android.h b/platform/android/file_access_android.h
index 56010c918a..bb4ce36947 100644
--- a/platform/android/file_access_android.h
+++ b/platform/android/file_access_android.h
@@ -31,7 +31,7 @@
#ifndef FILE_ACCESS_ANDROID_H
#define FILE_ACCESS_ANDROID_H
-#include "core/os/file_access.h"
+#include "core/io/file_access.h"
#include <android/asset_manager.h>
#include <android/log.h>
#include <stdio.h>
@@ -40,8 +40,8 @@
class FileAccessAndroid : public FileAccess {
static FileAccess *create_android();
mutable AAsset *a = nullptr;
- mutable size_t len = 0;
- mutable size_t pos = 0;
+ mutable uint64_t len = 0;
+ mutable uint64_t pos = 0;
mutable bool eof = false;
public:
@@ -51,15 +51,15 @@ public:
virtual void close(); ///< close a file
virtual bool is_open() const; ///< true when file is open
- virtual void seek(size_t p_position); ///< seek to a given position
+ virtual void seek(uint64_t p_position); ///< seek to a given position
virtual void seek_end(int64_t p_position = 0); ///< seek from the end of file
- virtual size_t get_position() const; ///< get position in the file
- virtual size_t get_len() const; ///< get size of the file
+ virtual uint64_t get_position() const; ///< get position in the file
+ virtual uint64_t get_length() const; ///< get size of the file
virtual bool eof_reached() const; ///< reading passed EOF
virtual uint8_t get_8() const; ///< get a byte
- virtual int get_buffer(uint8_t *p_dst, int p_length) const;
+ virtual uint64_t get_buffer(uint8_t *p_dst, uint64_t p_length) const;
virtual Error get_error() const; ///< get last error
diff --git a/platform/android/java/app/AndroidManifest.xml b/platform/android/java/app/AndroidManifest.xml
index 948fa8c00b..0874d77645 100644
--- a/platform/android/java/app/AndroidManifest.xml
+++ b/platform/android/java/app/AndroidManifest.xml
@@ -19,8 +19,9 @@
<application
android:label="@string/godot_project_name_string"
android:allowBackup="false"
- tools:ignore="GoogleAppIndexingWarning"
- android:icon="@mipmap/icon" >
+ android:icon="@mipmap/icon"
+ android:isGame="true"
+ tools:ignore="GoogleAppIndexingWarning" >
<!-- Records the version of the Godot editor used for building -->
<meta-data
@@ -30,11 +31,6 @@
<!-- The following metadata values are replaced when Godot exports, modifying them here has no effect. -->
<!-- Do these changes in the export preset. Adding new ones is fine. -->
- <!-- XR mode metadata. This is modified by the exporter based on the selected xr mode. DO NOT CHANGE the values here. -->
- <meta-data
- android:name="xr_mode_metadata_name"
- android:value="xr_mode_metadata_value" />
-
<activity
android:name=".GodotApp"
android:label="@string/godot_project_name_string"
@@ -45,8 +41,8 @@
android:resizeableActivity="false"
tools:ignore="UnusedAttribute" >
- <!-- Focus awareness metadata is updated at export time if the user enables it in the 'Xr Features' section. -->
- <meta-data android:name="com.oculus.vr.focusaware" android:value="false" />
+ <!-- Focus awareness metadata is removed at export time if the xr mode is not VR. -->
+ <meta-data android:name="com.oculus.vr.focusaware" android:value="true" />
<intent-filter>
<action android:name="android.intent.action.MAIN" />
diff --git a/platform/android/java/app/assets/.gitignore b/platform/android/java/app/assets/.gitignore
new file mode 100644
index 0000000000..d6b7ef32c8
--- /dev/null
+++ b/platform/android/java/app/assets/.gitignore
@@ -0,0 +1,2 @@
+*
+!.gitignore
diff --git a/platform/android/java/app/build.gradle b/platform/android/java/app/build.gradle
index 934c4bf441..18e07c3762 100644
--- a/platform/android/java/app/build.gradle
+++ b/platform/android/java/app/build.gradle
@@ -6,7 +6,7 @@ buildscript {
repositories {
google()
- jcenter()
+ mavenCentral()
}
dependencies {
classpath libraries.androidGradlePlugin
@@ -18,9 +18,8 @@ apply plugin: 'com.android.application'
allprojects {
repositories {
- mavenCentral()
google()
- jcenter()
+ mavenCentral()
// Godot user plugins custom maven repos
String[] mavenRepos = getGodotPluginsMavenRepos()
@@ -77,7 +76,7 @@ android {
defaultConfig {
// The default ignore pattern for the 'assets' directory includes hidden files and directories which are used by Godot projects.
aaptOptions {
- ignoreAssetsPattern "!.svn:!.git:!.ds_store:!*.scc:<dir>_*:!CVS:!thumbs.db:!picasa.ini:!*~"
+ ignoreAssetsPattern "!.svn:!.git:!.gitignore:!.ds_store:!*.scc:<dir>_*:!CVS:!thumbs.db:!picasa.ini:!*~"
}
ndk {
@@ -106,11 +105,22 @@ android {
exclude 'META-INF/LICENSE'
exclude 'META-INF/NOTICE'
- // Should be uncommented for development purpose within Android Studio
- // doNotStrip '**/*.so'
+ // 'doNotStrip' is enabled for development within Android Studio
+ if (shouldNotStrip()) {
+ doNotStrip '**/*.so'
+ }
}
signingConfigs {
+ debug {
+ if (hasCustomDebugKeystore()) {
+ storeFile new File(getDebugKeystoreFile())
+ storePassword getDebugKeystorePassword()
+ keyAlias getDebugKeyAlias()
+ keyPassword getDebugKeystorePassword()
+ }
+ }
+
release {
File keystoreFile = new File(getReleaseKeystoreFile())
if (keystoreFile.isFile()) {
@@ -155,7 +165,7 @@ android {
aidl.srcDirs = ['aidl']
assets.srcDirs = ['assets']
}
- debug.jniLibs.srcDirs = ['libs/debug']
+ debug.jniLibs.srcDirs = ['libs/debug', 'libs/debug/vulkan_validation_layers']
release.jniLibs.srcDirs = ['libs/release']
}
diff --git a/platform/android/java/app/config.gradle b/platform/android/java/app/config.gradle
index 585e517631..81fc87b7ef 100644
--- a/platform/android/java/app/config.gradle
+++ b/platform/android/java/app/config.gradle
@@ -1,11 +1,11 @@
ext.versions = [
- androidGradlePlugin: '4.0.1',
+ androidGradlePlugin: '4.2.1',
compileSdk : 29,
minSdk : 18,
targetSdk : 29,
- buildTools : '30.0.1',
+ buildTools : '30.0.3',
supportCoreUtils : '1.0.0',
- kotlinVersion : '1.4.10',
+ kotlinVersion : '1.5.10',
v4Support : '1.0.0',
javaVersion : 1.8,
ndkVersion : '21.4.7075529' // Also update 'platform/android/detect.py#get_project_ndk_version()' when this is updated.
@@ -191,6 +191,35 @@ ext.getGodotPluginsLocalBinaries = { ->
return binDeps
}
+ext.getDebugKeystoreFile = { ->
+ String keystoreFile = project.hasProperty("debug_keystore_file") ? project.property("debug_keystore_file") : ""
+ if (keystoreFile == null || keystoreFile.isEmpty()) {
+ keystoreFile = "."
+ }
+ return keystoreFile
+}
+
+ext.hasCustomDebugKeystore = { ->
+ File keystoreFile = new File(getDebugKeystoreFile())
+ return keystoreFile.isFile()
+}
+
+ext.getDebugKeystorePassword = { ->
+ String keystorePassword = project.hasProperty("debug_keystore_password") ? project.property("debug_keystore_password") : ""
+ if (keystorePassword == null || keystorePassword.isEmpty()) {
+ keystorePassword = "android"
+ }
+ return keystorePassword
+}
+
+ext.getDebugKeyAlias = { ->
+ String keyAlias = project.hasProperty("debug_keystore_alias") ? project.property("debug_keystore_alias") : ""
+ if (keyAlias == null || keyAlias.isEmpty()) {
+ keyAlias = "androiddebugkey"
+ }
+ return keyAlias
+}
+
ext.getReleaseKeystoreFile = { ->
String keystoreFile = project.hasProperty("release_keystore_file") ? project.property("release_keystore_file") : ""
if (keystoreFile == null || keystoreFile.isEmpty()) {
@@ -209,10 +238,19 @@ ext.getReleaseKeyAlias = { ->
return keyAlias
}
+ext.isAndroidStudio = { ->
+ def sysProps = System.getProperties()
+ return sysProps != null && sysProps['idea.platform.prefix'] != null
+}
+
ext.shouldZipAlign = { ->
String zipAlignFlag = project.hasProperty("perform_zipalign") ? project.property("perform_zipalign") : ""
if (zipAlignFlag == null || zipAlignFlag.isEmpty()) {
- zipAlignFlag = "false"
+ if (isAndroidStudio()) {
+ zipAlignFlag = "true"
+ } else {
+ zipAlignFlag = "false"
+ }
}
return Boolean.parseBoolean(zipAlignFlag)
}
@@ -220,7 +258,15 @@ ext.shouldZipAlign = { ->
ext.shouldSign = { ->
String signFlag = project.hasProperty("perform_signing") ? project.property("perform_signing") : ""
if (signFlag == null || signFlag.isEmpty()) {
- signFlag = "false"
+ if (isAndroidStudio()) {
+ signFlag = "true"
+ } else {
+ signFlag = "false"
+ }
}
return Boolean.parseBoolean(signFlag)
}
+
+ext.shouldNotStrip = { ->
+ return isAndroidStudio() || project.hasProperty("doNotStrip")
+}
diff --git a/platform/android/java/app/res/drawable/splash.png b/platform/android/java/app/res/drawable-nodpi/splash.png
index 7bddd4325a..7bddd4325a 100644
--- a/platform/android/java/app/res/drawable/splash.png
+++ b/platform/android/java/app/res/drawable-nodpi/splash.png
Binary files differ
diff --git a/platform/android/java/app/res/drawable/splash_bg_color.png b/platform/android/java/app/res/drawable-nodpi/splash_bg_color.png
index 004b6fd508..004b6fd508 100644
--- a/platform/android/java/app/res/drawable/splash_bg_color.png
+++ b/platform/android/java/app/res/drawable-nodpi/splash_bg_color.png
Binary files differ
diff --git a/platform/android/java/build.gradle b/platform/android/java/build.gradle
index ec02b0fc7a..ee24a46d9f 100644
--- a/platform/android/java/build.gradle
+++ b/platform/android/java/build.gradle
@@ -5,7 +5,7 @@ buildscript {
repositories {
google()
- jcenter()
+ mavenCentral()
}
dependencies {
classpath libraries.androidGradlePlugin
@@ -16,7 +16,6 @@ buildscript {
allprojects {
repositories {
google()
- jcenter()
mavenCentral()
}
}
@@ -112,26 +111,27 @@ task copyReleaseAARToBin(type: Copy) {
* The zip file also includes some gradle tools to allow building of the custom build.
*/
task zipCustomBuild(type: Zip) {
- dependsOn ':generateGodotTemplates'
+ onlyIf { generateGodotTemplates.state.executed || generateDevTemplate.state.executed }
doFirst {
logger.lifecycle("Generating Godot custom build template")
}
from(fileTree(dir: 'app', excludes: ['**/build/**', '**/.gradle/**', '**/*.iml']), fileTree(dir: '.', includes: ['gradle.properties', 'gradlew', 'gradlew.bat', 'gradle/**']))
include '**/*'
- archiveName 'android_source.zip'
- destinationDir(file(binDir))
+ archiveFileName = 'android_source.zip'
+ destinationDirectory = file(binDir)
}
-/**
- * Master task used to coordinate the tasks defined above to generate the set of Godot templates.
- */
-task generateGodotTemplates(type: GradleBuild) {
+def templateExcludedBuildTask() {
// We exclude these gradle tasks so we can run the scons command manually.
+ def excludedTasks = []
for (String buildType : supportedTargets) {
- startParameter.excludedTaskNames += ":lib:" + getSconsTaskName(buildType)
+ excludedTasks += ":lib:" + getSconsTaskName(buildType)
}
+ return excludedTasks
+}
- tasks = []
+def templateBuildTasks() {
+ def tasks = []
// Only build the apks and aar files for which we have native shared libraries.
for (String target : supportedTargets) {
@@ -152,6 +152,29 @@ task generateGodotTemplates(type: GradleBuild) {
}
}
+ return tasks
+}
+
+/**
+ * Master task used to coordinate the tasks defined above to generate the set of Godot templates.
+ */
+task generateGodotTemplates(type: GradleBuild) {
+ startParameter.excludedTaskNames = templateExcludedBuildTask()
+ tasks = templateBuildTasks()
+
+ finalizedBy 'zipCustomBuild'
+}
+
+/**
+ * Generates the same output as generateGodotTemplates but with dev symbols
+ */
+task generateDevTemplate (type: GradleBuild) {
+ // add parameter to set symbols to true
+ startParameter.projectProperties += [doNotStrip: true]
+
+ startParameter.excludedTaskNames = templateExcludedBuildTask()
+ tasks = templateBuildTasks()
+
finalizedBy 'zipCustomBuild'
}
diff --git a/platform/android/java/gradle.properties b/platform/android/java/gradle.properties
index 2dc069ad2f..6b3b62a9da 100644
--- a/platform/android/java/gradle.properties
+++ b/platform/android/java/gradle.properties
@@ -12,7 +12,7 @@ android.useAndroidX=true
# Specifies the JVM arguments used for the daemon process.
# The setting is particularly useful for tweaking memory settings.
-org.gradle.jvmargs=-Xmx1536m
+org.gradle.jvmargs=-Xmx4536m
# When configured, Gradle will run in incubating parallel mode.
# This option should only be used with decoupled projects. More details, visit
diff --git a/platform/android/java/gradle/wrapper/gradle-wrapper.properties b/platform/android/java/gradle/wrapper/gradle-wrapper.properties
index a7d8a0f310..74c5636f8a 100644
--- a/platform/android/java/gradle/wrapper/gradle-wrapper.properties
+++ b/platform/android/java/gradle/wrapper/gradle-wrapper.properties
@@ -1,6 +1,6 @@
-#Mon Sep 02 02:44:30 PDT 2019
+#Wed Jun 23 23:42:22 PDT 2021
distributionBase=GRADLE_USER_HOME
+distributionUrl=https\://services.gradle.org/distributions/gradle-6.7.1-all.zip
distributionPath=wrapper/dists
-zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-6.5-all.zip
+zipStoreBase=GRADLE_USER_HOME
diff --git a/platform/android/java/gradlew.bat b/platform/android/java/gradlew.bat
index f9553162f1..11cc30edb0 100644
--- a/platform/android/java/gradlew.bat
+++ b/platform/android/java/gradlew.bat
@@ -1,7 +1,7 @@
@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
-@rem Gradle startup script for Windows
+@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@@ -75,7 +75,7 @@ if "%ERRORLEVEL%"=="0" goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
-if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
exit /b 1
:mainEnd
diff --git a/platform/android/java/lib/build.gradle b/platform/android/java/lib/build.gradle
index 6fc9a11a08..663ba73d40 100644
--- a/platform/android/java/lib/build.gradle
+++ b/platform/android/java/lib/build.gradle
@@ -36,8 +36,10 @@ android {
exclude 'META-INF/LICENSE'
exclude 'META-INF/NOTICE'
- // Should be uncommented for development purpose within Android Studio
- // doNotStrip '**/*.so'
+ // 'doNotStrip' is enabled for development within Android Studio
+ if (shouldNotStrip()) {
+ doNotStrip '**/*.so'
+ }
}
sourceSets {
diff --git a/platform/android/java/lib/res/values/strings.xml b/platform/android/java/lib/res/values/strings.xml
index 590b066d8a..010006b81e 100644
--- a/platform/android/java/lib/res/values/strings.xml
+++ b/platform/android/java/lib/res/values/strings.xml
@@ -6,7 +6,7 @@
<string name="text_button_resume_cellular">Resume download</string>
<string name="text_button_wifi_settings">Wi-Fi settings</string>
<string name="text_verifying_download">Verifying Download</string>
- <string name="text_validation_complete">XAPK File Validation Complete. Select OK to exit.</string>
+ <string name="text_validation_complete">XAPK File Validation Complete. Select OK to exit.</string>
<string name="text_validation_failed">XAPK File Validation Failed.</string>
<string name="text_button_pause">Pause Download</string>
<string name="text_button_resume">Resume Download</string>
diff --git a/platform/android/java/lib/src/org/godotengine/godot/Dictionary.java b/platform/android/java/lib/src/org/godotengine/godot/Dictionary.java
index 0572cf3589..b12844702a 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/Dictionary.java
+++ b/platform/android/java/lib/src/org/godotengine/godot/Dictionary.java
@@ -43,10 +43,10 @@ public class Dictionary extends HashMap<String, Object> {
for (String key : keys) {
ret[i] = key;
i++;
- };
+ }
return ret;
- };
+ }
public Object[] get_values() {
Object[] ret = new Object[size()];
@@ -55,21 +55,21 @@ public class Dictionary extends HashMap<String, Object> {
for (String key : keys) {
ret[i] = get(key);
i++;
- };
+ }
return ret;
- };
+ }
public void set_keys(String[] keys) {
keys_cache = keys;
- };
+ }
public void set_values(Object[] vals) {
int i = 0;
for (String key : keys_cache) {
put(key, vals[i]);
i++;
- };
+ }
keys_cache = null;
- };
-};
+ }
+}
diff --git a/platform/android/java/lib/src/org/godotengine/godot/FullScreenGodotApp.java b/platform/android/java/lib/src/org/godotengine/godot/FullScreenGodotApp.java
index 4e67402c63..89497d1526 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/FullScreenGodotApp.java
+++ b/platform/android/java/lib/src/org/godotengine/godot/FullScreenGodotApp.java
@@ -34,6 +34,7 @@ import android.content.Intent;
import android.os.Bundle;
import android.view.KeyEvent;
+import androidx.annotation.CallSuper;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.FragmentActivity;
@@ -44,7 +45,7 @@ import androidx.fragment.app.FragmentActivity;
* It's also a reference implementation for how to setup and use the {@link Godot} fragment
* within an Android app.
*/
-public abstract class FullScreenGodotApp extends FragmentActivity {
+public abstract class FullScreenGodotApp extends FragmentActivity implements GodotHost {
@Nullable
private Godot godotFragment;
@@ -62,26 +63,37 @@ public abstract class FullScreenGodotApp extends FragmentActivity {
@Override
public void onNewIntent(Intent intent) {
+ super.onNewIntent(intent);
if (godotFragment != null) {
godotFragment.onNewIntent(intent);
}
}
+ @CallSuper
@Override
- public void onBackPressed() {
+ public void onActivityResult(int requestCode, int resultCode, Intent data) {
+ super.onActivityResult(requestCode, resultCode, data);
if (godotFragment != null) {
- godotFragment.onBackPressed();
- } else {
- super.onBackPressed();
+ godotFragment.onActivityResult(requestCode, resultCode, data);
+ }
+ }
+
+ @CallSuper
+ @Override
+ public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
+ super.onRequestPermissionsResult(requestCode, permissions, grantResults);
+ if (godotFragment != null) {
+ godotFragment.onRequestPermissionsResult(requestCode, permissions, grantResults);
}
}
@Override
- public boolean onKeyMultiple(final int inKeyCode, int repeatCount, KeyEvent event) {
- if (godotFragment != null && godotFragment.onKeyMultiple(inKeyCode, repeatCount, event)) {
- return true;
+ public void onBackPressed() {
+ if (godotFragment != null) {
+ godotFragment.onBackPressed();
+ } else {
+ super.onBackPressed();
}
- return super.onKeyMultiple(inKeyCode, repeatCount, event);
}
/**
diff --git a/platform/android/java/lib/src/org/godotengine/godot/Godot.java b/platform/android/java/lib/src/org/godotengine/godot/Godot.java
index 0891904dff..8ffa4a9249 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/Godot.java
+++ b/platform/android/java/lib/src/org/godotengine/godot/Godot.java
@@ -70,10 +70,8 @@ import android.os.VibrationEffect;
import android.os.Vibrator;
import android.provider.Settings.Secure;
import android.view.Display;
-import android.view.InputDevice;
import android.view.KeyEvent;
import android.view.LayoutInflater;
-import android.view.MotionEvent;
import android.view.Surface;
import android.view.View;
import android.view.ViewGroup;
@@ -103,6 +101,7 @@ import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.security.MessageDigest;
+import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
import java.util.Locale;
@@ -131,6 +130,7 @@ public class Godot extends Fragment implements SensorEventListener, IDownloaderC
private boolean activityResumed;
private int mState;
+ private GodotHost godotHost;
private GodotPluginRegistry pluginRegistry;
static private Intent mCurrentIntent;
@@ -173,12 +173,30 @@ public class Godot extends Fragment implements SensorEventListener, IDownloaderC
public static GodotNetUtils netUtils;
public interface ResultCallback {
- public void callback(int requestCode, int resultCode, Intent data);
+ void callback(int requestCode, int resultCode, Intent data);
}
public ResultCallback result_callback;
@Override
+ public void onAttach(Context context) {
+ super.onAttach(context);
+ if (getParentFragment() instanceof GodotHost) {
+ godotHost = (GodotHost)getParentFragment();
+ } else if (getActivity() instanceof GodotHost) {
+ godotHost = (GodotHost)getActivity();
+ }
+ }
+
+ @Override
+ public void onDetach() {
+ super.onDetach();
+ godotHost = null;
+ }
+
+ @CallSuper
+ @Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
+ super.onActivityResult(requestCode, resultCode, data);
if (result_callback != null) {
result_callback.callback(requestCode, resultCode, data);
result_callback = null;
@@ -189,8 +207,10 @@ public class Godot extends Fragment implements SensorEventListener, IDownloaderC
}
}
+ @CallSuper
@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
+ super.onRequestPermissionsResult(requestCode, permissions, grantResults);
for (GodotPlugin plugin : pluginRegistry.getAllPlugins()) {
plugin.onMainRequestPermissionsResult(requestCode, permissions, grantResults);
}
@@ -198,7 +218,21 @@ public class Godot extends Fragment implements SensorEventListener, IDownloaderC
for (int i = 0; i < permissions.length; i++) {
GodotLib.requestPermissionResult(permissions[i], grantResults[i] == PackageManager.PERMISSION_GRANTED);
}
- };
+ }
+
+ /**
+ * Invoked on the render thread when the Godot setup is complete.
+ */
+ @CallSuper
+ protected void onGodotSetupCompleted() {
+ for (GodotPlugin plugin : pluginRegistry.getAllPlugins()) {
+ plugin.onGodotSetupCompleted();
+ }
+
+ if (godotHost != null) {
+ godotHost.onGodotSetupCompleted();
+ }
+ }
/**
* Invoked on the render thread when the Godot main loop has started.
@@ -208,6 +242,10 @@ public class Godot extends Fragment implements SensorEventListener, IDownloaderC
for (GodotPlugin plugin : pluginRegistry.getAllPlugins()) {
plugin.onGodotMainLoopStarted();
}
+
+ if (godotHost != null) {
+ godotHost.onGodotMainLoopStarted();
+ }
}
/**
@@ -228,7 +266,7 @@ public class Godot extends Fragment implements SensorEventListener, IDownloaderC
GodotLib.setup(command_line);
- final String videoDriver = GodotLib.getGlobal("rendering/quality/driver/driver_name");
+ final String videoDriver = GodotLib.getGlobal("rendering/driver/driver_name");
if (videoDriver.equals("Vulkan")) {
mRenderView = new GodotVulkanRenderView(activity, this);
} else {
@@ -241,29 +279,20 @@ public class Godot extends Fragment implements SensorEventListener, IDownloaderC
editText.setView(mRenderView);
io.setEdit(editText);
- view.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
- @Override
- public void onGlobalLayout() {
- Point fullSize = new Point();
- activity.getWindowManager().getDefaultDisplay().getSize(fullSize);
- Rect gameSize = new Rect();
- mRenderView.getView().getWindowVisibleDisplayFrame(gameSize);
-
- final int keyboardHeight = fullSize.y - gameSize.bottom;
- GodotLib.setVirtualKeyboardHeight(keyboardHeight);
- }
+ view.getViewTreeObserver().addOnGlobalLayoutListener(() -> {
+ Point fullSize = new Point();
+ activity.getWindowManager().getDefaultDisplay().getSize(fullSize);
+ Rect gameSize = new Rect();
+ mRenderView.getView().getWindowVisibleDisplayFrame(gameSize);
+ final int keyboardHeight = fullSize.y - gameSize.bottom;
+ GodotLib.setVirtualKeyboardHeight(keyboardHeight);
});
- mRenderView.queueOnRenderThread(new Runnable() {
- @Override
- public void run() {
- // Must occur after GodotLib.setup has completed.
- for (GodotPlugin plugin : pluginRegistry.getAllPlugins()) {
- plugin.onRegisterPluginWithGodotNative();
- }
-
- setKeepScreenOn("True".equals(GodotLib.getGlobal("display/window/energy_saving/keep_screen_on")));
+ mRenderView.queueOnRenderThread(() -> {
+ for (GodotPlugin plugin : pluginRegistry.getAllPlugins()) {
+ plugin.onRegisterPluginWithGodotNative();
}
+ setKeepScreenOn("True".equals(GodotLib.getGlobal("display/window/energy_saving/keep_screen_on")));
});
// Include the returned non-null views in the Godot view hierarchy.
@@ -276,14 +305,11 @@ public class Godot extends Fragment implements SensorEventListener, IDownloaderC
}
public void setKeepScreenOn(final boolean p_enabled) {
- runOnUiThread(new Runnable() {
- @Override
- public void run() {
- if (p_enabled) {
- getActivity().getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
- } else {
- getActivity().getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
- }
+ runOnUiThread(() -> {
+ if (p_enabled) {
+ getActivity().getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
+ } else {
+ getActivity().getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
}
});
}
@@ -301,7 +327,7 @@ public class Godot extends Fragment implements SensorEventListener, IDownloaderC
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
v.vibrate(VibrationEffect.createOneShot(durationMs, VibrationEffect.DEFAULT_AMPLITUDE));
} else {
- //deprecated in API 26
+ // deprecated in API 26
v.vibrate(durationMs);
}
}
@@ -330,21 +356,14 @@ public class Godot extends Fragment implements SensorEventListener, IDownloaderC
public void alert(final String message, final String title) {
final Activity activity = getActivity();
- runOnUiThread(new Runnable() {
- @Override
- public void run() {
- AlertDialog.Builder builder = new AlertDialog.Builder(activity);
- builder.setMessage(message).setTitle(title);
- builder.setPositiveButton(
- "OK",
- new DialogInterface.OnClickListener() {
- public void onClick(DialogInterface dialog, int id) {
- dialog.cancel();
- }
- });
- AlertDialog dialog = builder.create();
- dialog.show();
- }
+ runOnUiThread(() -> {
+ AlertDialog.Builder builder = new AlertDialog.Builder(activity);
+ builder.setMessage(message).setTitle(title);
+ builder.setPositiveButton(
+ "OK",
+ (dialog, id) -> dialog.cancel());
+ AlertDialog dialog = builder.create();
+ dialog.show();
});
}
@@ -356,6 +375,21 @@ public class Godot extends Fragment implements SensorEventListener, IDownloaderC
@CallSuper
protected String[] getCommandLine() {
+ String[] original = parseCommandLine();
+ String[] updated;
+ List<String> hostCommandLine = godotHost != null ? godotHost.getCommandLine() : null;
+ if (hostCommandLine == null || hostCommandLine.isEmpty()) {
+ updated = original;
+ } else {
+ updated = Arrays.copyOf(original, original.length + hostCommandLine.size());
+ for (int i = 0; i < hostCommandLine.size(); i++) {
+ updated[original.length + i] = hostCommandLine.get(i);
+ }
+ }
+ return updated;
+ }
+
+ private String[] parseCommandLine() {
InputStream is;
try {
is = getActivity().getAssets().open("_cl_");
@@ -473,13 +507,13 @@ public class Godot extends Fragment implements SensorEventListener, IDownloaderC
mClipboard = (ClipboardManager)activity.getSystemService(Context.CLIPBOARD_SERVICE);
pluginRegistry = GodotPluginRegistry.initializePluginRegistry(this);
- //check for apk expansion API
+ // check for apk expansion API
boolean md5mismatch = false;
command_line = getCommandLine();
String main_pack_md5 = null;
String main_pack_key = null;
- List<String> new_args = new LinkedList<String>();
+ List<String> new_args = new LinkedList<>();
for (int i = 0; i < command_line.length; i++) {
boolean has_extra = i < command_line.length - 1;
@@ -529,9 +563,9 @@ public class Godot extends Fragment implements SensorEventListener, IDownloaderC
command_line = new_args.toArray(new String[new_args.size()]);
}
if (use_apk_expansion && main_pack_md5 != null && main_pack_key != null) {
- //check that environment is ok!
+ // check that environment is ok!
if (!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
- //show popup and die
+ // show popup and die
}
// Build the full path to the app's expansion files
@@ -700,19 +734,16 @@ public class Godot extends Fragment implements SensorEventListener, IDownloaderC
public void UiChangeListener() {
final View decorView = getActivity().getWindow().getDecorView();
- decorView.setOnSystemUiVisibilityChangeListener(new View.OnSystemUiVisibilityChangeListener() {
- @Override
- public void onSystemUiVisibilityChange(int visibility) {
- if ((visibility & View.SYSTEM_UI_FLAG_FULLSCREEN) == 0) {
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
- decorView.setSystemUiVisibility(
- View.SYSTEM_UI_FLAG_LAYOUT_STABLE |
- View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION |
- View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN |
- View.SYSTEM_UI_FLAG_HIDE_NAVIGATION |
- View.SYSTEM_UI_FLAG_FULLSCREEN |
- View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY);
- }
+ decorView.setOnSystemUiVisibilityChangeListener(visibility -> {
+ if ((visibility & View.SYSTEM_UI_FLAG_FULLSCREEN) == 0) {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
+ decorView.setSystemUiVisibility(
+ View.SYSTEM_UI_FLAG_LAYOUT_STABLE |
+ View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION |
+ View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN |
+ View.SYSTEM_UI_FLAG_HIDE_NAVIGATION |
+ View.SYSTEM_UI_FLAG_FULLSCREEN |
+ View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY);
}
}
});
@@ -725,7 +756,7 @@ public class Godot extends Fragment implements SensorEventListener, IDownloaderC
int displayRotation = display.getRotation();
float[] adjustedValues = new float[3];
- final int axisSwap[][] = {
+ final int[][] axisSwap = {
{ 1, -1, 0, 1 }, // ROTATION_0
{ -1, -1, 1, 0 }, // ROTATION_90
{ -1, 1, 0, 1 }, // ROTATION_180
@@ -743,21 +774,18 @@ public class Godot extends Fragment implements SensorEventListener, IDownloaderC
final int typeOfSensor = event.sensor.getType();
if (mRenderView != null) {
- mRenderView.queueOnRenderThread(new Runnable() {
- @Override
- public void run() {
- if (typeOfSensor == Sensor.TYPE_ACCELEROMETER) {
- GodotLib.accelerometer(-x, y, -z);
- }
- if (typeOfSensor == Sensor.TYPE_GRAVITY) {
- GodotLib.gravity(-x, y, -z);
- }
- if (typeOfSensor == Sensor.TYPE_MAGNETIC_FIELD) {
- GodotLib.magnetometer(-x, y, -z);
- }
- if (typeOfSensor == Sensor.TYPE_GYROSCOPE) {
- GodotLib.gyroscope(x, -y, z);
- }
+ mRenderView.queueOnRenderThread(() -> {
+ if (typeOfSensor == Sensor.TYPE_ACCELEROMETER) {
+ GodotLib.accelerometer(-x, y, -z);
+ }
+ if (typeOfSensor == Sensor.TYPE_GRAVITY) {
+ GodotLib.gravity(-x, y, -z);
+ }
+ if (typeOfSensor == Sensor.TYPE_MAGNETIC_FIELD) {
+ GodotLib.magnetometer(-x, y, -z);
+ }
+ if (typeOfSensor == Sensor.TYPE_GYROSCOPE) {
+ GodotLib.gyroscope(x, -y, z);
}
});
}
@@ -792,12 +820,7 @@ public class Godot extends Fragment implements SensorEventListener, IDownloaderC
}
if (shouldQuit && mRenderView != null) {
- mRenderView.queueOnRenderThread(new Runnable() {
- @Override
- public void run() {
- GodotLib.back();
- }
- });
+ mRenderView.queueOnRenderThread(GodotLib::back);
}
}
@@ -842,7 +865,7 @@ public class Godot extends Fragment implements SensorEventListener, IDownloaderC
byte[] messageDigest = complete.digest();
// Create Hex String
- StringBuffer hexString = new StringBuffer();
+ StringBuilder hexString = new StringBuilder();
for (int i = 0; i < messageDigest.length; i++) {
String s = Integer.toHexString(0xFF & messageDigest[i]);
@@ -863,33 +886,6 @@ public class Godot extends Fragment implements SensorEventListener, IDownloaderC
}
}
- public boolean onKeyMultiple(final int inKeyCode, int repeatCount, KeyEvent event) {
- String s = event.getCharacters();
- if (s == null || s.length() == 0)
- return false;
-
- final char[] cc = s.toCharArray();
- int cnt = 0;
- for (int i = cc.length; --i >= 0; cnt += cc[i] != 0 ? 1 : 0)
- ;
- if (cnt == 0)
- return false;
- mRenderView.queueOnRenderThread(new Runnable() {
- // This method will be called on the rendering thread:
- public void run() {
- for (int i = 0, n = cc.length; i < n; i++) {
- int keyCode;
- if ((keyCode = cc[i]) != 0) {
- // Simulate key down and up...
- GodotLib.key(0, 0, keyCode, true);
- GodotLib.key(0, 0, keyCode, false);
- }
- }
- }
- });
- return true;
- }
-
public boolean requestPermission(String p_name) {
return PermissionsUtil.requestPermission(p_name, getActivity());
}
diff --git a/platform/android/java/lib/src/org/godotengine/godot/GodotGLRenderView.java b/platform/android/java/lib/src/org/godotengine/godot/GodotGLRenderView.java
index 63c91561ff..a9d45c943b 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/GodotGLRenderView.java
+++ b/platform/android/java/lib/src/org/godotengine/godot/GodotGLRenderView.java
@@ -44,11 +44,15 @@ import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.PixelFormat;
import android.opengl.GLSurfaceView;
+import android.os.Build;
import android.view.GestureDetector;
import android.view.KeyEvent;
import android.view.MotionEvent;
+import android.view.PointerIcon;
import android.view.SurfaceView;
+import androidx.annotation.Keep;
+
/**
* A simple GLSurfaceView sub-class that demonstrate how to perform
* OpenGL ES 2.0 rendering into a GL Surface. Note the following important
@@ -72,6 +76,7 @@ public class GodotGLRenderView extends GLSurfaceView implements GodotRenderView
private final GodotInputHandler inputHandler;
private final GestureDetector detector;
private final GodotRenderer godotRenderer;
+ private PointerIcon pointerIcon;
public GodotGLRenderView(Context context, Godot godot, XRMode xrMode, boolean p_use_32_bits,
boolean p_use_debug_opengl) {
@@ -83,6 +88,9 @@ public class GodotGLRenderView extends GLSurfaceView implements GodotRenderView
this.inputHandler = new GodotInputHandler(this);
this.detector = new GestureDetector(context, new GodotGestureHandler(this));
this.godotRenderer = new GodotRenderer();
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
+ pointerIcon = PointerIcon.getSystemIcon(getContext(), PointerIcon.TYPE_DEFAULT);
+ }
init(xrMode, false, 16, 0);
}
@@ -149,6 +157,21 @@ public class GodotGLRenderView extends GLSurfaceView implements GodotRenderView
return inputHandler.onGenericMotionEvent(event);
}
+ /**
+ * called from JNI to change pointer icon
+ */
+ @Keep
+ public void setPointerIcon(int pointerType) {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
+ pointerIcon = PointerIcon.getSystemIcon(getContext(), pointerType);
+ }
+ }
+
+ @Override
+ public PointerIcon onResolvePointerIcon(MotionEvent me, int pointerIndex) {
+ return pointerIcon;
+ }
+
private void init(XRMode xrMode, boolean translucent, int depth, int stencil) {
setPreserveEGLContextOnPause(true);
setFocusableInTouchMode(true);
@@ -209,13 +232,10 @@ public class GodotGLRenderView extends GLSurfaceView implements GodotRenderView
public void onResume() {
super.onResume();
- queueEvent(new Runnable() {
- @Override
- public void run() {
- // Resume the renderer
- godotRenderer.onActivityResumed();
- GodotLib.focusin();
- }
+ queueEvent(() -> {
+ // Resume the renderer
+ godotRenderer.onActivityResumed();
+ GodotLib.focusin();
});
}
@@ -223,13 +243,10 @@ public class GodotGLRenderView extends GLSurfaceView implements GodotRenderView
public void onPause() {
super.onPause();
- queueEvent(new Runnable() {
- @Override
- public void run() {
- GodotLib.focusout();
- // Pause the renderer
- godotRenderer.onActivityPaused();
- }
+ queueEvent(() -> {
+ GodotLib.focusout();
+ // Pause the renderer
+ godotRenderer.onActivityPaused();
});
}
}
diff --git a/platform/javascript/api/javascript_eval.h b/platform/android/java/lib/src/org/godotengine/godot/GodotHost.java
index 24f7648ed9..317fd13535 100644
--- a/platform/javascript/api/javascript_eval.h
+++ b/platform/android/java/lib/src/org/godotengine/godot/GodotHost.java
@@ -1,5 +1,5 @@
/*************************************************************************/
-/* javascript_eval.h */
+/* GodotHost.java */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
@@ -28,26 +28,29 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#ifndef JAVASCRIPT_EVAL_H
-#define JAVASCRIPT_EVAL_H
-
-#include "core/object/class_db.h"
-
-class JavaScript : public Object {
-private:
- GDCLASS(JavaScript, Object);
-
- static JavaScript *singleton;
-
-protected:
- static void _bind_methods();
-
-public:
- Variant eval(const String &p_code, bool p_use_global_exec_context = false);
-
- static JavaScript *get_singleton();
- JavaScript();
- ~JavaScript();
-};
-
-#endif // JAVASCRIPT_EVAL_H
+package org.godotengine.godot;
+
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * Denotate a component (e.g: Activity, Fragment) that hosts the {@link Godot} fragment.
+ */
+public interface GodotHost {
+ /**
+ * Provides a set of command line parameters to setup the engine.
+ */
+ default List<String> getCommandLine() {
+ return Collections.emptyList();
+ }
+
+ /**
+ * Invoked on the render thread when the Godot setup is complete.
+ */
+ default void onGodotSetupCompleted() {}
+
+ /**
+ * Invoked on the render thread when the Godot main loop has started.
+ */
+ default void onGodotMainLoopStarted() {}
+}
diff --git a/platform/android/java/lib/src/org/godotengine/godot/GodotIO.java b/platform/android/java/lib/src/org/godotengine/godot/GodotIO.java
index c7c7c1b40c..66882e8e72 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/GodotIO.java
+++ b/platform/android/java/lib/src/org/godotengine/godot/GodotIO.java
@@ -34,11 +34,9 @@ import org.godotengine.godot.input.*;
import android.app.Activity;
import android.content.*;
-import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.res.AssetManager;
import android.graphics.Point;
-import android.media.*;
import android.net.Uri;
import android.os.*;
import android.util.DisplayMetrics;
@@ -73,7 +71,7 @@ public class GodotIO {
public int last_file_id = 1;
- class AssetData {
+ static class AssetData {
public boolean eof = false;
public String path;
public InputStream is;
@@ -232,7 +230,7 @@ public class GodotIO {
/// DIRECTORIES
/////////////////////////
- class AssetDir {
+ static class AssetDir {
public String[] files;
public int current;
public String path;
@@ -323,97 +321,8 @@ public class GodotIO {
am = p_activity.getAssets();
activity = p_activity;
//streams = new HashMap<Integer, AssetData>();
- streams = new SparseArray<AssetData>();
- dirs = new SparseArray<AssetDir>();
- }
-
- /////////////////////////
- // AUDIO
- /////////////////////////
-
- private Object buf;
- private Thread mAudioThread;
- private AudioTrack mAudioTrack;
-
- public Object audioInit(int sampleRate, int desiredFrames) {
- int channelConfig = AudioFormat.CHANNEL_OUT_STEREO;
- int audioFormat = AudioFormat.ENCODING_PCM_16BIT;
- int frameSize = 4;
-
- System.out.printf("audioInit: initializing audio:\n");
-
- //Log.v("Godot", "Godot audio: wanted " + (isStereo ? "stereo" : "mono") + " " + (is16Bit ? "16-bit" : "8-bit") + " " + ((float)sampleRate / 1000f) + "kHz, " + desiredFrames + " frames buffer");
-
- // Let the user pick a larger buffer if they really want -- but ye
- // gods they probably shouldn't, the minimums are horrifyingly high
- // latency already
- desiredFrames = Math.max(desiredFrames, (AudioTrack.getMinBufferSize(sampleRate, channelConfig, audioFormat) + frameSize - 1) / frameSize);
-
- mAudioTrack = new AudioTrack(AudioManager.STREAM_MUSIC, sampleRate,
- channelConfig, audioFormat, desiredFrames * frameSize, AudioTrack.MODE_STREAM);
-
- audioStartThread();
-
- //Log.v("Godot", "Godot audio: got " + ((mAudioTrack.getChannelCount() >= 2) ? "stereo" : "mono") + " " + ((mAudioTrack.getAudioFormat() == AudioFormat.ENCODING_PCM_16BIT) ? "16-bit" : "8-bit") + " " + ((float)mAudioTrack.getSampleRate() / 1000f) + "kHz, " + desiredFrames + " frames buffer");
-
- buf = new short[desiredFrames * 2];
- return buf;
- }
-
- public void audioStartThread() {
- mAudioThread = new Thread(new Runnable() {
- public void run() {
- mAudioTrack.play();
- GodotLib.audio();
- }
- });
-
- // I'd take REALTIME if I could get it!
- mAudioThread.setPriority(Thread.MAX_PRIORITY);
- mAudioThread.start();
- }
-
- public void audioWriteShortBuffer(short[] buffer) {
- for (int i = 0; i < buffer.length;) {
- int result = mAudioTrack.write(buffer, i, buffer.length - i);
- if (result > 0) {
- i += result;
- } else if (result == 0) {
- try {
- Thread.sleep(1);
- } catch (InterruptedException e) {
- // Nom nom
- }
- } else {
- Log.w("Godot", "Godot audio: error return from write(short)");
- return;
- }
- }
- }
-
- public void audioQuit() {
- if (mAudioThread != null) {
- try {
- mAudioThread.join();
- } catch (Exception e) {
- Log.v("Godot", "Problem stopping audio thread: " + e);
- }
- mAudioThread = null;
-
- //Log.v("Godot", "Finished waiting for audio thread");
- }
-
- if (mAudioTrack != null) {
- mAudioTrack.stop();
- mAudioTrack = null;
- }
- }
-
- public void audioPause(boolean p_pause) {
- if (p_pause)
- mAudioTrack.pause();
- else
- mAudioTrack.play();
+ streams = new SparseArray<>();
+ dirs = new SparseArray<>();
}
/////////////////////////
@@ -452,6 +361,10 @@ public class GodotIO {
return activity.getFilesDir().getAbsolutePath();
}
+ public String getExternalDataDir() {
+ return activity.getExternalFilesDir(null).getAbsolutePath();
+ }
+
public String getLocale() {
return Locale.getDefault().toString();
}
@@ -471,7 +384,7 @@ public class GodotIO {
Point size = new Point();
display.getRealSize(size);
- int result[] = { 0, 0, size.x, size.y };
+ int[] result = { 0, 0, size.x, size.y };
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
WindowInsets insets = activity.getWindow().getDecorView().getRootWindowInsets();
DisplayCutout cutout = insets.getDisplayCutout();
@@ -493,12 +406,12 @@ public class GodotIO {
//InputMethodManager inputMgr = (InputMethodManager)activity.getSystemService(Context.INPUT_METHOD_SERVICE);
//inputMgr.toggleSoftInput(InputMethodManager.SHOW_FORCED, 0);
- };
+ }
public void hideKeyboard() {
if (edit != null)
edit.hideKeyboard();
- };
+ }
public void setScreenOrientation(int p_orientation) {
switch (p_orientation) {
diff --git a/platform/android/java/lib/src/org/godotengine/godot/GodotLib.java b/platform/android/java/lib/src/org/godotengine/godot/GodotLib.java
index 534a50e9ed..95870acda1 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/GodotLib.java
+++ b/platform/android/java/lib/src/org/godotengine/godot/GodotLib.java
@@ -34,7 +34,6 @@ import android.app.Activity;
import android.hardware.SensorEvent;
import android.view.Surface;
-import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;
/**
@@ -175,11 +174,6 @@ public class GodotLib {
public static native void focusout();
/**
- * Invoked when the audio thread is started.
- */
- public static native void audio();
-
- /**
* Used to access Godot global properties.
* @param p_key Property key
* @return String value of the property
diff --git a/platform/android/java/lib/src/org/godotengine/godot/GodotRenderView.java b/platform/android/java/lib/src/org/godotengine/godot/GodotRenderView.java
index 2047c88070..79007764a7 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/GodotRenderView.java
+++ b/platform/android/java/lib/src/org/godotengine/godot/GodotRenderView.java
@@ -35,16 +35,18 @@ import org.godotengine.godot.input.GodotInputHandler;
import android.view.SurfaceView;
public interface GodotRenderView {
- abstract public SurfaceView getView();
+ SurfaceView getView();
- abstract public void initInputDevices();
+ void initInputDevices();
- abstract public void queueOnRenderThread(Runnable event);
+ void queueOnRenderThread(Runnable event);
- abstract public void onActivityPaused();
- abstract public void onActivityResumed();
+ void onActivityPaused();
+ void onActivityResumed();
- abstract public void onBackPressed();
+ void onBackPressed();
- abstract public GodotInputHandler getInputHandler();
+ GodotInputHandler getInputHandler();
+
+ void setPointerIcon(int pointerType);
}
diff --git a/platform/android/java/lib/src/org/godotengine/godot/GodotRenderer.java b/platform/android/java/lib/src/org/godotengine/godot/GodotRenderer.java
index 59bdbf7f8d..878a119c5c 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/GodotRenderer.java
+++ b/platform/android/java/lib/src/org/godotengine/godot/GodotRenderer.java
@@ -34,7 +34,6 @@ import org.godotengine.godot.plugin.GodotPlugin;
import org.godotengine.godot.plugin.GodotPluginRegistry;
import org.godotengine.godot.utils.GLUtils;
-import android.content.Context;
import android.opengl.GLSurfaceView;
import javax.microedition.khronos.egl.EGLConfig;
diff --git a/platform/android/java/lib/src/org/godotengine/godot/GodotVulkanRenderView.java b/platform/android/java/lib/src/org/godotengine/godot/GodotVulkanRenderView.java
index 2e59dbc0d0..6fca7f2a57 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/GodotVulkanRenderView.java
+++ b/platform/android/java/lib/src/org/godotengine/godot/GodotVulkanRenderView.java
@@ -37,17 +37,21 @@ import org.godotengine.godot.vulkan.VkSurfaceView;
import android.annotation.SuppressLint;
import android.content.Context;
+import android.os.Build;
import android.view.GestureDetector;
-import android.view.InputDevice;
import android.view.KeyEvent;
import android.view.MotionEvent;
+import android.view.PointerIcon;
import android.view.SurfaceView;
+import androidx.annotation.Keep;
+
public class GodotVulkanRenderView extends VkSurfaceView implements GodotRenderView {
private final Godot godot;
private final GodotInputHandler mInputHandler;
private final GestureDetector mGestureDetector;
private final VkRenderer mRenderer;
+ private PointerIcon pointerIcon;
public GodotVulkanRenderView(Context context, Godot godot) {
super(context);
@@ -56,7 +60,9 @@ public class GodotVulkanRenderView extends VkSurfaceView implements GodotRenderV
mInputHandler = new GodotInputHandler(this);
mGestureDetector = new GestureDetector(context, new GodotGestureHandler(this));
mRenderer = new VkRenderer();
-
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
+ pointerIcon = PointerIcon.getSystemIcon(getContext(), PointerIcon.TYPE_DEFAULT);
+ }
setFocusableInTouchMode(true);
startRenderer(mRenderer);
}
@@ -124,17 +130,29 @@ public class GodotVulkanRenderView extends VkSurfaceView implements GodotRenderV
return mInputHandler.onGenericMotionEvent(event);
}
+ /**
+ * called from JNI to change pointer icon
+ */
+ @Keep
+ public void setPointerIcon(int pointerType) {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
+ pointerIcon = PointerIcon.getSystemIcon(getContext(), pointerType);
+ }
+ }
+
+ @Override
+ public PointerIcon onResolvePointerIcon(MotionEvent me, int pointerIndex) {
+ return pointerIcon;
+ }
+
@Override
public void onResume() {
super.onResume();
- queueOnVkThread(new Runnable() {
- @Override
- public void run() {
- // Resume the renderer
- mRenderer.onVkResume();
- GodotLib.focusin();
- }
+ queueOnVkThread(() -> {
+ // Resume the renderer
+ mRenderer.onVkResume();
+ GodotLib.focusin();
});
}
@@ -142,13 +160,10 @@ public class GodotVulkanRenderView extends VkSurfaceView implements GodotRenderV
public void onPause() {
super.onPause();
- queueOnVkThread(new Runnable() {
- @Override
- public void run() {
- GodotLib.focusout();
- // Pause the renderer
- mRenderer.onVkPause();
- }
+ queueOnVkThread(() -> {
+ GodotLib.focusout();
+ // Pause the renderer
+ mRenderer.onVkPause();
});
}
}
diff --git a/platform/android/java/lib/src/org/godotengine/godot/input/GodotGestureHandler.java b/platform/android/java/lib/src/org/godotengine/godot/input/GodotGestureHandler.java
index 2c39d06832..1d60c21c60 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/input/GodotGestureHandler.java
+++ b/platform/android/java/lib/src/org/godotengine/godot/input/GodotGestureHandler.java
@@ -75,12 +75,7 @@ public class GodotGestureHandler extends GestureDetector.SimpleOnGestureListener
final int x = Math.round(event.getX());
final int y = Math.round(event.getY());
final int buttonMask = event.getButtonState();
- queueEvent(new Runnable() {
- @Override
- public void run() {
- GodotLib.doubleTap(buttonMask, x, y);
- }
- });
+ queueEvent(() -> GodotLib.doubleTap(buttonMask, x, y));
return true;
}
@@ -89,12 +84,7 @@ public class GodotGestureHandler extends GestureDetector.SimpleOnGestureListener
//Log.i("GodotGesture", "onScroll");
final int x = Math.round(distanceX);
final int y = Math.round(distanceY);
- queueEvent(new Runnable() {
- @Override
- public void run() {
- GodotLib.scroll(x, y);
- }
- });
+ queueEvent(() -> GodotLib.scroll(x, y));
return true;
}
diff --git a/platform/android/java/lib/src/org/godotengine/godot/input/GodotInputHandler.java b/platform/android/java/lib/src/org/godotengine/godot/input/GodotInputHandler.java
index 435b8b325f..4dc9157545 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/input/GodotInputHandler.java
+++ b/platform/android/java/lib/src/org/godotengine/godot/input/GodotInputHandler.java
@@ -45,13 +45,9 @@ import android.view.InputDevice.MotionRange;
import android.view.KeyEvent;
import android.view.MotionEvent;
-import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
-import java.util.HashMap;
import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
import java.util.Set;
/**
@@ -64,7 +60,7 @@ public class GodotInputHandler implements InputDeviceListener {
private final String tag = this.getClass().getSimpleName();
private final SparseIntArray mJoystickIds = new SparseIntArray(4);
- private final SparseArray<Joystick> mJoysticksDevices = new SparseArray<Joystick>(4);
+ private final SparseArray<Joystick> mJoysticksDevices = new SparseArray<>(4);
public GodotInputHandler(GodotRenderView godotView) {
mRenderView = godotView;
@@ -101,22 +97,12 @@ public class GodotInputHandler implements InputDeviceListener {
final int button = getGodotButton(keyCode);
final int godotJoyId = mJoystickIds.get(deviceId);
- queueEvent(new Runnable() {
- @Override
- public void run() {
- GodotLib.joybutton(godotJoyId, button, false);
- }
- });
+ queueEvent(() -> GodotLib.joybutton(godotJoyId, button, false));
}
} else {
final int scanCode = event.getScanCode();
final int chr = event.getUnicodeChar(0);
- queueEvent(new Runnable() {
- @Override
- public void run() {
- GodotLib.key(keyCode, scanCode, chr, false);
- }
- });
+ queueEvent(() -> GodotLib.key(keyCode, scanCode, chr, false));
}
return true;
@@ -147,22 +133,12 @@ public class GodotInputHandler implements InputDeviceListener {
final int button = getGodotButton(keyCode);
final int godotJoyId = mJoystickIds.get(deviceId);
- queueEvent(new Runnable() {
- @Override
- public void run() {
- GodotLib.joybutton(godotJoyId, button, true);
- }
- });
+ queueEvent(() -> GodotLib.joybutton(godotJoyId, button, true));
}
} else {
final int scanCode = event.getScanCode();
final int chr = event.getUnicodeChar(0);
- queueEvent(new Runnable() {
- @Override
- public void run() {
- GodotLib.key(keyCode, scanCode, chr, true);
- }
- });
+ queueEvent(() -> GodotLib.key(keyCode, scanCode, chr, true));
}
return true;
@@ -194,19 +170,16 @@ public class GodotInputHandler implements InputDeviceListener {
final int action = event.getActionMasked();
final int pointer_idx = event.getPointerId(event.getActionIndex());
- mRenderView.queueOnRenderThread(new Runnable() {
- @Override
- public void run() {
- switch (action) {
- case MotionEvent.ACTION_DOWN:
- case MotionEvent.ACTION_CANCEL:
- case MotionEvent.ACTION_UP:
- case MotionEvent.ACTION_MOVE:
- case MotionEvent.ACTION_POINTER_UP:
- case MotionEvent.ACTION_POINTER_DOWN: {
- GodotLib.touch(event.getSource(), action, pointer_idx, evcount, arr);
- } break;
- }
+ mRenderView.queueOnRenderThread(() -> {
+ switch (action) {
+ case MotionEvent.ACTION_DOWN:
+ case MotionEvent.ACTION_CANCEL:
+ case MotionEvent.ACTION_UP:
+ case MotionEvent.ACTION_MOVE:
+ case MotionEvent.ACTION_POINTER_UP:
+ case MotionEvent.ACTION_POINTER_DOWN: {
+ GodotLib.touch(event.getSource(), action, pointer_idx, evcount, arr);
+ } break;
}
});
}
@@ -232,13 +205,7 @@ public class GodotInputHandler implements InputDeviceListener {
// save value to prevent repeats
joystick.axesValues.put(axis, value);
final int godotAxisIdx = i;
- queueEvent(new Runnable() {
- @Override
- public void run() {
- GodotLib.joyaxis(godotJoyId, godotAxisIdx, value);
- //Log.i(tag, "GodotLib.joyaxis("+godotJoyId+", "+godotAxisIdx+", "+value+");");
- }
- });
+ queueEvent(() -> GodotLib.joyaxis(godotJoyId, godotAxisIdx, value));
}
}
@@ -248,13 +215,7 @@ public class GodotInputHandler implements InputDeviceListener {
if (joystick.hatX != hatX || joystick.hatY != hatY) {
joystick.hatX = hatX;
joystick.hatY = hatY;
- queueEvent(new Runnable() {
- @Override
- public void run() {
- GodotLib.joyhat(godotJoyId, hatX, hatY);
- //Log.i(tag, "GodotLib.joyhat("+godotJoyId+", "+hatX+", "+hatY+");");
- }
- });
+ queueEvent(() -> GodotLib.joyhat(godotJoyId, hatX, hatY));
}
}
return true;
@@ -263,12 +224,7 @@ public class GodotInputHandler implements InputDeviceListener {
final float x = event.getX();
final float y = event.getY();
final int type = event.getAction();
- queueEvent(new Runnable() {
- @Override
- public void run() {
- GodotLib.hover(type, x, y);
- }
- });
+ queueEvent(() -> GodotLib.hover(type, x, y));
return true;
} else if (event.isFromSource(InputDevice.SOURCE_MOUSE) || event.isFromSource(InputDevice.SOURCE_MOUSE_RELATIVE)) {
@@ -333,7 +289,7 @@ public class GodotInputHandler implements InputDeviceListener {
//Helps with creating new joypad mappings.
Log.i(tag, "=== New Input Device: " + joystick.name);
- Set<Integer> already = new HashSet<Integer>();
+ Set<Integer> already = new HashSet<>();
for (InputDevice.MotionRange range : device.getMotionRanges()) {
boolean isJoystick = range.isFromSource(InputDevice.SOURCE_JOYSTICK);
boolean isGamepad = range.isFromSource(InputDevice.SOURCE_GAMEPAD);
@@ -360,12 +316,7 @@ public class GodotInputHandler implements InputDeviceListener {
}
mJoysticksDevices.put(deviceId, joystick);
- queueEvent(new Runnable() {
- @Override
- public void run() {
- GodotLib.joyconnectionchanged(id, true, joystick.name);
- }
- });
+ queueEvent(() -> GodotLib.joyconnectionchanged(id, true, joystick.name));
}
@Override
@@ -378,12 +329,7 @@ public class GodotInputHandler implements InputDeviceListener {
mJoystickIds.delete(deviceId);
mJoysticksDevices.delete(deviceId);
- queueEvent(new Runnable() {
- @Override
- public void run() {
- GodotLib.joyconnectionchanged(godotJoyId, false, "");
- }
- });
+ queueEvent(() -> GodotLib.joyconnectionchanged(godotJoyId, false, ""));
}
@Override
@@ -472,12 +418,7 @@ public class GodotInputHandler implements InputDeviceListener {
final float x = event.getX();
final float y = event.getY();
final int type = event.getAction();
- queueEvent(new Runnable() {
- @Override
- public void run() {
- GodotLib.hover(type, x, y);
- }
- });
+ queueEvent(() -> GodotLib.hover(type, x, y));
return true;
}
case MotionEvent.ACTION_BUTTON_PRESS:
@@ -487,12 +428,7 @@ public class GodotInputHandler implements InputDeviceListener {
final float y = event.getY();
final int buttonsMask = event.getButtonState();
final int action = event.getAction();
- queueEvent(new Runnable() {
- @Override
- public void run() {
- GodotLib.touch(event.getSource(), action, 0, 1, new float[] { 0, x, y }, buttonsMask);
- }
- });
+ queueEvent(() -> GodotLib.touch(event.getSource(), action, 0, 1, new float[] { 0, x, y }, buttonsMask));
return true;
}
case MotionEvent.ACTION_SCROLL: {
@@ -502,12 +438,7 @@ public class GodotInputHandler implements InputDeviceListener {
final int action = event.getAction();
final float verticalFactor = event.getAxisValue(MotionEvent.AXIS_VSCROLL);
final float horizontalFactor = event.getAxisValue(MotionEvent.AXIS_HSCROLL);
- queueEvent(new Runnable() {
- @Override
- public void run() {
- GodotLib.touch(event.getSource(), action, 0, 1, new float[] { 0, x, y }, buttonsMask, verticalFactor, horizontalFactor);
- }
- });
+ queueEvent(() -> GodotLib.touch(event.getSource(), action, 0, 1, new float[] { 0, x, y }, buttonsMask, verticalFactor, horizontalFactor));
}
case MotionEvent.ACTION_DOWN:
case MotionEvent.ACTION_UP: {
diff --git a/platform/android/java/lib/src/org/godotengine/godot/input/GodotTextInputWrapper.java b/platform/android/java/lib/src/org/godotengine/godot/input/GodotTextInputWrapper.java
index 3e0e6a65fd..020870a110 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/input/GodotTextInputWrapper.java
+++ b/platform/android/java/lib/src/org/godotengine/godot/input/GodotTextInputWrapper.java
@@ -94,17 +94,14 @@ public class GodotTextInputWrapper implements TextWatcher, OnEditorActionListene
public void beforeTextChanged(final CharSequence pCharSequence, final int start, final int count, final int after) {
//Log.d(TAG, "beforeTextChanged(" + pCharSequence + ")start: " + start + ",count: " + count + ",after: " + after);
- mRenderView.queueOnRenderThread(new Runnable() {
- @Override
- public void run() {
- for (int i = 0; i < count; ++i) {
- GodotLib.key(KeyEvent.KEYCODE_DEL, KeyEvent.KEYCODE_DEL, 0, true);
- GodotLib.key(KeyEvent.KEYCODE_DEL, KeyEvent.KEYCODE_DEL, 0, false);
-
- if (mHasSelection) {
- mHasSelection = false;
- break;
- }
+ mRenderView.queueOnRenderThread(() -> {
+ for (int i = 0; i < count; ++i) {
+ GodotLib.key(KeyEvent.KEYCODE_DEL, KeyEvent.KEYCODE_DEL, 0, true);
+ GodotLib.key(KeyEvent.KEYCODE_DEL, KeyEvent.KEYCODE_DEL, 0, false);
+
+ if (mHasSelection) {
+ mHasSelection = false;
+ break;
}
}
});
@@ -118,18 +115,15 @@ public class GodotTextInputWrapper implements TextWatcher, OnEditorActionListene
for (int i = start; i < start + count; ++i) {
newChars[i - start] = pCharSequence.charAt(i);
}
- mRenderView.queueOnRenderThread(new Runnable() {
- @Override
- public void run() {
- for (int i = 0; i < count; ++i) {
- int key = newChars[i];
- if ((key == '\n') && !mEdit.isMultiline()) {
- // Return keys are handled through action events
- continue;
- }
- GodotLib.key(0, 0, key, true);
- GodotLib.key(0, 0, key, false);
+ mRenderView.queueOnRenderThread(() -> {
+ for (int i = 0; i < count; ++i) {
+ int key = newChars[i];
+ if ((key == '\n') && !mEdit.isMultiline()) {
+ // Return keys are handled through action events
+ continue;
}
+ GodotLib.key(0, 0, key, true);
+ GodotLib.key(0, 0, key, false);
}
});
}
@@ -139,23 +133,21 @@ public class GodotTextInputWrapper implements TextWatcher, OnEditorActionListene
if (mEdit == pTextView && isFullScreenEdit()) {
final String characters = pKeyEvent.getCharacters();
- mRenderView.queueOnRenderThread(new Runnable() {
- @Override
- public void run() {
- for (int i = 0; i < characters.length(); i++) {
- final int ch = characters.codePointAt(i);
- GodotLib.key(0, 0, ch, true);
- GodotLib.key(0, 0, ch, false);
- }
+ mRenderView.queueOnRenderThread(() -> {
+ for (int i = 0; i < characters.length(); i++) {
+ final int ch = characters.codePointAt(i);
+ GodotLib.key(0, 0, ch, true);
+ GodotLib.key(0, 0, ch, false);
}
});
}
if (pActionID == EditorInfo.IME_ACTION_DONE) {
// Enter key has been pressed
- GodotLib.key(KeyEvent.KEYCODE_ENTER, KeyEvent.KEYCODE_ENTER, 0, true);
- GodotLib.key(KeyEvent.KEYCODE_ENTER, KeyEvent.KEYCODE_ENTER, 0, false);
-
+ mRenderView.queueOnRenderThread(() -> {
+ GodotLib.key(KeyEvent.KEYCODE_ENTER, KeyEvent.KEYCODE_ENTER, 0, true);
+ GodotLib.key(KeyEvent.KEYCODE_ENTER, KeyEvent.KEYCODE_ENTER, 0, false);
+ });
mRenderView.getView().requestFocus();
return true;
}
diff --git a/platform/android/java/lib/src/org/godotengine/godot/input/InputManagerCompat.java b/platform/android/java/lib/src/org/godotengine/godot/input/InputManagerCompat.java
index 62810ad3a4..21fdc658bb 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/input/InputManagerCompat.java
+++ b/platform/android/java/lib/src/org/godotengine/godot/input/InputManagerCompat.java
@@ -28,14 +28,14 @@ public interface InputManagerCompat {
* @param id The device id
* @return The input device or null if not found
*/
- public InputDevice getInputDevice(int id);
+ InputDevice getInputDevice(int id);
/**
* Gets the ids of all input devices in the system.
*
* @return The input device ids.
*/
- public int[] getInputDeviceIds();
+ int[] getInputDeviceIds();
/**
* Registers an input device listener to receive notifications about when
@@ -46,7 +46,7 @@ public interface InputManagerCompat {
* null if the listener should be invoked on the calling thread's
* looper.
*/
- public void registerInputDeviceListener(InputManagerCompat.InputDeviceListener listener,
+ void registerInputDeviceListener(InputManagerCompat.InputDeviceListener listener,
Handler handler);
/**
@@ -54,7 +54,7 @@ public interface InputManagerCompat {
*
* @param listener The listener to unregister.
*/
- public void unregisterInputDeviceListener(InputManagerCompat.InputDeviceListener listener);
+ void unregisterInputDeviceListener(InputManagerCompat.InputDeviceListener listener);
/*
* The following three calls are to simulate V16 behavior on pre-Jellybean
@@ -69,7 +69,7 @@ public interface InputManagerCompat {
*
* @param event the motion event from the app
*/
- public void onGenericMotionEvent(MotionEvent event);
+ void onGenericMotionEvent(MotionEvent event);
/**
* Tell the V9 input manager that it should stop polling for disconnected
@@ -77,7 +77,7 @@ public interface InputManagerCompat {
* might want to call it whenever your game is not active (or whenever you
* don't care about being notified of new input devices)
*/
- public void onPause();
+ void onPause();
/**
* Tell the V9 input manager that it should start polling for disconnected
@@ -85,9 +85,9 @@ public interface InputManagerCompat {
* might want to call it less often (only when the gameplay is actually
* active)
*/
- public void onResume();
+ void onResume();
- public interface InputDeviceListener {
+ interface InputDeviceListener {
/**
* Called whenever the input manager detects that a device has been
* added. This will only be called in the V9 version when a motion event
@@ -119,7 +119,7 @@ public interface InputManagerCompat {
/**
* Use this to construct a compatible InputManager.
*/
- public static class Factory {
+ class Factory {
/**
* Constructs and returns a compatible InputManger
*
diff --git a/platform/android/java/lib/src/org/godotengine/godot/input/InputManagerV16.java b/platform/android/java/lib/src/org/godotengine/godot/input/InputManagerV16.java
index 61828dccae..0dbc13c77b 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/input/InputManagerV16.java
+++ b/platform/android/java/lib/src/org/godotengine/godot/input/InputManagerV16.java
@@ -34,7 +34,7 @@ public class InputManagerV16 implements InputManagerCompat {
public InputManagerV16(Context context) {
mInputManager = (InputManager)context.getSystemService(Context.INPUT_SERVICE);
- mListeners = new HashMap<InputManagerCompat.InputDeviceListener, V16InputDeviceListener>();
+ mListeners = new HashMap<>();
}
@Override
diff --git a/platform/android/java/lib/src/org/godotengine/godot/input/Joystick.java b/platform/android/java/lib/src/org/godotengine/godot/input/Joystick.java
index 4b7318c718..bff90d7ce9 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/input/Joystick.java
+++ b/platform/android/java/lib/src/org/godotengine/godot/input/Joystick.java
@@ -41,12 +41,12 @@ import java.util.List;
class Joystick {
int device_id;
String name;
- List<Integer> axes = new ArrayList<Integer>();
+ List<Integer> axes = new ArrayList<>();
protected boolean hasAxisHat = false;
/*
* Keep track of values so we can prevent flooding the engine with useless events.
*/
- protected final SparseArray axesValues = new SparseArray<Float>(4);
+ protected final SparseArray<Float> axesValues = new SparseArray<>(4);
protected int hatX;
protected int hatY;
}
diff --git a/platform/android/java/lib/src/org/godotengine/godot/plugin/GodotPlugin.java b/platform/android/java/lib/src/org/godotengine/godot/plugin/GodotPlugin.java
index 993f0e9127..2dc8359615 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/plugin/GodotPlugin.java
+++ b/platform/android/java/lib/src/org/godotengine/godot/plugin/GodotPlugin.java
@@ -47,6 +47,7 @@ import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -109,31 +110,9 @@ public abstract class GodotPlugin {
* This method is invoked on the render thread.
*/
public final void onRegisterPluginWithGodotNative() {
- registeredSignals.putAll(registerPluginWithGodotNative(this, new GodotPluginInfoProvider() {
- @NonNull
- @Override
- public String getPluginName() {
- return GodotPlugin.this.getPluginName();
- }
-
- @NonNull
- @Override
- public List<String> getPluginMethods() {
- return GodotPlugin.this.getPluginMethods();
- }
-
- @NonNull
- @Override
- public Set<SignalInfo> getPluginSignals() {
- return GodotPlugin.this.getPluginSignals();
- }
-
- @NonNull
- @Override
- public Set<String> getPluginGDNativeLibrariesPaths() {
- return GodotPlugin.this.getPluginGDNativeLibrariesPaths();
- }
- }));
+ registeredSignals.putAll(
+ registerPluginWithGodotNative(this, getPluginName(), getPluginMethods(), getPluginSignals(),
+ getPluginGDNativeLibrariesPaths()));
}
/**
@@ -141,48 +120,65 @@ public abstract class GodotPlugin {
*
* This method must be invoked on the render thread.
*/
- public static Map<String, SignalInfo> registerPluginWithGodotNative(Object pluginObject, GodotPluginInfoProvider pluginInfoProvider) {
- nativeRegisterSingleton(pluginInfoProvider.getPluginName(), pluginObject);
+ public static void registerPluginWithGodotNative(Object pluginObject,
+ GodotPluginInfoProvider pluginInfoProvider) {
+ registerPluginWithGodotNative(pluginObject, pluginInfoProvider.getPluginName(),
+ Collections.emptyList(), pluginInfoProvider.getPluginSignals(),
+ pluginInfoProvider.getPluginGDNativeLibrariesPaths());
+
+ // Notify that registration is complete.
+ pluginInfoProvider.onPluginRegistered();
+ }
+
+ private static Map<String, SignalInfo> registerPluginWithGodotNative(Object pluginObject,
+ String pluginName, List<String> pluginMethods, Set<SignalInfo> pluginSignals,
+ Set<String> pluginGDNativeLibrariesPaths) {
+ nativeRegisterSingleton(pluginName, pluginObject);
+
+ Set<Method> filteredMethods = new HashSet<>();
+ Class<?> clazz = pluginObject.getClass();
- Class clazz = pluginObject.getClass();
Method[] methods = clazz.getDeclaredMethods();
for (Method method : methods) {
- boolean found = false;
-
- for (String s : pluginInfoProvider.getPluginMethods()) {
- if (s.equals(method.getName())) {
- found = true;
- break;
+ // Check if the method is annotated with {@link UsedByGodot}.
+ if (method.getAnnotation(UsedByGodot.class) != null) {
+ filteredMethods.add(method);
+ } else {
+ // For backward compatibility, process the methods from the given <pluginMethods> argument.
+ for (String methodName : pluginMethods) {
+ if (methodName.equals(method.getName())) {
+ filteredMethods.add(method);
+ break;
+ }
}
}
- if (!found)
- continue;
+ }
+ for (Method method : filteredMethods) {
List<String> ptr = new ArrayList<>();
- Class[] paramTypes = method.getParameterTypes();
- for (Class c : paramTypes) {
+ Class<?>[] paramTypes = method.getParameterTypes();
+ for (Class<?> c : paramTypes) {
ptr.add(c.getName());
}
String[] pt = new String[ptr.size()];
ptr.toArray(pt);
- nativeRegisterMethod(pluginInfoProvider.getPluginName(), method.getName(), method.getReturnType().getName(), pt);
+ nativeRegisterMethod(pluginName, method.getName(), method.getReturnType().getName(), pt);
}
// Register the signals for this plugin.
Map<String, SignalInfo> registeredSignals = new HashMap<>();
- for (SignalInfo signalInfo : pluginInfoProvider.getPluginSignals()) {
+ for (SignalInfo signalInfo : pluginSignals) {
String signalName = signalInfo.getName();
- nativeRegisterSignal(pluginInfoProvider.getPluginName(), signalName, signalInfo.getParamTypesNames());
+ nativeRegisterSignal(pluginName, signalName, signalInfo.getParamTypesNames());
registeredSignals.put(signalName, signalInfo);
}
// Get the list of gdnative libraries to register.
- Set<String> gdnativeLibrariesPaths = pluginInfoProvider.getPluginGDNativeLibrariesPaths();
- if (!gdnativeLibrariesPaths.isEmpty()) {
- nativeRegisterGDNativeLibraries(gdnativeLibrariesPaths.toArray(new String[0]));
+ if (!pluginGDNativeLibrariesPaths.isEmpty()) {
+ nativeRegisterGDNativeLibraries(pluginGDNativeLibrariesPaths.toArray(new String[0]));
}
return registeredSignals;
@@ -236,6 +232,11 @@ public abstract class GodotPlugin {
public boolean onMainBackPressed() { return false; }
/**
+ * Invoked on the render thread when the Godot setup is complete.
+ */
+ public void onGodotSetupCompleted() {}
+
+ /**
* Invoked on the render thread when the Godot main loop has started.
*/
public void onGodotMainLoopStarted() {}
@@ -282,8 +283,11 @@ public abstract class GodotPlugin {
/**
* Returns the list of methods to be exposed to Godot.
+ *
+ * @deprecated Used the {@link UsedByGodot} annotation instead.
*/
@NonNull
+ @Deprecated
public List<String> getPluginMethods() {
return Collections.emptyList();
}
diff --git a/platform/android/java/lib/src/org/godotengine/godot/plugin/GodotPluginInfoProvider.java b/platform/android/java/lib/src/org/godotengine/godot/plugin/GodotPluginInfoProvider.java
index c3084b036e..09366384c2 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/plugin/GodotPluginInfoProvider.java
+++ b/platform/android/java/lib/src/org/godotengine/godot/plugin/GodotPluginInfoProvider.java
@@ -32,7 +32,7 @@ package org.godotengine.godot.plugin;
import androidx.annotation.NonNull;
-import java.util.List;
+import java.util.Collections;
import java.util.Set;
/**
@@ -46,16 +46,12 @@ public interface GodotPluginInfoProvider {
String getPluginName();
/**
- * Returns the list of methods to be exposed to Godot.
- */
- @NonNull
- List<String> getPluginMethods();
-
- /**
* Returns the list of signals to be exposed to Godot.
*/
@NonNull
- Set<SignalInfo> getPluginSignals();
+ default Set<SignalInfo> getPluginSignals() {
+ return Collections.emptySet();
+ }
/**
* Returns the paths for the plugin's gdnative libraries (if any).
@@ -63,5 +59,14 @@ public interface GodotPluginInfoProvider {
* The paths must be relative to the 'assets' directory and point to a '*.gdnlib' file.
*/
@NonNull
- Set<String> getPluginGDNativeLibrariesPaths();
+ default Set<String> getPluginGDNativeLibrariesPaths() {
+ return Collections.emptySet();
+ }
+
+ /**
+ * This is invoked on the render thread when the plugin described by this instance has been
+ * registered.
+ */
+ default void onPluginRegistered() {
+ }
}
diff --git a/platform/server/godot_server.cpp b/platform/android/java/lib/src/org/godotengine/godot/plugin/UsedByGodot.java
index 1ced95fcbc..04c091d944 100644
--- a/platform/server/godot_server.cpp
+++ b/platform/android/java/lib/src/org/godotengine/godot/plugin/UsedByGodot.java
@@ -1,5 +1,5 @@
/*************************************************************************/
-/* godot_server.cpp */
+/* UsedByGodot.java */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
@@ -28,22 +28,18 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#include "main/main.h"
-#include "os_server.h"
+package org.godotengine.godot.plugin;
-int main(int argc, char *argv[]) {
- OS_Server os;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
- // We must override main when testing is enabled
- TEST_MAIN_OVERRIDE
-
- Error err = Main::setup(argv[0], argc - 1, &argv[1]);
- if (err != OK)
- return 255;
-
- if (Main::start())
- os.run(); // it is actually the OS that decides how to run
- Main::cleanup();
-
- return os.get_exit_code();
-}
+/**
+ * Annotation to indicate a method is being invoked from the Godot game logic.
+ *
+ * At runtime, annotated plugin methods are detected and automatically registered.
+ */
+@Target({ ElementType.METHOD })
+@Retention(RetentionPolicy.RUNTIME)
+public @interface UsedByGodot {}
diff --git a/platform/android/java/lib/src/org/godotengine/godot/utils/Crypt.java b/platform/android/java/lib/src/org/godotengine/godot/utils/Crypt.java
index d6e49bb635..2b6e4ad2be 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/utils/Crypt.java
+++ b/platform/android/java/lib/src/org/godotengine/godot/utils/Crypt.java
@@ -39,10 +39,10 @@ public class Crypt {
// Create MD5 Hash
MessageDigest digest = java.security.MessageDigest.getInstance("MD5");
digest.update(input.getBytes());
- byte messageDigest[] = digest.digest();
+ byte[] messageDigest = digest.digest();
// Create Hex String
- StringBuffer hexString = new StringBuffer();
+ StringBuilder hexString = new StringBuilder();
for (int i = 0; i < messageDigest.length; i++)
hexString.append(Integer.toHexString(0xFF & messageDigest[i]));
return hexString.toString();
diff --git a/platform/android/java/lib/src/org/godotengine/godot/vulkan/VkSurfaceView.kt b/platform/android/java/lib/src/org/godotengine/godot/vulkan/VkSurfaceView.kt
index f0e37d80b8..b01dc2653a 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/vulkan/VkSurfaceView.kt
+++ b/platform/android/java/lib/src/org/godotengine/godot/vulkan/VkSurfaceView.kt
@@ -115,7 +115,7 @@ open internal class VkSurfaceView(context: Context) : SurfaceView(context), Surf
/**
* Tear down the rendering thread.
*
- * Must not be called before a [VkRenderer] has been set.
+ * Must not be called before a [VkRenderer] has been set.
*/
fun onDestroy() {
vkThread.blockingExit()
diff --git a/platform/android/java/lib/src/org/godotengine/godot/vulkan/VkThread.kt b/platform/android/java/lib/src/org/godotengine/godot/vulkan/VkThread.kt
index b967fd5f24..6e59268076 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/vulkan/VkThread.kt
+++ b/platform/android/java/lib/src/org/godotengine/godot/vulkan/VkThread.kt
@@ -61,6 +61,7 @@ internal class VkThread(private val vkSurfaceView: VkSurfaceView, private val vk
private var rendererInitialized = false
private var rendererResumed = false
private var resumed = false
+ private var surfaceChanged = false
private var hasSurface = false
private var width = 0
private var height = 0
@@ -141,8 +142,10 @@ internal class VkThread(private val vkSurfaceView: VkSurfaceView, private val vk
fun onSurfaceChanged(width: Int, height: Int) {
lock.withLock {
hasSurface = true
+ surfaceChanged = true;
this.width = width
this.height = height
+
lockCondition.signalAll()
}
}
@@ -188,8 +191,11 @@ internal class VkThread(private val vkSurfaceView: VkSurfaceView, private val vk
rendererInitialized = true
vkRenderer.onVkSurfaceCreated(vkSurfaceView.holder.surface)
}
+ }
+ if (surfaceChanged) {
vkRenderer.onVkSurfaceChanged(vkSurfaceView.holder.surface, width, height)
+ surfaceChanged = false
}
// Break out of the loop so drawing can occur without holding onto the lock.
diff --git a/platform/android/java/lib/src/org/godotengine/godot/xr/regular/RegularContextFactory.java b/platform/android/java/lib/src/org/godotengine/godot/xr/regular/RegularContextFactory.java
index 71610d2d00..c852e8759a 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/xr/regular/RegularContextFactory.java
+++ b/platform/android/java/lib/src/org/godotengine/godot/xr/regular/RegularContextFactory.java
@@ -30,7 +30,6 @@
package org.godotengine.godot.xr.regular;
-import org.godotengine.godot.GodotLib;
import org.godotengine.godot.utils.GLUtils;
import android.opengl.GLSurfaceView;
diff --git a/platform/android/java/nativeSrcsConfigs/README.md b/platform/android/java/nativeSrcsConfigs/README.md
index e48505ccda..9d884415cc 100644
--- a/platform/android/java/nativeSrcsConfigs/README.md
+++ b/platform/android/java/nativeSrcsConfigs/README.md
@@ -1,4 +1,4 @@
## Native sources configs
-This is a non functional Android library used to provide Android Studio editor support to the Godot project native files.
+This is a non-functional Android library used to provide Android Studio editor support to the Godot project native files.
Nothing else should be added to this library.
diff --git a/platform/android/java/nativeSrcsConfigs/build.gradle b/platform/android/java/nativeSrcsConfigs/build.gradle
index 66077060ea..158bb2b98e 100644
--- a/platform/android/java/nativeSrcsConfigs/build.gradle
+++ b/platform/android/java/nativeSrcsConfigs/build.gradle
@@ -20,9 +20,6 @@ android {
packagingOptions {
exclude 'META-INF/LICENSE'
exclude 'META-INF/NOTICE'
-
- // Should be uncommented for development purpose within Android Studio
- // doNotStrip '**/*.so'
}
sourceSets {
diff --git a/platform/android/java_class_wrapper.cpp b/platform/android/java_class_wrapper.cpp
index ab03599dc3..ed6b5c3e14 100644
--- a/platform/android/java_class_wrapper.cpp
+++ b/platform/android/java_class_wrapper.cpp
@@ -38,6 +38,7 @@ bool JavaClass::_call_method(JavaObject *p_instance, const StringName &p_method,
return false;
JNIEnv *env = get_jni_env();
+ ERR_FAIL_COND_V(env == nullptr, false);
MethodInfo *method = nullptr;
for (List<MethodInfo>::Element *E = M->get().front(); E; E = E->next()) {
@@ -101,7 +102,7 @@ bool JavaClass::_call_method(JavaObject *p_instance, const StringName &p_method,
if (p_args[i]->get_type() != Variant::OBJECT)
arg_expected = Variant::OBJECT;
else {
- Ref<Reference> ref = *p_args[i];
+ Ref<RefCounted> ref = *p_args[i];
if (!ref.is_null()) {
if (Object::cast_to<JavaObject>(ref.ptr())) {
Ref<JavaObject> jo = ref;
@@ -487,7 +488,7 @@ Variant JavaClass::call(const StringName &p_method, const Variant **p_args, int
return ret;
}
- return Reference::call(p_method, p_args, p_argcount, r_error);
+ return RefCounted::call(p_method, p_args, p_argcount, r_error);
}
JavaClass::JavaClass() {
@@ -965,6 +966,7 @@ Ref<JavaClass> JavaClassWrapper::wrap(const String &p_class) {
return class_cache[p_class];
JNIEnv *env = get_jni_env();
+ ERR_FAIL_COND_V(env == nullptr, Ref<JavaClass>());
jclass bclass = env->FindClass(p_class.utf8().get_data());
ERR_FAIL_COND_V(!bclass, Ref<JavaClass>());
@@ -1149,6 +1151,7 @@ JavaClassWrapper::JavaClassWrapper(jobject p_activity) {
singleton = this;
JNIEnv *env = get_jni_env();
+ ERR_FAIL_COND(env == nullptr);
jclass activityClass = env->FindClass("android/app/Activity");
jmethodID getClassLoader = env->GetMethodID(activityClass, "getClassLoader", "()Ljava/lang/ClassLoader;");
diff --git a/platform/android/java_godot_io_wrapper.cpp b/platform/android/java_godot_io_wrapper.cpp
index 41201db32b..5e99135498 100644
--- a/platform/android/java_godot_io_wrapper.cpp
+++ b/platform/android/java_godot_io_wrapper.cpp
@@ -49,6 +49,7 @@ GodotIOJavaWrapper::GodotIOJavaWrapper(JNIEnv *p_env, jobject p_godot_io_instanc
_open_URI = p_env->GetMethodID(cls, "openURI", "(Ljava/lang/String;)I");
_get_data_dir = p_env->GetMethodID(cls, "getDataDir", "()Ljava/lang/String;");
+ _get_external_data_dir = p_env->GetMethodID(cls, "getExternalDataDir", "()Ljava/lang/String;");
_get_locale = p_env->GetMethodID(cls, "getLocale", "()Ljava/lang/String;");
_get_model = p_env->GetMethodID(cls, "getModel", "()Ljava/lang/String;");
_get_screen_DPI = p_env->GetMethodID(cls, "getScreenDPI", "()I");
@@ -73,6 +74,7 @@ jobject GodotIOJavaWrapper::get_instance() {
Error GodotIOJavaWrapper::open_uri(const String &p_uri) {
if (_open_URI) {
JNIEnv *env = get_jni_env();
+ ERR_FAIL_COND_V(env == nullptr, ERR_UNAVAILABLE);
jstring jStr = env->NewStringUTF(p_uri.utf8().get_data());
return env->CallIntMethod(godot_io_instance, _open_URI, jStr) ? ERR_CANT_OPEN : OK;
} else {
@@ -83,6 +85,7 @@ Error GodotIOJavaWrapper::open_uri(const String &p_uri) {
String GodotIOJavaWrapper::get_user_data_dir() {
if (_get_data_dir) {
JNIEnv *env = get_jni_env();
+ ERR_FAIL_COND_V(env == nullptr, String());
jstring s = (jstring)env->CallObjectMethod(godot_io_instance, _get_data_dir);
return jstring_to_string(s, env);
} else {
@@ -90,9 +93,21 @@ String GodotIOJavaWrapper::get_user_data_dir() {
}
}
+String GodotIOJavaWrapper::get_external_data_dir() {
+ if (_get_external_data_dir) {
+ JNIEnv *env = get_jni_env();
+ ERR_FAIL_COND_V(env == nullptr, String());
+ jstring s = (jstring)env->CallObjectMethod(godot_io_instance, _get_external_data_dir);
+ return jstring_to_string(s, env);
+ } else {
+ return String();
+ }
+}
+
String GodotIOJavaWrapper::get_locale() {
if (_get_locale) {
JNIEnv *env = get_jni_env();
+ ERR_FAIL_COND_V(env == nullptr, String());
jstring s = (jstring)env->CallObjectMethod(godot_io_instance, _get_locale);
return jstring_to_string(s, env);
} else {
@@ -103,6 +118,7 @@ String GodotIOJavaWrapper::get_locale() {
String GodotIOJavaWrapper::get_model() {
if (_get_model) {
JNIEnv *env = get_jni_env();
+ ERR_FAIL_COND_V(env == nullptr, String());
jstring s = (jstring)env->CallObjectMethod(godot_io_instance, _get_model);
return jstring_to_string(s, env);
} else {
@@ -113,6 +129,7 @@ String GodotIOJavaWrapper::get_model() {
int GodotIOJavaWrapper::get_screen_dpi() {
if (_get_screen_DPI) {
JNIEnv *env = get_jni_env();
+ ERR_FAIL_COND_V(env == nullptr, 160);
return env->CallIntMethod(godot_io_instance, _get_screen_DPI);
} else {
return 160;
@@ -122,6 +139,7 @@ int GodotIOJavaWrapper::get_screen_dpi() {
void GodotIOJavaWrapper::screen_get_usable_rect(int (&p_rect_xywh)[4]) {
if (_screen_get_usable_rect) {
JNIEnv *env = get_jni_env();
+ ERR_FAIL_COND(env == nullptr);
jintArray returnArray = (jintArray)env->CallObjectMethod(godot_io_instance, _screen_get_usable_rect);
ERR_FAIL_COND(env->GetArrayLength(returnArray) != 4);
jint *arrayBody = env->GetIntArrayElements(returnArray, JNI_FALSE);
@@ -135,6 +153,7 @@ void GodotIOJavaWrapper::screen_get_usable_rect(int (&p_rect_xywh)[4]) {
String GodotIOJavaWrapper::get_unique_id() {
if (_get_unique_id) {
JNIEnv *env = get_jni_env();
+ ERR_FAIL_COND_V(env == nullptr, String());
jstring s = (jstring)env->CallObjectMethod(godot_io_instance, _get_unique_id);
return jstring_to_string(s, env);
} else {
@@ -149,6 +168,7 @@ bool GodotIOJavaWrapper::has_vk() {
void GodotIOJavaWrapper::show_vk(const String &p_existing, bool p_multiline, int p_max_input_length, int p_cursor_start, int p_cursor_end) {
if (_show_keyboard) {
JNIEnv *env = get_jni_env();
+ ERR_FAIL_COND(env == nullptr);
jstring jStr = env->NewStringUTF(p_existing.utf8().get_data());
env->CallVoidMethod(godot_io_instance, _show_keyboard, jStr, p_multiline, p_max_input_length, p_cursor_start, p_cursor_end);
}
@@ -157,6 +177,7 @@ void GodotIOJavaWrapper::show_vk(const String &p_existing, bool p_multiline, int
void GodotIOJavaWrapper::hide_vk() {
if (_hide_keyboard) {
JNIEnv *env = get_jni_env();
+ ERR_FAIL_COND(env == nullptr);
env->CallVoidMethod(godot_io_instance, _hide_keyboard);
}
}
@@ -164,6 +185,7 @@ void GodotIOJavaWrapper::hide_vk() {
void GodotIOJavaWrapper::set_screen_orientation(int p_orient) {
if (_set_screen_orientation) {
JNIEnv *env = get_jni_env();
+ ERR_FAIL_COND(env == nullptr);
env->CallVoidMethod(godot_io_instance, _set_screen_orientation, p_orient);
}
}
@@ -171,6 +193,7 @@ void GodotIOJavaWrapper::set_screen_orientation(int p_orient) {
int GodotIOJavaWrapper::get_screen_orientation() {
if (_get_screen_orientation) {
JNIEnv *env = get_jni_env();
+ ERR_FAIL_COND_V(env == nullptr, 0);
return env->CallIntMethod(godot_io_instance, _get_screen_orientation);
} else {
return 0;
@@ -180,6 +203,7 @@ int GodotIOJavaWrapper::get_screen_orientation() {
String GodotIOJavaWrapper::get_system_dir(int p_dir) {
if (_get_system_dir) {
JNIEnv *env = get_jni_env();
+ ERR_FAIL_COND_V(env == nullptr, String("."));
jstring s = (jstring)env->CallObjectMethod(godot_io_instance, _get_system_dir, p_dir);
return jstring_to_string(s, env);
} else {
diff --git a/platform/android/java_godot_io_wrapper.h b/platform/android/java_godot_io_wrapper.h
index 394e97effa..e4c0a4b2c7 100644
--- a/platform/android/java_godot_io_wrapper.h
+++ b/platform/android/java_godot_io_wrapper.h
@@ -47,6 +47,7 @@ private:
jmethodID _open_URI = 0;
jmethodID _get_data_dir = 0;
+ jmethodID _get_external_data_dir = 0;
jmethodID _get_locale = 0;
jmethodID _get_model = 0;
jmethodID _get_screen_DPI = 0;
@@ -66,6 +67,7 @@ public:
Error open_uri(const String &p_uri);
String get_user_data_dir();
+ String get_external_data_dir();
String get_locale();
String get_model();
int get_screen_dpi();
diff --git a/platform/android/java_godot_lib_jni.cpp b/platform/android/java_godot_lib_jni.cpp
index bb22162879..2eded01bf4 100644
--- a/platform/android/java_godot_lib_jni.cpp
+++ b/platform/android/java_godot_lib_jni.cpp
@@ -36,7 +36,6 @@
#include "android/asset_manager_jni.h"
#include "api/java_class_wrapper.h"
#include "api/jni_singleton.h"
-#include "audio_driver_jandroid.h"
#include "core/config/engine.h"
#include "core/config/project_settings.h"
#include "core/input/input.h"
@@ -94,7 +93,6 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_initialize(JNIEnv *en
FileAccessAndroid::asset_manager = AAssetManager_fromJava(env, amgr);
DirAccessJAndroid::setup(godot_io_java->get_instance());
- AudioDriverAndroid::setup(godot_io_java->get_instance());
NetSocketAndroid::setup(godot_java->get_member_object("netUtils", "Lorg/godotengine/godot/utils/GodotNetUtils;", env));
os_android = new OS_Android(godot_java, godot_io_java, p_use_apk_expansion);
@@ -127,9 +125,11 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_setup(JNIEnv *env, jc
if (p_cmdline) {
cmdlen = env->GetArrayLength(p_cmdline);
if (cmdlen) {
- cmdline = (const char **)malloc((cmdlen + 1) * sizeof(const char *));
+ cmdline = (const char **)memalloc((cmdlen + 1) * sizeof(const char *));
+ ERR_FAIL_NULL_MSG(cmdline, "Out of memory.");
cmdline[cmdlen] = nullptr;
- j_cmdline = (jstring *)malloc(cmdlen * sizeof(jstring));
+ j_cmdline = (jstring *)memalloc(cmdlen * sizeof(jstring));
+ ERR_FAIL_NULL_MSG(j_cmdline, "Out of memory.");
for (int i = 0; i < cmdlen; i++) {
jstring string = (jstring)env->GetObjectArrayElement(p_cmdline, i);
@@ -147,17 +147,17 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_setup(JNIEnv *env, jc
for (int i = 0; i < cmdlen; ++i) {
env->ReleaseStringUTFChars(j_cmdline[i], cmdline[i]);
}
- free(j_cmdline);
+ memfree(j_cmdline);
}
- free(cmdline);
+ memfree(cmdline);
}
if (err != OK) {
- return; //should exit instead and print the error
+ return; // should exit instead and print the error
}
java_class_wrapper = memnew(JavaClassWrapper(godot_java->get_activity()));
- ClassDB::register_class<JNISingleton>();
+ GDREGISTER_CLASS(JNISingleton);
}
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_resize(JNIEnv *env, jclass clazz, jobject p_surface, jint p_width, jint p_height) {
@@ -171,6 +171,7 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_resize(JNIEnv *env, j
os_android->set_native_window(native_window);
DisplayServerAndroid::get_singleton()->reset_window();
+ DisplayServerAndroid::get_singleton()->notify_surface_changed(p_width, p_height);
}
}
}
@@ -215,9 +216,10 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_step(JNIEnv *env, jcl
if (step == 1) {
if (!Main::start()) {
- return; //should exit instead and print the error
+ return; // should exit instead and print the error
}
+ godot_java->on_godot_setup_completed(env);
os_android->main_loop_begin();
godot_java->on_godot_main_loop_started(env);
++step;
@@ -322,15 +324,15 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_joyhat(JNIEnv *env, j
int hat = 0;
if (p_hat_x != 0) {
if (p_hat_x < 0)
- hat |= Input::HAT_MASK_LEFT;
+ hat |= HatMask::HAT_MASK_LEFT;
else
- hat |= Input::HAT_MASK_RIGHT;
+ hat |= HatMask::HAT_MASK_RIGHT;
}
if (p_hat_y != 0) {
if (p_hat_y < 0)
- hat |= Input::HAT_MASK_UP;
+ hat |= HatMask::HAT_MASK_UP;
else
- hat |= Input::HAT_MASK_DOWN;
+ hat |= HatMask::HAT_MASK_DOWN;
}
jevent.hat = hat;
@@ -381,11 +383,6 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_focusout(JNIEnv *env,
os_android->main_loop_focusout();
}
-JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_audio(JNIEnv *env, jclass clazz) {
- setup_android_thread();
- AudioDriverAndroid::thread_func(env);
-}
-
JNIEXPORT jstring JNICALL Java_org_godotengine_godot_GodotLib_getGlobal(JNIEnv *env, jclass clazz, jstring path) {
String js = jstring_to_string(path, env);
diff --git a/platform/android/java_godot_lib_jni.h b/platform/android/java_godot_lib_jni.h
index a3e2933185..63e9e6d8e5 100644
--- a/platform/android/java_godot_lib_jni.h
+++ b/platform/android/java_godot_lib_jni.h
@@ -56,7 +56,6 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_joybutton(JNIEnv *env
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_joyaxis(JNIEnv *env, jclass clazz, jint p_device, jint p_axis, jfloat p_value);
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_joyhat(JNIEnv *env, jclass clazz, jint p_device, jint p_hat_x, jint p_hat_y);
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_joyconnectionchanged(JNIEnv *env, jclass clazz, jint p_device, jboolean p_connected, jstring p_name);
-JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_audio(JNIEnv *env, jclass clazz);
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_accelerometer(JNIEnv *env, jclass clazz, jfloat x, jfloat y, jfloat z);
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_gravity(JNIEnv *env, jclass clazz, jfloat x, jfloat y, jfloat z);
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_magnetometer(JNIEnv *env, jclass clazz, jfloat x, jfloat y, jfloat z);
diff --git a/platform/android/java_godot_view_wrapper.cpp b/platform/android/java_godot_view_wrapper.cpp
index 5b638300ef..837d2aeced 100644
--- a/platform/android/java_godot_view_wrapper.cpp
+++ b/platform/android/java_godot_view_wrapper.cpp
@@ -34,6 +34,7 @@
GodotJavaViewWrapper::GodotJavaViewWrapper(jobject godot_view) {
JNIEnv *env = get_jni_env();
+ ERR_FAIL_COND(env == nullptr);
_godot_view = env->NewGlobalRef(godot_view);
@@ -42,12 +43,15 @@ GodotJavaViewWrapper::GodotJavaViewWrapper(jobject godot_view) {
if (android_get_device_api_level() >= __ANDROID_API_O__) {
_request_pointer_capture = env->GetMethodID(_cls, "requestPointerCapture", "()V");
_release_pointer_capture = env->GetMethodID(_cls, "releasePointerCapture", "()V");
+ _set_pointer_icon = env->GetMethodID(_cls, "setPointerIcon", "(I)V");
}
}
void GodotJavaViewWrapper::request_pointer_capture() {
if (_request_pointer_capture != 0) {
JNIEnv *env = get_jni_env();
+ ERR_FAIL_COND(env == nullptr);
+
env->CallVoidMethod(_godot_view, _request_pointer_capture);
}
}
@@ -55,12 +59,25 @@ void GodotJavaViewWrapper::request_pointer_capture() {
void GodotJavaViewWrapper::release_pointer_capture() {
if (_request_pointer_capture != 0) {
JNIEnv *env = get_jni_env();
+ ERR_FAIL_COND(env == nullptr);
+
env->CallVoidMethod(_godot_view, _release_pointer_capture);
}
}
+void GodotJavaViewWrapper::set_pointer_icon(int pointer_type) {
+ if (_set_pointer_icon != 0) {
+ JNIEnv *env = get_jni_env();
+ ERR_FAIL_COND(env == nullptr);
+
+ env->CallVoidMethod(_godot_view, _set_pointer_icon, pointer_type);
+ }
+}
+
GodotJavaViewWrapper::~GodotJavaViewWrapper() {
JNIEnv *env = get_jni_env();
+ ERR_FAIL_COND(env == nullptr);
+
env->DeleteGlobalRef(_godot_view);
env->DeleteGlobalRef(_cls);
}
diff --git a/platform/android/java_godot_view_wrapper.h b/platform/android/java_godot_view_wrapper.h
index 548c278292..da547d8118 100644
--- a/platform/android/java_godot_view_wrapper.h
+++ b/platform/android/java_godot_view_wrapper.h
@@ -34,6 +34,8 @@
#include <android/log.h>
#include <jni.h>
+#include "string_android.h"
+
// Class that makes functions in java/src/org/godotengine/godot/GodotView.java callable from C++
class GodotJavaViewWrapper {
private:
@@ -43,12 +45,14 @@ private:
jmethodID _request_pointer_capture = 0;
jmethodID _release_pointer_capture = 0;
+ jmethodID _set_pointer_icon = 0;
public:
GodotJavaViewWrapper(jobject godot_view);
void request_pointer_capture();
void release_pointer_capture();
+ void set_pointer_icon(int pointer_type);
~GodotJavaViewWrapper();
};
diff --git a/platform/android/java_godot_wrapper.cpp b/platform/android/java_godot_wrapper.cpp
index 759980373a..bfd93345f3 100644
--- a/platform/android/java_godot_wrapper.cpp
+++ b/platform/android/java_godot_wrapper.cpp
@@ -74,6 +74,7 @@ GodotJavaWrapper::GodotJavaWrapper(JNIEnv *p_env, jobject p_activity, jobject p_
_is_activity_resumed = p_env->GetMethodID(godot_class, "isActivityResumed", "()Z");
_vibrate = p_env->GetMethodID(godot_class, "vibrate", "(I)V");
_get_input_fallback_mapping = p_env->GetMethodID(godot_class, "getInputFallbackMapping", "()Ljava/lang/String;");
+ _on_godot_setup_completed = p_env->GetMethodID(godot_class, "onGodotSetupCompleted", "()V");
_on_godot_main_loop_started = p_env->GetMethodID(godot_class, "onGodotMainLoopStarted", "()V");
// get some Activity method pointers...
@@ -93,6 +94,8 @@ jobject GodotJavaWrapper::get_member_object(const char *p_name, const char *p_cl
if (p_env == nullptr)
p_env = get_jni_env();
+ ERR_FAIL_COND_V(p_env == nullptr, nullptr);
+
jfieldID fid = p_env->GetStaticFieldID(godot_class, p_name, p_class);
return p_env->GetStaticObjectField(godot_class, fid);
} else {
@@ -103,6 +106,8 @@ jobject GodotJavaWrapper::get_member_object(const char *p_name, const char *p_cl
jobject GodotJavaWrapper::get_class_loader() {
if (_get_class_loader) {
JNIEnv *env = get_jni_env();
+ ERR_FAIL_COND_V(env == nullptr, nullptr);
+
return env->CallObjectMethod(activity, _get_class_loader);
} else {
return nullptr;
@@ -114,17 +119,30 @@ GodotJavaViewWrapper *GodotJavaWrapper::get_godot_view() {
return _godot_view;
}
JNIEnv *env = get_jni_env();
+ ERR_FAIL_COND_V(env == nullptr, nullptr);
+
jmethodID godot_view_getter = env->GetMethodID(godot_class, "getRenderView", "()Lorg/godotengine/godot/GodotRenderView;");
_godot_view = new GodotJavaViewWrapper(env->CallObjectMethod(godot_instance, godot_view_getter));
return _godot_view;
}
void GodotJavaWrapper::on_video_init(JNIEnv *p_env) {
- if (_on_video_init)
+ if (_on_video_init) {
if (p_env == nullptr)
p_env = get_jni_env();
+ ERR_FAIL_COND(p_env == nullptr);
- p_env->CallVoidMethod(godot_instance, _on_video_init);
+ p_env->CallVoidMethod(godot_instance, _on_video_init);
+ }
+}
+
+void GodotJavaWrapper::on_godot_setup_completed(JNIEnv *p_env) {
+ if (_on_godot_setup_completed) {
+ if (p_env == nullptr) {
+ p_env = get_jni_env();
+ }
+ p_env->CallVoidMethod(godot_instance, _on_godot_setup_completed);
+ }
}
void GodotJavaWrapper::on_godot_main_loop_started(JNIEnv *p_env) {
@@ -132,29 +150,36 @@ void GodotJavaWrapper::on_godot_main_loop_started(JNIEnv *p_env) {
if (p_env == nullptr) {
p_env = get_jni_env();
}
+ ERR_FAIL_COND(p_env == nullptr);
+ p_env->CallVoidMethod(godot_instance, _on_godot_main_loop_started);
}
- p_env->CallVoidMethod(godot_instance, _on_godot_main_loop_started);
}
void GodotJavaWrapper::restart(JNIEnv *p_env) {
- if (_restart)
+ if (_restart) {
if (p_env == nullptr)
p_env = get_jni_env();
+ ERR_FAIL_COND(p_env == nullptr);
- p_env->CallVoidMethod(godot_instance, _restart);
+ p_env->CallVoidMethod(godot_instance, _restart);
+ }
}
void GodotJavaWrapper::force_quit(JNIEnv *p_env) {
- if (_finish)
+ if (_finish) {
if (p_env == nullptr)
p_env = get_jni_env();
+ ERR_FAIL_COND(p_env == nullptr);
- p_env->CallVoidMethod(godot_instance, _finish);
+ p_env->CallVoidMethod(godot_instance, _finish);
+ }
}
void GodotJavaWrapper::set_keep_screen_on(bool p_enabled) {
if (_set_keep_screen_on) {
JNIEnv *env = get_jni_env();
+ ERR_FAIL_COND(env == nullptr);
+
env->CallVoidMethod(godot_instance, _set_keep_screen_on, p_enabled);
}
}
@@ -162,6 +187,8 @@ void GodotJavaWrapper::set_keep_screen_on(bool p_enabled) {
void GodotJavaWrapper::alert(const String &p_message, const String &p_title) {
if (_alert) {
JNIEnv *env = get_jni_env();
+ ERR_FAIL_COND(env == nullptr);
+
jstring jStrMessage = env->NewStringUTF(p_message.utf8().get_data());
jstring jStrTitle = env->NewStringUTF(p_title.utf8().get_data());
env->CallVoidMethod(godot_instance, _alert, jStrMessage, jStrTitle);
@@ -170,6 +197,8 @@ void GodotJavaWrapper::alert(const String &p_message, const String &p_title) {
int GodotJavaWrapper::get_gles_version_code() {
JNIEnv *env = get_jni_env();
+ ERR_FAIL_COND_V(env == nullptr, 0);
+
if (_get_GLES_version_code) {
return env->CallIntMethod(godot_instance, _get_GLES_version_code);
}
@@ -184,6 +213,8 @@ bool GodotJavaWrapper::has_get_clipboard() {
String GodotJavaWrapper::get_clipboard() {
if (_get_clipboard) {
JNIEnv *env = get_jni_env();
+ ERR_FAIL_COND_V(env == nullptr, String());
+
jstring s = (jstring)env->CallObjectMethod(godot_instance, _get_clipboard);
return jstring_to_string(s, env);
} else {
@@ -194,6 +225,8 @@ String GodotJavaWrapper::get_clipboard() {
String GodotJavaWrapper::get_input_fallback_mapping() {
if (_get_input_fallback_mapping) {
JNIEnv *env = get_jni_env();
+ ERR_FAIL_COND_V(env == nullptr, String());
+
jstring fallback_mapping = (jstring)env->CallObjectMethod(godot_instance, _get_input_fallback_mapping);
return jstring_to_string(fallback_mapping, env);
} else {
@@ -208,6 +241,8 @@ bool GodotJavaWrapper::has_set_clipboard() {
void GodotJavaWrapper::set_clipboard(const String &p_text) {
if (_set_clipboard) {
JNIEnv *env = get_jni_env();
+ ERR_FAIL_COND(env == nullptr);
+
jstring jStr = env->NewStringUTF(p_text.utf8().get_data());
env->CallVoidMethod(godot_instance, _set_clipboard, jStr);
}
@@ -216,6 +251,8 @@ void GodotJavaWrapper::set_clipboard(const String &p_text) {
bool GodotJavaWrapper::request_permission(const String &p_name) {
if (_request_permission) {
JNIEnv *env = get_jni_env();
+ ERR_FAIL_COND_V(env == nullptr, false);
+
jstring jStrName = env->NewStringUTF(p_name.utf8().get_data());
return env->CallBooleanMethod(godot_instance, _request_permission, jStrName);
} else {
@@ -226,6 +263,8 @@ bool GodotJavaWrapper::request_permission(const String &p_name) {
bool GodotJavaWrapper::request_permissions() {
if (_request_permissions) {
JNIEnv *env = get_jni_env();
+ ERR_FAIL_COND_V(env == nullptr, false);
+
return env->CallBooleanMethod(godot_instance, _request_permissions);
} else {
return false;
@@ -236,6 +275,8 @@ Vector<String> GodotJavaWrapper::get_granted_permissions() const {
Vector<String> permissions_list;
if (_get_granted_permissions) {
JNIEnv *env = get_jni_env();
+ ERR_FAIL_COND_V(env == nullptr, permissions_list);
+
jobject permissions_object = env->CallObjectMethod(godot_instance, _get_granted_permissions);
jobjectArray *arr = reinterpret_cast<jobjectArray *>(&permissions_object);
@@ -254,6 +295,8 @@ Vector<String> GodotJavaWrapper::get_granted_permissions() const {
void GodotJavaWrapper::init_input_devices() {
if (_init_input_devices) {
JNIEnv *env = get_jni_env();
+ ERR_FAIL_COND(env == nullptr);
+
env->CallVoidMethod(godot_instance, _init_input_devices);
}
}
@@ -261,6 +304,8 @@ void GodotJavaWrapper::init_input_devices() {
jobject GodotJavaWrapper::get_surface() {
if (_get_surface) {
JNIEnv *env = get_jni_env();
+ ERR_FAIL_COND_V(env == nullptr, nullptr);
+
return env->CallObjectMethod(godot_instance, _get_surface);
} else {
return nullptr;
@@ -270,6 +315,8 @@ jobject GodotJavaWrapper::get_surface() {
bool GodotJavaWrapper::is_activity_resumed() {
if (_is_activity_resumed) {
JNIEnv *env = get_jni_env();
+ ERR_FAIL_COND_V(env == nullptr, false);
+
return env->CallBooleanMethod(godot_instance, _is_activity_resumed);
} else {
return false;
@@ -279,6 +326,8 @@ bool GodotJavaWrapper::is_activity_resumed() {
void GodotJavaWrapper::vibrate(int p_duration_ms) {
if (_vibrate) {
JNIEnv *env = get_jni_env();
+ ERR_FAIL_COND(env == nullptr);
+
env->CallVoidMethod(godot_instance, _vibrate, p_duration_ms);
}
}
diff --git a/platform/android/java_godot_wrapper.h b/platform/android/java_godot_wrapper.h
index 99a60dffa1..0e20747a16 100644
--- a/platform/android/java_godot_wrapper.h
+++ b/platform/android/java_godot_wrapper.h
@@ -66,6 +66,7 @@ private:
jmethodID _is_activity_resumed = 0;
jmethodID _vibrate = 0;
jmethodID _get_input_fallback_mapping = 0;
+ jmethodID _on_godot_setup_completed = 0;
jmethodID _on_godot_main_loop_started = 0;
jmethodID _get_class_loader = 0;
@@ -80,6 +81,7 @@ public:
GodotJavaViewWrapper *get_godot_view();
void on_video_init(JNIEnv *p_env = nullptr);
+ void on_godot_setup_completed(JNIEnv *p_env = nullptr);
void on_godot_main_loop_started(JNIEnv *p_env = nullptr);
void restart(JNIEnv *p_env = nullptr);
void force_quit(JNIEnv *p_env = nullptr);
diff --git a/platform/android/net_socket_android.cpp b/platform/android/net_socket_android.cpp
index ddc2368793..dbd1870ee6 100644
--- a/platform/android/net_socket_android.cpp
+++ b/platform/android/net_socket_android.cpp
@@ -103,7 +103,7 @@ Error NetSocketAndroid::set_broadcasting_enabled(bool p_enabled) {
return OK;
}
-Error NetSocketAndroid::join_multicast_group(const IP_Address &p_multi_address, String p_if_name) {
+Error NetSocketAndroid::join_multicast_group(const IPAddress &p_multi_address, String p_if_name) {
Error err = NetSocketPosix::join_multicast_group(p_multi_address, p_if_name);
if (err != OK)
return err;
@@ -115,7 +115,7 @@ Error NetSocketAndroid::join_multicast_group(const IP_Address &p_multi_address,
return OK;
}
-Error NetSocketAndroid::leave_multicast_group(const IP_Address &p_multi_address, String p_if_name) {
+Error NetSocketAndroid::leave_multicast_group(const IPAddress &p_multi_address, String p_if_name) {
Error err = NetSocketPosix::leave_multicast_group(p_multi_address, p_if_name);
if (err != OK)
return err;
diff --git a/platform/android/net_socket_android.h b/platform/android/net_socket_android.h
index cc2a68ac49..60090c26bb 100644
--- a/platform/android/net_socket_android.h
+++ b/platform/android/net_socket_android.h
@@ -67,8 +67,8 @@ public:
virtual void close();
virtual Error set_broadcasting_enabled(bool p_enabled);
- virtual Error join_multicast_group(const IP_Address &p_multi_address, String p_if_name);
- virtual Error leave_multicast_group(const IP_Address &p_multi_address, String p_if_name);
+ virtual Error join_multicast_group(const IPAddress &p_multi_address, String p_if_name);
+ virtual Error leave_multicast_group(const IPAddress &p_multi_address, String p_if_name);
NetSocketAndroid() {}
~NetSocketAndroid();
diff --git a/platform/android/os_android.cpp b/platform/android/os_android.cpp
index 422814dd50..222976d948 100644
--- a/platform/android/os_android.cpp
+++ b/platform/android/os_android.cpp
@@ -45,6 +45,23 @@
#include "java_godot_io_wrapper.h"
#include "java_godot_wrapper.h"
+String _remove_symlink(const String &dir) {
+ // Workaround for Android 6.0+ using a symlink.
+ // Save the current directory.
+ char current_dir_name[2048];
+ getcwd(current_dir_name, 2048);
+ // Change directory to the external data directory.
+ chdir(dir.utf8().get_data());
+ // Get the actual directory without the potential symlink.
+ char dir_name_wihout_symlink[2048];
+ getcwd(dir_name_wihout_symlink, 2048);
+ // Convert back to a String.
+ String dir_without_symlink(dir_name_wihout_symlink);
+ // Restore original current directory.
+ chdir(current_dir_name);
+ return dir_without_symlink;
+}
+
class AndroidLogger : public Logger {
public:
virtual void logv(const char *p_format, va_list p_list, bool p_err) {
@@ -199,26 +216,18 @@ String OS_Android::get_user_data_dir() const {
String data_dir = godot_io_java->get_user_data_dir();
if (data_dir != "") {
- //store current dir
- char real_current_dir_name[2048];
- getcwd(real_current_dir_name, 2048);
-
- //go to data dir
- chdir(data_dir.utf8().get_data());
-
- //get actual data dir, so we resolve potential symlink (Android 6.0+ seems to use symlink)
- char data_current_dir_name[2048];
- getcwd(data_current_dir_name, 2048);
-
- //cache by parsing utf8
- data_dir_cache.parse_utf8(data_current_dir_name);
-
- //restore original dir so we don't mess things up
- chdir(real_current_dir_name);
-
+ data_dir_cache = _remove_symlink(data_dir);
return data_dir_cache;
}
+ return ".";
+}
+String OS_Android::get_external_data_dir() const {
+ String data_dir = godot_io_java->get_external_data_dir();
+ if (data_dir != "") {
+ data_dir = _remove_symlink(data_dir);
+ return data_dir;
+ }
return ".";
}
diff --git a/platform/android/os_android.h b/platform/android/os_android.h
index dd14b69cf9..1e89e9211d 100644
--- a/platform/android/os_android.h
+++ b/platform/android/os_android.h
@@ -31,7 +31,6 @@
#ifndef OS_ANDROID_H
#define OS_ANDROID_H
-#include "audio_driver_jandroid.h"
#include "audio_driver_opensl.h"
#include "core/os/main_loop.h"
#include "drivers/unix/os_unix.h"
@@ -59,7 +58,6 @@ private:
mutable String data_dir_cache;
- //AudioDriverAndroid audio_driver_android;
AudioDriverOpenSL audio_driver_android;
MainLoop *main_loop;
@@ -111,6 +109,7 @@ public:
virtual Error shell_open(String p_uri) override;
virtual String get_user_data_dir() const override;
+ virtual String get_external_data_dir() const override;
virtual String get_resource_dir() const override;
virtual String get_locale() const override;
virtual String get_model_name() const override;
diff --git a/platform/android/plugin/godot_plugin_config.h b/platform/android/plugin/godot_plugin_config.h
index 173ac115a2..6b708548ae 100644
--- a/platform/android/plugin/godot_plugin_config.h
+++ b/platform/android/plugin/godot_plugin_config.h
@@ -37,8 +37,8 @@
/*
The `config` section and fields are required and defined as follow:
-- **name**: name of the plugin
-- **binary_type**: can be either `local` or `remote`. The type affects the **binary** field
+- **name**: name of the plugin.
+- **binary_type**: can be either `local` or `remote`. The type affects the **binary** field.
- **binary**:
- if **binary_type** is `local`, then this should be the filename of the plugin `aar` file in the `res://android/plugins` directory (e.g: `MyPlugin.aar`).
- if **binary_type** is `remote`, then this should be a declaration for a remote gradle binary (e.g: "org.godot.example:my-plugin:0.0.0").
@@ -102,7 +102,7 @@ struct PluginConfigAndroid {
static inline String resolve_local_dependency_path(String plugin_config_dir, String dependency_path) {
String absolute_path;
if (!dependency_path.is_empty()) {
- if (dependency_path.is_abs_path()) {
+ if (dependency_path.is_absolute_path()) {
absolute_path = ProjectSettings::get_singleton()->globalize_path(dependency_path);
} else {
absolute_path = plugin_config_dir.plus_file(dependency_path);
diff --git a/platform/android/plugin/godot_plugin_jni.cpp b/platform/android/plugin/godot_plugin_jni.cpp
index ba3e9fa20f..cf0c02e2bf 100644
--- a/platform/android/plugin/godot_plugin_jni.cpp
+++ b/platform/android/plugin/godot_plugin_jni.cpp
@@ -43,7 +43,7 @@ extern "C" {
JNIEXPORT void JNICALL Java_org_godotengine_godot_plugin_GodotPlugin_nativeRegisterSingleton(JNIEnv *env, jclass clazz, jstring name, jobject obj) {
String singname = jstring_to_string(name, env);
- JNISingleton *s = (JNISingleton *)ClassDB::instance("JNISingleton");
+ JNISingleton *s = (JNISingleton *)ClassDB::instantiate("JNISingleton");
s->set_instance(env->NewGlobalRef(obj));
jni_singletons[singname] = s;
diff --git a/platform/android/thread_jandroid.cpp b/platform/android/thread_jandroid.cpp
index afcc7294f2..ba379c8d43 100644
--- a/platform/android/thread_jandroid.cpp
+++ b/platform/android/thread_jandroid.cpp
@@ -30,17 +30,34 @@
#include "thread_jandroid.h"
+#include <android/log.h>
+
#include "core/os/thread.h"
static JavaVM *java_vm = nullptr;
static thread_local JNIEnv *env = nullptr;
+// The logic here need to improve, init_thread/term_tread are designed to work with Thread::callback
+// Calling init_thread from setup_android_thread and get_jni_env to setup an env we're keeping and not detaching
+// could cause issues on app termination.
+//
+// We should be making sure that any thread started calls a nice cleanup function when it's done,
+// especially now that we use many more threads.
+
static void init_thread() {
+ if (env) {
+ // thread never detached! just keep using...
+ return;
+ }
+
java_vm->AttachCurrentThread(&env, nullptr);
}
static void term_thread() {
java_vm->DetachCurrentThread();
+
+ // this is no longer valid, must called init_thread to re-establish
+ env = nullptr;
}
void init_thread_jandroid(JavaVM *p_jvm, JNIEnv *p_env) {
@@ -50,9 +67,17 @@ void init_thread_jandroid(JavaVM *p_jvm, JNIEnv *p_env) {
}
void setup_android_thread() {
- init_thread();
+ if (!env) {
+ // !BAS! see remarks above
+ init_thread();
+ }
}
JNIEnv *get_jni_env() {
+ if (!env) {
+ // !BAS! see remarks above
+ init_thread();
+ }
+
return env;
}
diff --git a/platform/android/vulkan/vulkan_context_android.cpp b/platform/android/vulkan/vulkan_context_android.cpp
index 1bf85f07f1..a031f3beee 100644
--- a/platform/android/vulkan/vulkan_context_android.cpp
+++ b/platform/android/vulkan/vulkan_context_android.cpp
@@ -36,7 +36,7 @@ const char *VulkanContextAndroid::_get_platform_surface_extension() const {
return VK_KHR_ANDROID_SURFACE_EXTENSION_NAME;
}
-int VulkanContextAndroid::window_create(ANativeWindow *p_window, int p_width, int p_height) {
+int VulkanContextAndroid::window_create(ANativeWindow *p_window, DisplayServer::VSyncMode p_vsync_mode, int p_width, int p_height) {
VkAndroidSurfaceCreateInfoKHR createInfo;
createInfo.sType = VK_STRUCTURE_TYPE_ANDROID_SURFACE_CREATE_INFO_KHR;
createInfo.pNext = nullptr;
@@ -49,13 +49,13 @@ int VulkanContextAndroid::window_create(ANativeWindow *p_window, int p_width, in
ERR_FAIL_V_MSG(-1, "vkCreateAndroidSurfaceKHR failed with error " + itos(err));
}
- return _window_create(DisplayServer::MAIN_WINDOW_ID, surface, p_width, p_height);
+ return _window_create(DisplayServer::MAIN_WINDOW_ID, p_vsync_mode, surface, p_width, p_height);
}
-VulkanContextAndroid::VulkanContextAndroid() {
- // TODO: fix validation layers
- use_validation_layers = false;
-}
+bool VulkanContextAndroid::_use_validation_layers() {
+ uint32_t count = 0;
+ _get_preferred_validation_layers(&count, nullptr);
-VulkanContextAndroid::~VulkanContextAndroid() {
+ // On Android, we use validation layers automatically if they were explicitly linked with the app.
+ return count > 0;
}
diff --git a/platform/android/vulkan/vulkan_context_android.h b/platform/android/vulkan/vulkan_context_android.h
index c608f2d665..182ce33c97 100644
--- a/platform/android/vulkan/vulkan_context_android.h
+++ b/platform/android/vulkan/vulkan_context_android.h
@@ -36,13 +36,16 @@
struct ANativeWindow;
class VulkanContextAndroid : public VulkanContext {
- virtual const char *_get_platform_surface_extension() const;
+ virtual const char *_get_platform_surface_extension() const override;
public:
- int window_create(ANativeWindow *p_window, int p_width, int p_height);
+ int window_create(ANativeWindow *p_window, DisplayServer::VSyncMode p_vsync_mode, int p_width, int p_height);
- VulkanContextAndroid();
- ~VulkanContextAndroid();
+ VulkanContextAndroid() = default;
+ ~VulkanContextAndroid() override = default;
+
+protected:
+ bool _use_validation_layers() override;
};
#endif // VULKAN_CONTEXT_ANDROID_H
diff --git a/platform/iphone/SCsub b/platform/iphone/SCsub
index ee7b2f4ab5..58b574a72f 100644
--- a/platform/iphone/SCsub
+++ b/platform/iphone/SCsub
@@ -19,7 +19,6 @@ iphone_lib = [
"godot_view_gesture_recognizer.mm",
"device_metrics.m",
"keyboard_input_view.mm",
- "native_video_view.m",
]
env_ios = env.Clone()
diff --git a/platform/iphone/detect.py b/platform/iphone/detect.py
index 17796beb6f..cf358e0878 100644
--- a/platform/iphone/detect.py
+++ b/platform/iphone/detect.py
@@ -54,7 +54,7 @@ def configure(env):
if env["optimize"] == "speed": # optimize for speed (default)
env.Append(CCFLAGS=["-O2", "-ftree-vectorize", "-fomit-frame-pointer"])
env.Append(LINKFLAGS=["-O2"])
- else: # optimize for size
+ elif env["optimize"] == "size": # optimize for size
env.Append(CCFLAGS=["-Os", "-ftree-vectorize"])
env.Append(LINKFLAGS=["-Os"])
diff --git a/platform/iphone/display_server_iphone.h b/platform/iphone/display_server_iphone.h
index 31a44723a5..6f64130b23 100644
--- a/platform/iphone/display_server_iphone.h
+++ b/platform/iphone/display_server_iphone.h
@@ -67,7 +67,7 @@ class DisplayServerIPhone : public DisplayServer {
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(const String &p_rendering_driver, DisplayServer::WindowMode p_mode, DisplayServer::VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i &p_resolution, Error &r_error);
~DisplayServerIPhone();
public:
@@ -76,7 +76,7 @@ public:
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 DisplayServer *create_func(const String &p_rendering_driver, WindowMode p_mode, DisplayServer::VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i &p_resolution, Error &r_error);
static Vector<String> get_rendering_drivers_func();
// MARK: - Events
@@ -99,7 +99,7 @@ public:
// MARK: Touches
- void touch_press(int p_idx, int p_x, int p_y, bool p_pressed, bool p_doubleclick);
+ void touch_press(int p_idx, int p_x, int p_y, bool p_pressed, bool p_double_click);
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);
@@ -176,6 +176,9 @@ public:
virtual bool can_any_window_draw() const override;
+ virtual void window_set_vsync_mode(DisplayServer::VSyncMode p_vsync_mode, WindowID p_window = MAIN_WINDOW_ID) override;
+ virtual DisplayServer::VSyncMode window_get_vsync_mode(WindowID p_vsync_mode) 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, bool p_multiline, int p_max_length, int p_cursor_start, int p_cursor_end) override;
@@ -190,12 +193,6 @@ public:
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);
};
diff --git a/platform/iphone/display_server_iphone.mm b/platform/iphone/display_server_iphone.mm
index 05dc78bb4d..bd95e2c703 100644
--- a/platform/iphone/display_server_iphone.mm
+++ b/platform/iphone/display_server_iphone.mm
@@ -36,7 +36,6 @@
#import "godot_view.h"
#include "ios.h"
#import "keyboard_input_view.h"
-#import "native_video_view.h"
#include "os_iphone.h"
#import "view_controller.h"
@@ -49,7 +48,7 @@ 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) {
+DisplayServerIPhone::DisplayServerIPhone(const String &p_rendering_driver, WindowMode p_mode, DisplayServer::VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i &p_resolution, Error &r_error) {
rendering_driver = p_rendering_driver;
#if defined(OPENGL_ENABLED)
@@ -109,7 +108,7 @@ DisplayServerIPhone::DisplayServerIPhone(const String &p_rendering_driver, Displ
}
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) {
+ if (context_vulkan->window_create(MAIN_WINDOW_ID, p_vsync_mode, layer, size.width, size.height) != OK) {
memdelete(context_vulkan);
context_vulkan = nullptr;
ERR_FAIL_MSG("Failed to create Vulkan window.");
@@ -136,20 +135,20 @@ DisplayServerIPhone::~DisplayServerIPhone() {
if (rendering_device_vulkan) {
rendering_device_vulkan->finalize();
memdelete(rendering_device_vulkan);
- rendering_device_vulkan = NULL;
+ rendering_device_vulkan = nullptr;
}
if (context_vulkan) {
context_vulkan->window_destroy(MAIN_WINDOW_ID);
memdelete(context_vulkan);
- context_vulkan = NULL;
+ context_vulkan = nullptr;
}
}
#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));
+DisplayServer *DisplayServerIPhone::create_func(const String &p_rendering_driver, WindowMode p_mode, DisplayServer::VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i &p_resolution, Error &r_error) {
+ return memnew(DisplayServerIPhone(p_rendering_driver, p_mode, p_vsync_mode, p_flags, p_resolution, r_error));
}
Vector<String> DisplayServerIPhone::get_rendering_drivers_func() {
@@ -222,10 +221,10 @@ void DisplayServerIPhone::_window_callback(const Callable &p_callable, const Var
// MARK: Touches
-void DisplayServerIPhone::touch_press(int p_idx, int p_x, int p_y, bool p_pressed, bool p_doubleclick) {
+void DisplayServerIPhone::touch_press(int p_idx, int p_x, int p_y, bool p_pressed, bool p_double_click) {
if (!GLOBAL_DEF("debug/disable_touch", false)) {
Ref<InputEventScreenTouch> ev;
- ev.instance();
+ ev.instantiate();
ev->set_index(p_idx);
ev->set_pressed(p_pressed);
@@ -237,7 +236,7 @@ void DisplayServerIPhone::touch_press(int p_idx, int p_x, int p_y, bool p_presse
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.instantiate();
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));
@@ -257,7 +256,7 @@ void DisplayServerIPhone::touches_cancelled(int p_idx) {
void DisplayServerIPhone::key(uint32_t p_key, bool p_pressed) {
Ref<InputEventKey> ev;
- ev.instance();
+ ev.instantiate();
ev->set_echo(false);
ev->set_pressed(p_pressed);
ev->set_keycode(p_key);
@@ -305,7 +304,6 @@ bool DisplayServerIPhone::has_feature(Feature p_feature) const {
// 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:
@@ -569,69 +567,6 @@ 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.utf8().get_data());
- 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.utf8().get_data());
-
- String file_path = ProjectSettings::get_singleton()->globalize_path(p_path);
-
- NSString *filePath = [[NSString alloc] initWithUTF8String:file_path.utf8().get_data()];
- 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.videoView isVideoPlaying];
-}
-
-void DisplayServerIPhone::native_video_pause() {
- if (native_video_is_playing()) {
- [AppDelegate.viewController.videoView pauseVideo];
- }
-}
-
-void DisplayServerIPhone::native_video_unpause() {
- [AppDelegate.viewController.videoView unpauseVideo];
-};
-
-void DisplayServerIPhone::native_video_stop() {
- if (native_video_is_playing()) {
- [AppDelegate.viewController.videoView stopVideo];
- }
-}
-
void DisplayServerIPhone::resize_window(CGSize viewSize) {
Size2i size = Size2i(viewSize.width, viewSize.height) * screen_get_max_scale();
@@ -646,3 +581,19 @@ void DisplayServerIPhone::resize_window(CGSize viewSize) {
Variant resize_rect = Rect2i(Point2i(), size);
_window_callback(window_resize_callback, resize_rect);
}
+
+void DisplayServerIPhone::window_set_vsync_mode(DisplayServer::VSyncMode p_vsync_mode, WindowID p_window) {
+ _THREAD_SAFE_METHOD_
+#if defined(VULKAN_ENABLED)
+ context_vulkan->set_vsync_mode(p_window, p_vsync_mode);
+#endif
+}
+
+DisplayServer::VSyncMode DisplayServerIPhone::window_get_vsync_mode(WindowID p_window) const {
+ _THREAD_SAFE_METHOD_
+#if defined(VULKAN_ENABLED)
+ return context_vulkan->get_vsync_mode(p_window);
+#else
+ return DisplayServer::VSYNC_ENABLED;
+#endif
+}
diff --git a/platform/iphone/export/export.cpp b/platform/iphone/export/export.cpp
index c585c2afbe..f21a14e84b 100644
--- a/platform/iphone/export/export.cpp
+++ b/platform/iphone/export/export.cpp
@@ -31,11 +31,11 @@
#include "export.h"
#include "core/config/project_settings.h"
+#include "core/io/file_access.h"
#include "core/io/image_loader.h"
#include "core/io/marshalls.h"
#include "core/io/resource_saver.h"
#include "core/io/zip_io.h"
-#include "core/os/file_access.h"
#include "core/os/os.h"
#include "core/templates/safe_refcount.h"
#include "core/version.h"
@@ -353,6 +353,8 @@ void EditorExportPlatformIOS::get_export_options(List<ExportOption> *r_options)
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/code_sign_identity_release", PROPERTY_HINT_PLACEHOLDER_TEXT, "iPhone Distribution"), ""));
r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "application/export_method_release", PROPERTY_HINT_ENUM, "App Store,Development,Ad-Hoc,Enterprise"), 0));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "application/targeted_device_family", PROPERTY_HINT_ENUM, "iPhone,iPad,iPhone & iPad"), 2));
+
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/name", PROPERTY_HINT_PLACEHOLDER_TEXT, "Game Name"), ""));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/info"), "Made with Godot Engine"));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/bundle_identifier", PROPERTY_HINT_PLACEHOLDER_TEXT, "com.example.game"), ""));
@@ -365,6 +367,31 @@ void EditorExportPlatformIOS::get_export_options(List<ExportOption> *r_options)
for (int i = 0; i < found_plugins.size(); i++) {
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "plugins/" + found_plugins[i].name), false));
}
+
+ Set<String> plist_keys;
+
+ for (int i = 0; i < found_plugins.size(); i++) {
+ // Editable plugin plist values
+ PluginConfigIOS plugin = found_plugins[i];
+ const String *K = nullptr;
+
+ while ((K = plugin.plist.next(K))) {
+ String key = *K;
+ PluginConfigIOS::PlistItem item = plugin.plist[key];
+ switch (item.type) {
+ case PluginConfigIOS::PlistItemType::STRING_INPUT: {
+ String preset_name = "plugins_plist/" + key;
+ if (!plist_keys.has(preset_name)) {
+ r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, preset_name), item.value));
+ plist_keys.insert(preset_name);
+ }
+ } break;
+ default:
+ continue;
+ }
+ }
+ }
+
plugins_changed.clear();
plugins = found_plugins;
@@ -378,11 +405,6 @@ void EditorExportPlatformIOS::get_export_options(List<ExportOption> *r_options)
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "privacy/microphone_usage_description", PROPERTY_HINT_PLACEHOLDER_TEXT, "Provide a message if you need to use the microphone"), ""));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "privacy/photolibrary_usage_description", PROPERTY_HINT_PLACEHOLDER_TEXT, "Provide a message if you need access to the photo library"), ""));
- r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "orientation/portrait"), true));
- r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "orientation/landscape_left"), true));
- r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "orientation/landscape_right"), true));
- r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "orientation/portrait_upside_down"), true));
-
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "icons/generate_missing"), false));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "required_icons/iphone_120x120", PROPERTY_HINT_FILE, "*.png"), "")); // Home screen on iPhone/iPod Touch with retina display
@@ -396,7 +418,7 @@ void EditorExportPlatformIOS::get_export_options(List<ExportOption> *r_options)
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "optional_icons/spotlight_80x80", PROPERTY_HINT_FILE, "*.png"), "")); // Spotlight on devices with retina display
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "storyboard/use_launch_screen_storyboard"), false));
- r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "storyboard/image_scale_mode", PROPERTY_HINT_ENUM, "Same as Logo,Center,Scale To Fit,Scale To Fill,Scale"), 0));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "storyboard/image_scale_mode", PROPERTY_HINT_ENUM, "Same as Logo,Center,Scale to Fit,Scale to Fill,Scale"), 0));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "storyboard/custom_image@2x", PROPERTY_HINT_FILE, "*.png"), ""));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "storyboard/custom_image@3x", PROPERTY_HINT_FILE, "*.png"), ""));
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "storyboard/use_custom_bg_color"), false));
@@ -453,6 +475,8 @@ void EditorExportPlatformIOS::_fix_config_file(const Ref<EditorExportPreset> &p_
strnew += lines[i].replace("$copyright", p_preset->get("application/copyright")) + "\n";
} else if (lines[i].find("$team_id") != -1) {
strnew += lines[i].replace("$team_id", p_preset->get("application/app_store_team_id")) + "\n";
+ } else if (lines[i].find("$default_build_config") != -1) {
+ strnew += lines[i].replace("$default_build_config", p_debug ? "Debug" : "Release") + "\n";
} else if (lines[i].find("$export_method") != -1) {
int export_method = p_preset->get(p_debug ? "application/export_method_debug" : "application/export_method_release");
strnew += lines[i].replace("$export_method", export_method_string[export_method]) + "\n";
@@ -473,6 +497,20 @@ void EditorExportPlatformIOS::_fix_config_file(const Ref<EditorExportPreset> &p_
strnew += lines[i].replace("$godot_archs", p_config.architectures) + "\n";
} else if (lines[i].find("$linker_flags") != -1) {
strnew += lines[i].replace("$linker_flags", p_config.linker_flags) + "\n";
+ } else if (lines[i].find("$targeted_device_family") != -1) {
+ String xcode_value;
+ switch ((int)p_preset->get("application/targeted_device_family")) {
+ case 0: // iPhone
+ xcode_value = "1";
+ break;
+ case 1: // iPad
+ xcode_value = "2";
+ break;
+ case 2: // iPhone & iPad
+ xcode_value = "1,2";
+ break;
+ }
+ strnew += lines[i].replace("$targeted_device_family", xcode_value) + "\n";
} else if (lines[i].find("$cpp_code") != -1) {
strnew += lines[i].replace("$cpp_code", p_config.cpp_code) + "\n";
} else if (lines[i].find("$docs_in_place") != -1) {
@@ -501,18 +539,39 @@ void EditorExportPlatformIOS::_fix_config_file(const Ref<EditorExportPreset> &p_
strnew += lines[i].replace("$required_device_capabilities", capabilities);
} else if (lines[i].find("$interface_orientations") != -1) {
String orientations;
+ const DisplayServer::ScreenOrientation screen_orientation =
+ DisplayServer::ScreenOrientation(int(GLOBAL_GET("display/window/handheld/orientation")));
- if ((bool)p_preset->get("orientation/portrait")) {
- orientations += "<string>UIInterfaceOrientationPortrait</string>\n";
- }
- if ((bool)p_preset->get("orientation/landscape_left")) {
- orientations += "<string>UIInterfaceOrientationLandscapeLeft</string>\n";
- }
- if ((bool)p_preset->get("orientation/landscape_right")) {
- orientations += "<string>UIInterfaceOrientationLandscapeRight</string>\n";
- }
- if ((bool)p_preset->get("orientation/portrait_upside_down")) {
- orientations += "<string>UIInterfaceOrientationPortraitUpsideDown</string>\n";
+ switch (screen_orientation) {
+ case DisplayServer::SCREEN_LANDSCAPE:
+ orientations += "<string>UIInterfaceOrientationLandscapeLeft</string>\n";
+ break;
+ case DisplayServer::SCREEN_PORTRAIT:
+ orientations += "<string>UIInterfaceOrientationPortrait</string>\n";
+ break;
+ case DisplayServer::SCREEN_REVERSE_LANDSCAPE:
+ orientations += "<string>UIInterfaceOrientationLandscapeRight</string>\n";
+ break;
+ case DisplayServer::SCREEN_REVERSE_PORTRAIT:
+ orientations += "<string>UIInterfaceOrientationPortraitUpsideDown</string>\n";
+ break;
+ case DisplayServer::SCREEN_SENSOR_LANDSCAPE:
+ // Allow both landscape orientations depending on sensor direction.
+ orientations += "<string>UIInterfaceOrientationLandscapeLeft</string>\n";
+ orientations += "<string>UIInterfaceOrientationLandscapeRight</string>\n";
+ break;
+ case DisplayServer::SCREEN_SENSOR_PORTRAIT:
+ // Allow both portrait orientations depending on sensor direction.
+ orientations += "<string>UIInterfaceOrientationPortrait</string>\n";
+ orientations += "<string>UIInterfaceOrientationPortraitUpsideDown</string>\n";
+ break;
+ case DisplayServer::SCREEN_SENSOR:
+ // Allow all screen orientations depending on sensor direction.
+ orientations += "<string>UIInterfaceOrientationLandscapeLeft</string>\n";
+ orientations += "<string>UIInterfaceOrientationLandscapeRight</string>\n";
+ orientations += "<string>UIInterfaceOrientationPortrait</string>\n";
+ orientations += "<string>UIInterfaceOrientationPortraitUpsideDown</string>\n";
+ break;
}
strnew += lines[i].replace("$interface_orientations", orientations);
@@ -786,7 +845,7 @@ Error EditorExportPlatformIOS::_export_loading_screen_file(const Ref<EditorExpor
if (custom_launch_image_2x.length() > 0 && custom_launch_image_3x.length() > 0) {
Ref<Image> image;
String image_path = p_dest_dir.plus_file("splash@2x.png");
- image.instance();
+ image.instantiate();
Error err = image->load(custom_launch_image_2x);
if (err) {
@@ -800,7 +859,7 @@ Error EditorExportPlatformIOS::_export_loading_screen_file(const Ref<EditorExpor
image.unref();
image_path = p_dest_dir.plus_file("splash@3x.png");
- image.instance();
+ image.instantiate();
err = image->load(custom_launch_image_3x);
if (err) {
@@ -817,7 +876,7 @@ Error EditorExportPlatformIOS::_export_loading_screen_file(const Ref<EditorExpor
const String splash_path = ProjectSettings::get_singleton()->get("application/boot_splash/image");
if (!splash_path.is_empty()) {
- splash.instance();
+ splash.instantiate();
const Error err = splash->load(splash_path);
if (err) {
splash.unref();
@@ -1433,13 +1492,28 @@ Error EditorExportPlatformIOS::_export_ios_plugins(const Ref<EditorExportPreset>
while ((K = plugin.plist.next(K))) {
String key = *K;
- String value = plugin.plist[key];
+ PluginConfigIOS::PlistItem item = plugin.plist[key];
+
+ String value;
+
+ switch (item.type) {
+ case PluginConfigIOS::PlistItemType::STRING_INPUT: {
+ String preset_name = "plugins_plist/" + key;
+ String input_value = p_preset->get(preset_name);
+ value = "<string>" + input_value + "</string>";
+ } break;
+ default:
+ value = item.value;
+ break;
+ }
if (key.is_empty() || value.is_empty()) {
continue;
}
- plist_values[key] = value;
+ String plist_key = "<key>" + key + "</key>";
+
+ plist_values[plist_key] = value;
}
// CPP Code
@@ -1466,7 +1540,7 @@ Error EditorExportPlatformIOS::_export_ios_plugins(const Ref<EditorExportPreset>
continue;
}
- p_config_data.plist_content += "<key>" + key + "</key><string>" + value + "</string>\n";
+ p_config_data.plist_content += key + value + "\n";
}
}
@@ -1965,7 +2039,7 @@ bool EditorExportPlatformIOS::can_export(const Ref<EditorExportPreset> &p_preset
EditorExportPlatformIOS::EditorExportPlatformIOS() {
Ref<Image> img = memnew(Image(_iphone_logo));
- logo.instance();
+ logo.instantiate();
logo->create_from_image(img);
plugins_changed.set();
@@ -1980,7 +2054,7 @@ EditorExportPlatformIOS::~EditorExportPlatformIOS() {
void register_iphone_exporter() {
Ref<EditorExportPlatformIOS> platform;
- platform.instance();
+ platform.instantiate();
EditorExport::get_singleton()->add_export_platform(platform);
}
diff --git a/platform/iphone/godot_app_delegate.m b/platform/iphone/godot_app_delegate.m
index 3ce9bffc79..6c433c5c3c 100644
--- a/platform/iphone/godot_app_delegate.m
+++ b/platform/iphone/godot_app_delegate.m
@@ -56,7 +56,7 @@ static NSMutableArray<ApplicationDelegateService *> *services = nil;
[services addObject:service];
}
-// UIApplicationDelegate documantation can be found here: https://developer.apple.com/documentation/uikit/uiapplicationdelegate
+// UIApplicationDelegate documentation can be found here: https://developer.apple.com/documentation/uikit/uiapplicationdelegate
// MARK: Window
diff --git a/platform/iphone/godot_iphone.mm b/platform/iphone/godot_iphone.mm
index 62bc2e6e52..6c3e1eabde 100644
--- a/platform/iphone/godot_iphone.mm
+++ b/platform/iphone/godot_iphone.mm
@@ -50,7 +50,7 @@ int add_path(int p_argc, char **p_args) {
p_args[p_argc++] = (char *)"--path";
p_args[p_argc++] = (char *)[str cStringUsingEncoding:NSUTF8StringEncoding];
- p_args[p_argc] = NULL;
+ p_args[p_argc] = nullptr;
return p_argc;
};
@@ -69,7 +69,7 @@ int add_cmdline(int p_argc, char **p_args) {
p_args[p_argc++] = (char *)[str cStringUsingEncoding:NSUTF8StringEncoding];
};
- p_args[p_argc] = NULL;
+ p_args[p_argc] = nullptr;
return p_argc;
};
diff --git a/platform/iphone/godot_view.mm b/platform/iphone/godot_view.mm
index 887297848e..00a88d79c5 100644
--- a/platform/iphone/godot_view.mm
+++ b/platform/iphone/godot_view.mm
@@ -39,6 +39,7 @@
#import <CoreMotion/CoreMotion.h>
static const int max_touches = 8;
+static const float earth_gravity = 9.80665;
@interface GodotView () {
UITouch *godot_touches[max_touches];
@@ -290,14 +291,14 @@ static const int max_touches = 8;
- (void)initTouches {
for (int i = 0; i < max_touches; i++) {
- godot_touches[i] = NULL;
+ godot_touches[i] = nullptr;
}
}
- (int)getTouchIDForTouch:(UITouch *)p_touch {
int first = -1;
for (int i = 0; i < max_touches; i++) {
- if (first == -1 && godot_touches[i] == NULL) {
+ if (first == -1 && godot_touches[i] == nullptr) {
first = i;
continue;
}
@@ -317,11 +318,11 @@ static const int max_touches = 8;
- (int)removeTouch:(UITouch *)p_touch {
int remaining = 0;
for (int i = 0; i < max_touches; i++) {
- if (godot_touches[i] == NULL) {
+ if (godot_touches[i] == nullptr) {
continue;
}
if (godot_touches[i] == p_touch) {
- godot_touches[i] = NULL;
+ godot_touches[i] = nullptr;
} else {
++remaining;
}
@@ -331,7 +332,7 @@ static const int max_touches = 8;
- (void)clearTouches {
for (int i = 0; i < max_touches; i++) {
- godot_touches[i] = NULL;
+ godot_touches[i] = nullptr;
}
}
@@ -402,10 +403,19 @@ static const int max_touches = 8;
// 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
+ // component. We add them back together.
CMAcceleration gravity = self.motionManager.deviceMotion.gravity;
CMAcceleration acceleration = self.motionManager.deviceMotion.userAcceleration;
+ // To be consistent with Android we convert the unit of measurement from g (Earth's gravity)
+ // to m/s^2.
+ gravity.x *= earth_gravity;
+ gravity.y *= earth_gravity;
+ gravity.z *= earth_gravity;
+ acceleration.x *= earth_gravity;
+ acceleration.y *= earth_gravity;
+ acceleration.z *= earth_gravity;
+
///@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;
diff --git a/platform/iphone/ios.mm b/platform/iphone/ios.mm
index cef03534c4..3430a9cba7 100644
--- a/platform/iphone/ios.mm
+++ b/platform/iphone/ios.mm
@@ -56,16 +56,16 @@ void iOS::alert(const char *p_alert, const char *p_title) {
String iOS::get_model() const {
// [[UIDevice currentDevice] model] only returns "iPad" or "iPhone".
size_t size;
- sysctlbyname("hw.machine", NULL, &size, NULL, 0);
+ sysctlbyname("hw.machine", nullptr, &size, nullptr, 0);
char *model = (char *)malloc(size);
- if (model == NULL) {
+ if (model == nullptr) {
return "";
}
- sysctlbyname("hw.machine", model, &size, NULL, 0);
+ sysctlbyname("hw.machine", model, &size, nullptr, 0);
NSString *platform = [NSString stringWithCString:model encoding:NSUTF8StringEncoding];
free(model);
const char *str = [platform UTF8String];
- return String(str != NULL ? str : "");
+ return String(str != nullptr ? str : "");
}
String iOS::get_rate_url(int p_app_id) const {
diff --git a/platform/iphone/joypad_iphone.mm b/platform/iphone/joypad_iphone.mm
index a0f0eee5d3..45842b38aa 100644
--- a/platform/iphone/joypad_iphone.mm
+++ b/platform/iphone/joypad_iphone.mm
@@ -287,7 +287,7 @@ void JoypadIPhone::start_processing() {
gamepad.dpad.right.isPressed);
};
- Input::JoyAxis jx;
+ Input::JoyAxisValue jx;
jx.min = -1;
if (element == gamepad.leftThumbstick) {
jx.value = gamepad.leftThumbstick.xAxis.value;
diff --git a/platform/iphone/keyboard_input_view.mm b/platform/iphone/keyboard_input_view.mm
index 1408f78e90..0e5a98a3e6 100644
--- a/platform/iphone/keyboard_input_view.mm
+++ b/platform/iphone/keyboard_input_view.mm
@@ -88,13 +88,15 @@
self.text = existingString;
self.previousText = existingString;
+ NSInteger safeStartIndex = MAX(start, 0);
+
NSRange textRange;
// Either a simple cursor or a selection.
if (end > 0) {
- textRange = NSMakeRange(start, end - start);
+ textRange = NSMakeRange(safeStartIndex, end - start);
} else {
- textRange = NSMakeRange(start, 0);
+ textRange = NSMakeRange(safeStartIndex, 0);
}
self.selectedRange = textRange;
diff --git a/platform/iphone/native_video_view.m b/platform/iphone/native_video_view.m
deleted file mode 100644
index f126749600..0000000000
--- a/platform/iphone/native_video_view.m
+++ /dev/null
@@ -1,266 +0,0 @@
-/*************************************************************************/
-/* native_video_view.m */
-/*************************************************************************/
-/* This file is part of: */
-/* GODOT ENGINE */
-/* https://godotengine.org */
-/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 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 "native_video_view.h"
-#import <AVFoundation/AVFoundation.h>
-
-@interface GodotNativeVideoView ()
-
-@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;
-
-@end
-
-@implementation GodotNativeVideoView
-
-- (instancetype)initWithFrame:(CGRect)frame {
- self = [super initWithFrame:frame];
-
- 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 observeVideoAudio];
-}
-
-- (void)layoutSubviews {
- [super layoutSubviews];
-
- self.avPlayerLayer.frame = self.bounds;
-}
-
-- (void)observeVideoAudio {
- 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];
- }
-}
-
-// MARK: Video 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];
- }
-
- 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.bounds];
- [self.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)finishPlayingVideo {
- [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;
-}
-
-- (void)stopVideo {
- [self finishPlayingVideo];
-
- [self removeFromSuperview];
-}
-
-@end
diff --git a/platform/iphone/os_iphone.mm b/platform/iphone/os_iphone.mm
index 51c4da2960..1f08901082 100644
--- a/platform/iphone/os_iphone.mm
+++ b/platform/iphone/os_iphone.mm
@@ -33,9 +33,9 @@
#include "os_iphone.h"
#import "app_delegate.h"
#include "core/config/project_settings.h"
+#include "core/io/dir_access.h"
+#include "core/io/file_access.h"
#include "core/io/file_access_pack.h"
-#include "core/os/dir_access.h"
-#include "core/os/file_access.h"
#include "display_server_iphone.h"
#include "drivers/unix/syslog_logger.h"
#import "godot_view.h"
@@ -301,10 +301,6 @@ void OSIPhone::on_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();
}
}
@@ -319,10 +315,6 @@ void OSIPhone::on_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();
}
}
diff --git a/platform/iphone/plugin/godot_plugin_config.h b/platform/iphone/plugin/godot_plugin_config.h
index f4e30c8349..f9c5d7e51f 100644
--- a/platform/iphone/plugin/godot_plugin_config.h
+++ b/platform/iphone/plugin/godot_plugin_config.h
@@ -70,6 +70,20 @@ struct PluginConfigIOS {
inline static const char *PLIST_SECTION = "plist";
+ enum PlistItemType {
+ UNKNOWN,
+ STRING,
+ INTEGER,
+ BOOLEAN,
+ RAW,
+ STRING_INPUT,
+ };
+
+ struct PlistItem {
+ PlistItemType type;
+ String value;
+ };
+
// Set to true when the config file is properly loaded.
bool valid_config = false;
bool supports_targets = false;
@@ -93,8 +107,10 @@ struct PluginConfigIOS {
Vector<String> linker_flags;
// Optional plist section
- // Supports only string types for now
- HashMap<String, String> plist;
+ // String value is default value.
+ // Currently supports `string`, `boolean`, `integer`, `raw`, `string_input` types
+ // <name>:<type> = <value>
+ HashMap<String, PlistItem> plist;
};
static inline String resolve_local_dependency_path(String plugin_config_dir, String dependency_path) {
@@ -104,7 +120,7 @@ static inline String resolve_local_dependency_path(String plugin_config_dir, Str
return absolute_path;
}
- if (dependency_path.is_abs_path()) {
+ if (dependency_path.is_absolute_path()) {
return dependency_path;
}
@@ -121,7 +137,7 @@ static inline String resolve_system_dependency_path(String dependency_path) {
return absolute_path;
}
- if (dependency_path.is_abs_path()) {
+ if (dependency_path.is_absolute_path()) {
return dependency_path;
}
@@ -218,8 +234,9 @@ static inline uint64_t get_plugin_modification_time(const PluginConfigIOS &plugi
} else {
String file_path = plugin_config.binary.get_base_dir();
String file_name = plugin_config.binary.get_basename().get_file();
- String release_file_name = file_path.plus_file(file_name + ".release.a");
- String debug_file_name = file_path.plus_file(file_name + ".debug.a");
+ String plugin_extension = plugin_config.binary.get_extension();
+ String release_file_name = file_path.plus_file(file_name + ".release." + plugin_extension);
+ String debug_file_name = file_path.plus_file(file_name + ".debug." + plugin_extension);
last_updated = MAX(last_updated, FileAccess::get_modified_time(release_file_name));
last_updated = MAX(last_updated, FileAccess::get_modified_time(debug_file_name));
@@ -235,6 +252,8 @@ static inline PluginConfigIOS load_plugin_config(Ref<ConfigFile> config_file, co
return plugin_config;
}
+ config_file->clear();
+
Error err = config_file->load(path);
if (err != OK) {
@@ -272,13 +291,68 @@ static inline PluginConfigIOS load_plugin_config(Ref<ConfigFile> config_file, co
config_file->get_section_keys(PluginConfigIOS::PLIST_SECTION, &keys);
for (int i = 0; i < keys.size(); i++) {
- String value = config_file->get_value(PluginConfigIOS::PLIST_SECTION, keys[i], String());
+ Vector<String> key_components = keys[i].split(":");
+
+ String key_value = "";
+ PluginConfigIOS::PlistItemType key_type = PluginConfigIOS::PlistItemType::UNKNOWN;
+
+ if (key_components.size() == 1) {
+ key_value = key_components[0];
+ key_type = PluginConfigIOS::PlistItemType::STRING;
+ } else if (key_components.size() == 2) {
+ key_value = key_components[0];
+
+ if (key_components[1].to_lower() == "string") {
+ key_type = PluginConfigIOS::PlistItemType::STRING;
+ } else if (key_components[1].to_lower() == "integer") {
+ key_type = PluginConfigIOS::PlistItemType::INTEGER;
+ } else if (key_components[1].to_lower() == "boolean") {
+ key_type = PluginConfigIOS::PlistItemType::BOOLEAN;
+ } else if (key_components[1].to_lower() == "raw") {
+ key_type = PluginConfigIOS::PlistItemType::RAW;
+ } else if (key_components[1].to_lower() == "string_input") {
+ key_type = PluginConfigIOS::PlistItemType::STRING_INPUT;
+ }
+ }
- if (value.is_empty()) {
+ if (key_value.is_empty() || key_type == PluginConfigIOS::PlistItemType::UNKNOWN) {
continue;
}
- plugin_config.plist[keys[i]] = value;
+ String value;
+
+ switch (key_type) {
+ case PluginConfigIOS::PlistItemType::STRING: {
+ String raw_value = config_file->get_value(PluginConfigIOS::PLIST_SECTION, keys[i], String());
+ value = "<string>" + raw_value + "</string>";
+ } break;
+ case PluginConfigIOS::PlistItemType::INTEGER: {
+ int raw_value = config_file->get_value(PluginConfigIOS::PLIST_SECTION, keys[i], 0);
+ Dictionary value_dictionary;
+ String value_format = "<integer>$value</integer>";
+ value_dictionary["value"] = raw_value;
+ value = value_format.format(value_dictionary, "$_");
+ } break;
+ case PluginConfigIOS::PlistItemType::BOOLEAN:
+ if (config_file->get_value(PluginConfigIOS::PLIST_SECTION, keys[i], false)) {
+ value = "<true/>";
+ } else {
+ value = "<false/>";
+ }
+ break;
+ case PluginConfigIOS::PlistItemType::RAW: {
+ String raw_value = config_file->get_value(PluginConfigIOS::PLIST_SECTION, keys[i], String());
+ value = raw_value;
+ } break;
+ case PluginConfigIOS::PlistItemType::STRING_INPUT: {
+ String raw_value = config_file->get_value(PluginConfigIOS::PLIST_SECTION, keys[i], String());
+ value = raw_value;
+ } break;
+ default:
+ continue;
+ }
+
+ plugin_config.plist[key_value] = PluginConfigIOS::PlistItem{ key_type, value };
}
}
diff --git a/platform/iphone/view_controller.h b/platform/iphone/view_controller.h
index 52fb6fbbf2..400145b8b7 100644
--- a/platform/iphone/view_controller.h
+++ b/platform/iphone/view_controller.h
@@ -37,11 +37,6 @@
@interface ViewController : UIViewController
@property(nonatomic, readonly, strong) GodotView *godotView;
-@property(nonatomic, readonly, strong) GodotNativeVideoView *videoView;
@property(nonatomic, readonly, strong) GodotKeyboardInputView *keyboardView;
-// MARK: Native Video Player
-
-- (BOOL)playVideoAtPath:(NSString *)filePath volume:(float)videoVolume audio:(NSString *)audioTrack subtitle:(NSString *)subtitleTrack;
-
@end
diff --git a/platform/iphone/view_controller.mm b/platform/iphone/view_controller.mm
index 6cef244567..2723ac4706 100644
--- a/platform/iphone/view_controller.mm
+++ b/platform/iphone/view_controller.mm
@@ -34,7 +34,6 @@
#import "godot_view.h"
#import "godot_view_renderer.h"
#import "keyboard_input_view.h"
-#import "native_video_view.h"
#include "os_iphone.h"
#import <AVFoundation/AVFoundation.h>
@@ -43,7 +42,6 @@
@interface ViewController () <GodotViewDelegate>
@property(strong, nonatomic) GodotViewRenderer *renderer;
-@property(strong, nonatomic) GodotNativeVideoView *videoView;
@property(strong, nonatomic) GodotKeyboardInputView *keyboardView;
@property(strong, nonatomic) UIView *godotLoadingOverlay;
@@ -151,10 +149,6 @@
}
- (void)dealloc {
- [self.videoView stopVideo];
-
- self.videoView = nil;
-
self.keyboardView = nil;
self.renderer = nil;
@@ -243,22 +237,4 @@
}
}
-// MARK: Native Video Player
-
-- (BOOL)playVideoAtPath:(NSString *)filePath volume:(float)videoVolume audio:(NSString *)audioTrack subtitle:(NSString *)subtitleTrack {
- // If we are showing some video already, reuse existing view for new video.
- if (self.videoView) {
- return [self.videoView playVideoAtPath:filePath volume:videoVolume audio:audioTrack subtitle:subtitleTrack];
- } else {
- // Create autoresizing view for video playback.
- GodotNativeVideoView *videoView = [[GodotNativeVideoView alloc] initWithFrame:self.view.bounds];
- videoView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
- [self.view addSubview:videoView];
-
- self.videoView = videoView;
-
- return [self.videoView playVideoAtPath:filePath volume:videoVolume audio:audioTrack subtitle:subtitleTrack];
- }
-}
-
@end
diff --git a/platform/iphone/vulkan_context_iphone.h b/platform/iphone/vulkan_context_iphone.h
index 88764e270e..ec6aaf46e8 100644
--- a/platform/iphone/vulkan_context_iphone.h
+++ b/platform/iphone/vulkan_context_iphone.h
@@ -39,7 +39,7 @@ class VulkanContextIPhone : public VulkanContext {
virtual const char *_get_platform_surface_extension() const;
public:
- Error window_create(DisplayServer::WindowID p_window_id, CALayer *p_metal_layer, int p_width, int p_height);
+ Error window_create(DisplayServer::WindowID p_window_id, DisplayServer::VSyncMode p_vsync_mode, 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 b980ae99f0..17f2b167b3 100644
--- a/platform/iphone/vulkan_context_iphone.mm
+++ b/platform/iphone/vulkan_context_iphone.mm
@@ -35,19 +35,19 @@ const char *VulkanContextIPhone::_get_platform_surface_extension() const {
return VK_MVK_IOS_SURFACE_EXTENSION_NAME;
}
-Error VulkanContextIPhone::window_create(DisplayServer::WindowID p_window_id, CALayer *p_metal_layer, int p_width, int p_height) {
+Error VulkanContextIPhone::window_create(DisplayServer::WindowID p_window_id, DisplayServer::VSyncMode p_vsync_mode, CALayer *p_metal_layer, int p_width, int p_height) {
VkIOSSurfaceCreateInfoMVK createInfo;
createInfo.sType = VK_STRUCTURE_TYPE_IOS_SURFACE_CREATE_INFO_MVK;
- createInfo.pNext = NULL;
+ createInfo.pNext = nullptr;
createInfo.flags = 0;
createInfo.pView = (__bridge const void *)p_metal_layer;
VkSurfaceKHR surface;
VkResult err =
- vkCreateIOSSurfaceMVK(_get_instance(), &createInfo, NULL, &surface);
+ vkCreateIOSSurfaceMVK(_get_instance(), &createInfo, nullptr, &surface);
ERR_FAIL_COND_V(err, ERR_CANT_CREATE);
- return _window_create(p_window_id, surface, p_width, p_height);
+ return _window_create(p_window_id, p_vsync_mode, surface, p_width, p_height);
}
VulkanContextIPhone::VulkanContextIPhone() {}
diff --git a/platform/javascript/SCsub b/platform/javascript/SCsub
index 50b61c0db3..62a8660ae4 100644
--- a/platform/javascript/SCsub
+++ b/platform/javascript/SCsub
@@ -6,7 +6,7 @@ javascript_files = [
"audio_driver_javascript.cpp",
"display_server_javascript.cpp",
"http_client_javascript.cpp",
- "javascript_eval.cpp",
+ "javascript_singleton.cpp",
"javascript_main.cpp",
"os_javascript.cpp",
"api/javascript_tools_editor_plugin.cpp",
@@ -17,16 +17,14 @@ sys_env.AddJSLibraries(
[
"js/libs/library_godot_audio.js",
"js/libs/library_godot_display.js",
- "js/libs/library_godot_http_request.js",
+ "js/libs/library_godot_fetch.js",
"js/libs/library_godot_os.js",
"js/libs/library_godot_runtime.js",
]
)
-if env["tools"]:
- sys_env.AddJSLibraries(["js/libs/library_godot_editor_tools.js"])
if env["javascript_eval"]:
- sys_env.AddJSLibraries(["js/libs/library_godot_eval.js"])
+ sys_env.AddJSLibraries(["js/libs/library_godot_javascript_singleton.js"])
for lib in sys_env["JS_LIBS"]:
sys_env.Append(LINKFLAGS=["--js-library", lib])
@@ -47,6 +45,7 @@ if env["gdnative_enabled"]:
sys_env.Append(LINKFLAGS=["-s", "MAIN_MODULE=1"])
sys_env.Append(CCFLAGS=["-s", "EXPORT_ALL=1"])
sys_env.Append(LINKFLAGS=["-s", "EXPORT_ALL=1"])
+ sys_env.Append(LINKFLAGS=["-s", "WARN_ON_UNDEFINED_SYMBOLS=0"])
# Force exporting the standard library (printf, malloc, etc.)
sys_env["ENV"]["EMCC_FORCE_STDLIBS"] = "libc,libc++,libc++abi"
# The main emscripten runtime, with exported standard libraries.
@@ -86,40 +85,6 @@ wrap_list = [
]
js_wrapped = env.Textfile("#bin/godot", [env.File(f) for f in wrap_list], TEXTFILESUFFIX="${PROGSUFFIX}.wrapped.js")
-zip_dir = env.Dir("#bin/.javascript_zip")
-binary_name = "godot.tools" if env["tools"] else "godot"
-out_files = [
- zip_dir.File(binary_name + ".js"),
- zip_dir.File(binary_name + ".wasm"),
- zip_dir.File(binary_name + ".html"),
- zip_dir.File(binary_name + ".audio.worklet.js"),
-]
-html_file = "#misc/dist/html/full-size.html"
-if env["tools"]:
- subst_dict = {"@GODOT_VERSION@": env.GetBuildVersion()}
- html_file = env.Substfile(
- target="#bin/godot${PROGSUFFIX}.html", source="#misc/dist/html/editor.html", SUBST_DICT=subst_dict
- )
-
-in_files = [js_wrapped, build[1], html_file, "#platform/javascript/js/libs/audio.worklet.js"]
-if env["gdnative_enabled"]:
- in_files.append(build[2]) # Runtime
- out_files.append(zip_dir.File(binary_name + ".side.wasm"))
-elif env["threads_enabled"]:
- in_files.append(build[2]) # Worker
- out_files.append(zip_dir.File(binary_name + ".worker.js"))
-
-if env["tools"]:
- in_files.append("#misc/dist/html/logo.svg")
- out_files.append(zip_dir.File("logo.svg"))
- in_files.append("#icon.png")
- out_files.append(zip_dir.File("favicon.png"))
-
-zip_files = env.InstallAs(out_files, in_files)
-env.Zip(
- "#bin/godot",
- zip_files,
- ZIPROOT=zip_dir,
- ZIPSUFFIX="${PROGSUFFIX}${ZIPSUFFIX}",
- ZIPCOMSTR="Archiving $SOURCES as $TARGET",
-)
+# Extra will be the thread worker, or the GDNative side, or None
+extra = build[2] if len(build) > 2 else None
+env.CreateTemplateZip(js_wrapped, build[1], extra)
diff --git a/platform/javascript/api/api.cpp b/platform/javascript/api/api.cpp
index 2f7bde065f..e7c018ba9f 100644
--- a/platform/javascript/api/api.cpp
+++ b/platform/javascript/api/api.cpp
@@ -30,14 +30,15 @@
#include "api.h"
#include "core/config/engine.h"
-#include "javascript_eval.h"
+#include "javascript_singleton.h"
#include "javascript_tools_editor_plugin.h"
static JavaScript *javascript_eval;
void register_javascript_api() {
JavaScriptToolsEditorPlugin::initialize();
- ClassDB::register_virtual_class<JavaScript>();
+ GDREGISTER_VIRTUAL_CLASS(JavaScriptObject);
+ GDREGISTER_VIRTUAL_CLASS(JavaScript);
javascript_eval = memnew(JavaScript);
Engine::get_singleton()->add_singleton(Engine::Singleton("JavaScript", javascript_eval));
}
@@ -61,10 +62,46 @@ JavaScript::~JavaScript() {}
void JavaScript::_bind_methods() {
ClassDB::bind_method(D_METHOD("eval", "code", "use_global_execution_context"), &JavaScript::eval, DEFVAL(false));
+ ClassDB::bind_method(D_METHOD("get_interface", "interface"), &JavaScript::get_interface);
+ ClassDB::bind_method(D_METHOD("create_callback", "callable"), &JavaScript::create_callback);
+ {
+ MethodInfo mi;
+ mi.name = "create_object";
+ mi.arguments.push_back(PropertyInfo(Variant::STRING, "object"));
+ ClassDB::bind_vararg_method(METHOD_FLAGS_DEFAULT, "create_object", &JavaScript::_create_object_bind, mi);
+ }
+ ClassDB::bind_method(D_METHOD("download_buffer", "buffer", "name", "mime"), &JavaScript::download_buffer, DEFVAL("application/octet-stream"));
}
#if !defined(JAVASCRIPT_ENABLED) || !defined(JAVASCRIPT_EVAL_ENABLED)
Variant JavaScript::eval(const String &p_code, bool p_use_global_exec_context) {
return Variant();
}
+
+Ref<JavaScriptObject> JavaScript::get_interface(const String &p_interface) {
+ return Ref<JavaScriptObject>();
+}
+
+Ref<JavaScriptObject> JavaScript::create_callback(const Callable &p_callable) {
+ return Ref<JavaScriptObject>();
+}
+
+Variant JavaScript::_create_object_bind(const Variant **p_args, int p_argcount, Callable::CallError &r_error) {
+ if (p_argcount < 1) {
+ r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS;
+ r_error.argument = 0;
+ return Ref<JavaScriptObject>();
+ }
+ if (p_args[0]->get_type() != Variant::STRING) {
+ r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
+ r_error.argument = 0;
+ r_error.expected = Variant::STRING;
+ return Ref<JavaScriptObject>();
+ }
+ return Ref<JavaScriptObject>();
+}
+#endif
+#if !defined(JAVASCRIPT_ENABLED)
+void JavaScript::download_buffer(Vector<uint8_t> p_arr, const String &p_name, const String &p_mime) {
+}
#endif
diff --git a/platform/android/audio_driver_jandroid.h b/platform/javascript/api/javascript_singleton.h
index 9007fd2f81..9d7a392278 100644
--- a/platform/android/audio_driver_jandroid.h
+++ b/platform/javascript/api/javascript_singleton.h
@@ -1,5 +1,5 @@
/*************************************************************************/
-/* audio_driver_jandroid.h */
+/* javascript_singleton.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
@@ -28,51 +28,41 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#ifndef AUDIO_DRIVER_ANDROID_H
-#define AUDIO_DRIVER_ANDROID_H
+#ifndef JAVASCRIPT_SINGLETON_H
+#define JAVASCRIPT_SINGLETON_H
-#include "servers/audio_server.h"
+#include "core/object/class_db.h"
+#include "core/object/ref_counted.h"
-#include "java_godot_lib_jni.h"
+class JavaScriptObject : public RefCounted {
+private:
+ GDCLASS(JavaScriptObject, RefCounted);
-class AudioDriverAndroid : public AudioDriver {
- static Mutex mutex;
- static AudioDriverAndroid *s_ad;
- static jobject io;
- static jmethodID _init_audio;
- static jmethodID _write_buffer;
- static jmethodID _quit;
- static jmethodID _pause;
- static bool active;
- static bool quit;
-
- static jclass cls;
-
- static jobject audioBuffer;
- static void *audioBufferPinned;
- static int32_t *audioBuffer32;
- static int audioBufferFrames;
- static int mix_rate;
-
-public:
- void set_singleton();
+protected:
+ virtual bool _set(const StringName &p_name, const Variant &p_value) { return false; }
+ virtual bool _get(const StringName &p_name, Variant &r_ret) const { return false; }
+ virtual void _get_property_list(List<PropertyInfo> *p_list) const {}
+};
- virtual const char *get_name() const;
+class JavaScript : public Object {
+private:
+ GDCLASS(JavaScript, Object);
- virtual Error init();
- virtual void start();
- virtual int get_mix_rate() const;
- virtual SpeakerMode get_speaker_mode() const;
- virtual void lock();
- virtual void unlock();
- virtual void finish();
+ static JavaScript *singleton;
- virtual void set_pause(bool p_pause);
+protected:
+ static void _bind_methods();
- static void setup(jobject p_io);
- static void thread_func(JNIEnv *env);
+public:
+ Variant eval(const String &p_code, bool p_use_global_exec_context = false);
+ Ref<JavaScriptObject> get_interface(const String &p_interface);
+ Ref<JavaScriptObject> create_callback(const Callable &p_callable);
+ Variant _create_object_bind(const Variant **p_args, int p_argcount, Callable::CallError &r_error);
+ void download_buffer(Vector<uint8_t> p_arr, const String &p_name, const String &p_mime = "application/octet-stream");
- AudioDriverAndroid();
+ static JavaScript *get_singleton();
+ JavaScript();
+ ~JavaScript();
};
-#endif // AUDIO_DRIVER_ANDROID_H
+#endif // JAVASCRIPT_SINGLETON_H
diff --git a/platform/javascript/api/javascript_tools_editor_plugin.cpp b/platform/javascript/api/javascript_tools_editor_plugin.cpp
index 8355faccc2..54f541f607 100644
--- a/platform/javascript/api/javascript_tools_editor_plugin.cpp
+++ b/platform/javascript/api/javascript_tools_editor_plugin.cpp
@@ -33,15 +33,15 @@
#include "core/config/engine.h"
#include "core/config/project_settings.h"
-#include "core/os/dir_access.h"
-#include "core/os/file_access.h"
+#include "core/io/dir_access.h"
+#include "core/io/file_access.h"
#include "editor/editor_node.h"
#include <emscripten/emscripten.h>
// JavaScript functions defined in library_godot_editor_tools.js
extern "C" {
-extern void godot_js_editor_download_file(const char *p_path, const char *p_name, const char *p_mime);
+extern void godot_js_os_download_buffer(const uint8_t *p_buf, int p_buf_size, const char *p_name, const char *p_mime);
}
static void _javascript_editor_init_callback() {
@@ -65,11 +65,16 @@ void JavaScriptToolsEditorPlugin::_download_zip(Variant p_v) {
FileAccess *src_f;
zlib_filefunc_def io = zipio_create_io_from_file(&src_f);
- zipFile zip = zipOpen2("/tmp/project.zip", APPEND_STATUS_CREATE, NULL, &io);
+ zipFile zip = zipOpen2("/tmp/project.zip", APPEND_STATUS_CREATE, nullptr, &io);
String base_path = resource_path.substr(0, resource_path.rfind("/")) + "/";
_zip_recursive(resource_path, base_path, zip);
- zipClose(zip, NULL);
- godot_js_editor_download_file("/tmp/project.zip", "project.zip", "application/zip");
+ zipClose(zip, nullptr);
+ FileAccess *f = FileAccess::open("/tmp/project.zip", FileAccess::READ);
+ ERR_FAIL_COND_MSG(!f, "Unable to create zip file");
+ Vector<uint8_t> buf;
+ buf.resize(f->get_length());
+ f->get_buffer(buf.ptrw(), buf.size());
+ godot_js_os_download_buffer(buf.ptr(), buf.size(), "project.zip", "application/zip");
}
void JavaScriptToolsEditorPlugin::_zip_file(String p_path, String p_base_path, zipFile p_zip) {
@@ -79,7 +84,7 @@ void JavaScriptToolsEditorPlugin::_zip_file(String p_path, String p_base_path, z
return;
}
Vector<uint8_t> data;
- int len = f->get_len();
+ uint64_t len = f->get_length();
data.resize(len);
f->get_buffer(data.ptrw(), len);
f->close();
@@ -88,12 +93,12 @@ void JavaScriptToolsEditorPlugin::_zip_file(String p_path, String p_base_path, z
String path = p_path.replace_first(p_base_path, "");
zipOpenNewFileInZip(p_zip,
path.utf8().get_data(),
- NULL,
- NULL,
+ nullptr,
+ nullptr,
0,
- NULL,
+ nullptr,
0,
- NULL,
+ nullptr,
Z_DEFLATED,
Z_DEFAULT_COMPRESSION);
zipWriteInFileInZip(p_zip, data.ptr(), data.size());
@@ -116,12 +121,12 @@ void JavaScriptToolsEditorPlugin::_zip_recursive(String p_path, String p_base_pa
String path = cs.replace_first(p_base_path, "") + "/";
zipOpenNewFileInZip(p_zip,
path.utf8().get_data(),
- NULL,
- NULL,
+ nullptr,
+ nullptr,
0,
- NULL,
+ nullptr,
0,
- NULL,
+ nullptr,
Z_DEFLATED,
Z_DEFAULT_COMPRESSION);
zipCloseFileInZip(p_zip);
diff --git a/platform/javascript/detect.py b/platform/javascript/detect.py
index 4297088c09..173b558b6d 100644
--- a/platform/javascript/detect.py
+++ b/platform/javascript/detect.py
@@ -7,7 +7,7 @@ from emscripten_helpers import (
add_js_libraries,
add_js_pre,
add_js_externs,
- get_build_version,
+ create_template_zip,
)
from methods import get_compiler_version
from SCons.Util import WhereIs
@@ -29,7 +29,7 @@ def get_opts():
from SCons.Variables import BoolVariable
return [
- ("initial_memory", "Initial WASM memory (in MiB)", 16),
+ ("initial_memory", "Initial WASM memory (in MiB)", 32),
BoolVariable("use_assertions", "Use Emscripten runtime assertions", False),
BoolVariable("use_thinlto", "Use ThinLTO", False),
BoolVariable("use_ubsan", "Use Emscripten undefined behavior sanitizer (UBSAN)", False),
@@ -38,7 +38,7 @@ def get_opts():
BoolVariable("use_safe_heap", "Use Emscripten SAFE_HEAP sanitizer", False),
# eval() can be a security concern, so it can be disabled.
BoolVariable("javascript_eval", "Enable JavaScript eval interface", True),
- BoolVariable("threads_enabled", "Enable WebAssembly Threads support (limited browser support)", False),
+ BoolVariable("threads_enabled", "Enable WebAssembly Threads support (limited browser support)", True),
BoolVariable("gdnative_enabled", "Enable WebAssembly GDNative support (produces bigger binaries)", False),
BoolVariable("use_closure_compiler", "Use closure compiler to minimize JavaScript code", False),
]
@@ -53,6 +53,7 @@ def get_flags():
# in this platform. For the available networking methods, the browser
# manages TLS.
("module_mbedtls_enabled", False),
+ ("vulkan", False),
]
@@ -64,21 +65,21 @@ def configure(env):
sys.exit(255)
## Build type
- if env["target"] == "release":
+ if env["target"].startswith("release"):
# Use -Os to prioritize optimizing for reduced file size. This is
# particularly valuable for the web platform because it directly
# decreases download time.
# -Os reduces file size by around 5 MiB over -O3. -Oz only saves about
# 100 KiB over -Os, which does not justify the negative impact on
# run-time performance.
- env.Append(CCFLAGS=["-Os"])
- env.Append(LINKFLAGS=["-Os"])
- elif env["target"] == "release_debug":
- env.Append(CCFLAGS=["-Os"])
- env.Append(LINKFLAGS=["-Os"])
- env.Append(CPPDEFINES=["DEBUG_ENABLED"])
- # Retain function names for backtraces at the cost of file size.
- env.Append(LINKFLAGS=["--profiling-funcs"])
+ if env["optimize"] != "none":
+ env.Append(CCFLAGS=["-Os"])
+ env.Append(LINKFLAGS=["-Os"])
+
+ if env["target"] == "release_debug":
+ env.Append(CPPDEFINES=["DEBUG_ENABLED"])
+ # Retain function names for backtraces at the cost of file size.
+ env.Append(LINKFLAGS=["--profiling-funcs"])
else: # "debug"
env.Append(CPPDEFINES=["DEBUG_ENABLED"])
env.Append(CCFLAGS=["-O1", "-g"])
@@ -95,8 +96,9 @@ def configure(env):
if env["initial_memory"] < 64:
print("Editor build requires at least 64MiB of initial memory. Forcing it.")
env["initial_memory"] = 64
- elif env["builtin_icu"]:
env.Append(CCFLAGS=["-frtti"])
+ elif env["builtin_icu"]:
+ env.Append(CCFLAGS=["-fno-exceptions", "-frtti"])
else:
# Disable exceptions and rtti on non-tools (template) builds
# These flags help keep the file size down.
@@ -128,7 +130,6 @@ def configure(env):
env.Append(CCFLAGS=["-fsanitize=leak"])
env.Append(LINKFLAGS=["-fsanitize=leak"])
if env["use_safe_heap"]:
- env.Append(CCFLAGS=["-s", "SAFE_HEAP=1"])
env.Append(LINKFLAGS=["-s", "SAFE_HEAP=1"])
# Closure compiler
@@ -147,12 +148,12 @@ def configure(env):
env.AddMethod(add_js_pre, "AddJSPre")
env.AddMethod(add_js_externs, "AddJSExterns")
- # Add method for getting build version string.
- env.AddMethod(get_build_version, "GetBuildVersion")
-
# Add method that joins/compiles our Engine files.
env.AddMethod(create_engine_file, "CreateEngineFile")
+ # Add method for creating the final zip file
+ env.AddMethod(create_template_zip, "CreateTemplateZip")
+
# Closure compiler extern and support for ecmascript specs (const, let, etc).
env["ENV"]["EMCC_CLOSURE_ARGS"] = "--language_in ECMASCRIPT6"
@@ -174,7 +175,7 @@ def configure(env):
# Program() output consists of multiple files, so specify suffixes manually at builder.
env["PROGSUFFIX"] = ""
env["LIBPREFIX"] = "lib"
- env["LIBSUFFIX"] = ".bc"
+ env["LIBSUFFIX"] = ".a"
env["LIBPREFIXES"] = ["$LIBPREFIX"]
env["LIBSUFFIXES"] = ["$LIBSUFFIX"]
@@ -228,8 +229,8 @@ def configure(env):
# Allow use to take control of swapping WebGL buffers.
env.Append(LINKFLAGS=["-s", "OFFSCREEN_FRAMEBUFFER=1"])
- # callMain for manual start.
- env.Append(LINKFLAGS=["-s", "EXTRA_EXPORTED_RUNTIME_METHODS=['callMain','cwrap']"])
+ # callMain for manual start, cwrap for the mono version.
+ env.Append(LINKFLAGS=["-s", "EXPORTED_RUNTIME_METHODS=['callMain','cwrap']"])
# Add code that allow exiting runtime.
env.Append(LINKFLAGS=["-s", "EXIT_RUNTIME=1"])
@@ -238,6 +239,6 @@ def configure(env):
env.Append(
LINKFLAGS=[
"-s",
- "EXPORTED_FUNCTIONS=['_main', '_emscripten_webgl_get_current_context', '_emscripten_webgl_commit_frame', '_emscripten_webgl_create_context']",
+ "EXPORTED_FUNCTIONS=['_main', '_emscripten_webgl_get_current_context']",
]
)
diff --git a/platform/javascript/display_server_javascript.cpp b/platform/javascript/display_server_javascript.cpp
index a605f22e16..5e2b089c7f 100644
--- a/platform/javascript/display_server_javascript.cpp
+++ b/platform/javascript/display_server_javascript.cpp
@@ -30,8 +30,8 @@
#include "platform/javascript/display_server_javascript.h"
-#include "drivers/dummy/rasterizer_dummy.h"
#include "platform/javascript/os_javascript.h"
+#include "servers/rendering/rasterizer_dummy.h"
#include <emscripten.h>
#include <png.h>
@@ -120,15 +120,15 @@ void DisplayServerJavaScript::request_quit_callback() {
template <typename T>
void DisplayServerJavaScript::dom2godot_mod(T *emscripten_event_ptr, Ref<InputEventWithModifiers> godot_event) {
- godot_event->set_shift(emscripten_event_ptr->shiftKey);
- godot_event->set_alt(emscripten_event_ptr->altKey);
- godot_event->set_control(emscripten_event_ptr->ctrlKey);
- godot_event->set_metakey(emscripten_event_ptr->metaKey);
+ godot_event->set_shift_pressed(emscripten_event_ptr->shiftKey);
+ godot_event->set_alt_pressed(emscripten_event_ptr->altKey);
+ godot_event->set_ctrl_pressed(emscripten_event_ptr->ctrlKey);
+ godot_event->set_meta_pressed(emscripten_event_ptr->metaKey);
}
Ref<InputEventKey> DisplayServerJavaScript::setup_key_event(const EmscriptenKeyboardEvent *emscripten_event) {
Ref<InputEventKey> ev;
- ev.instance();
+ ev.instantiate();
ev->set_echo(emscripten_event->repeat);
dom2godot_mod(emscripten_event, ev);
ev->set_keycode(dom_code2godot_scancode(emscripten_event->code, emscripten_event->key, false));
@@ -181,7 +181,7 @@ EM_BOOL DisplayServerJavaScript::mouse_button_callback(int p_event_type, const E
DisplayServerJavaScript *display = get_singleton();
Ref<InputEventMouseButton> ev;
- ev.instance();
+ ev.instantiate();
ev->set_pressed(p_event_type == EMSCRIPTEN_EVENT_MOUSEDOWN);
ev->set_position(compute_position_in_canvas(p_event->clientX, p_event->clientY));
ev->set_global_position(ev->get_position());
@@ -189,19 +189,19 @@ EM_BOOL DisplayServerJavaScript::mouse_button_callback(int p_event_type, const E
switch (p_event->button) {
case DOM_BUTTON_LEFT:
- ev->set_button_index(BUTTON_LEFT);
+ ev->set_button_index(MOUSE_BUTTON_LEFT);
break;
case DOM_BUTTON_MIDDLE:
- ev->set_button_index(BUTTON_MIDDLE);
+ ev->set_button_index(MOUSE_BUTTON_MIDDLE);
break;
case DOM_BUTTON_RIGHT:
- ev->set_button_index(BUTTON_RIGHT);
+ ev->set_button_index(MOUSE_BUTTON_RIGHT);
break;
case DOM_BUTTON_XBUTTON1:
- ev->set_button_index(BUTTON_XBUTTON1);
+ ev->set_button_index(MOUSE_BUTTON_XBUTTON1);
break;
case DOM_BUTTON_XBUTTON2:
- ev->set_button_index(BUTTON_XBUTTON2);
+ ev->set_button_index(MOUSE_BUTTON_XBUTTON2);
break;
default:
return false;
@@ -215,14 +215,14 @@ EM_BOOL DisplayServerJavaScript::mouse_button_callback(int p_event_type, const E
display->last_click_ms = 0;
display->last_click_pos = Point2(-100, -100);
display->last_click_button_index = -1;
- ev->set_doubleclick(true);
+ ev->set_double_click(true);
}
} else {
display->last_click_button_index = ev->get_button_index();
}
- if (!ev->is_doubleclick()) {
+ if (!ev->is_double_click()) {
display->last_click_ms += diff;
display->last_click_pos = ev->get_position();
}
@@ -230,7 +230,7 @@ EM_BOOL DisplayServerJavaScript::mouse_button_callback(int p_event_type, const E
Input *input = Input::get_singleton();
int mask = input->get_mouse_button_mask();
- int button_flag = 1 << (ev->get_button_index() - 1);
+ MouseButton button_flag = MouseButton(1 << (ev->get_button_index() - 1));
if (ev->is_pressed()) {
// Since the event is consumed, focus manually. The containing iframe,
// if exists, may not have focus yet, so focus even if already focused.
@@ -261,7 +261,7 @@ EM_BOOL DisplayServerJavaScript::mousemove_callback(int p_event_type, const Emsc
return false;
Ref<InputEventMouseMotion> ev;
- ev.instance();
+ ev.instantiate();
dom2godot_mod(p_event, ev);
ev->set_button_mask(input_mask);
@@ -341,7 +341,7 @@ void DisplayServerJavaScript::cursor_set_custom_image(const RES &p_cursor, Curso
Rect2 atlas_rect;
if (texture.is_valid()) {
- image = texture->get_data();
+ image = texture->get_image();
}
if (!image.is_valid() && atlas_texture.is_valid()) {
@@ -364,7 +364,7 @@ void DisplayServerJavaScript::cursor_set_custom_image(const RES &p_cursor, Curso
ERR_FAIL_COND(texture_size.width > 256 || texture_size.height > 256);
ERR_FAIL_COND(p_hotspot.x > texture_size.width || p_hotspot.y > texture_size.height);
- image = texture->get_data();
+ image = texture->get_image();
ERR_FAIL_COND(!image.is_valid());
@@ -399,7 +399,7 @@ void DisplayServerJavaScript::cursor_set_custom_image(const RES &p_cursor, Curso
godot_js_display_cursor_set_custom_shape(godot2dom_cursor(p_shape), png.ptr(), len, p_hotspot.x, p_hotspot.y);
} else {
- godot_js_display_cursor_set_custom_shape(godot2dom_cursor(p_shape), NULL, 0, 0, 0);
+ godot_js_display_cursor_set_custom_shape(godot2dom_cursor(p_shape), nullptr, 0, 0, 0);
}
cursor_set_shape(cursor_shape);
@@ -407,9 +407,10 @@ void DisplayServerJavaScript::cursor_set_custom_image(const RES &p_cursor, Curso
// Mouse mode
void DisplayServerJavaScript::mouse_set_mode(MouseMode p_mode) {
- ERR_FAIL_COND_MSG(p_mode == MOUSE_MODE_CONFINED, "MOUSE_MODE_CONFINED is not supported for the HTML5 platform.");
- if (p_mode == mouse_get_mode())
+ ERR_FAIL_COND_MSG(p_mode == MOUSE_MODE_CONFINED || p_mode == MOUSE_MODE_CONFINED_HIDDEN, "MOUSE_MODE_CONFINED is not supported for the HTML5 platform.");
+ if (p_mode == mouse_get_mode()) {
return;
+ }
if (p_mode == MOUSE_MODE_VISIBLE) {
godot_js_display_cursor_set_visible(1);
@@ -451,23 +452,23 @@ EM_BOOL DisplayServerJavaScript::wheel_callback(int p_event_type, const Emscript
Input *input = Input::get_singleton();
Ref<InputEventMouseButton> ev;
- ev.instance();
+ ev.instantiate();
ev->set_position(input->get_mouse_position());
ev->set_global_position(ev->get_position());
- ev->set_shift(input->is_key_pressed(KEY_SHIFT));
- ev->set_alt(input->is_key_pressed(KEY_ALT));
- ev->set_control(input->is_key_pressed(KEY_CONTROL));
- ev->set_metakey(input->is_key_pressed(KEY_META));
+ ev->set_shift_pressed(input->is_key_pressed(KEY_SHIFT));
+ ev->set_alt_pressed(input->is_key_pressed(KEY_ALT));
+ ev->set_ctrl_pressed(input->is_key_pressed(KEY_CTRL));
+ ev->set_meta_pressed(input->is_key_pressed(KEY_META));
if (p_event->deltaY < 0)
- ev->set_button_index(BUTTON_WHEEL_UP);
+ ev->set_button_index(MOUSE_BUTTON_WHEEL_UP);
else if (p_event->deltaY > 0)
- ev->set_button_index(BUTTON_WHEEL_DOWN);
+ ev->set_button_index(MOUSE_BUTTON_WHEEL_DOWN);
else if (p_event->deltaX > 0)
- ev->set_button_index(BUTTON_WHEEL_LEFT);
+ ev->set_button_index(MOUSE_BUTTON_WHEEL_LEFT);
else if (p_event->deltaX < 0)
- ev->set_button_index(BUTTON_WHEEL_RIGHT);
+ ev->set_button_index(MOUSE_BUTTON_WHEEL_RIGHT);
else
return false;
@@ -477,11 +478,11 @@ EM_BOOL DisplayServerJavaScript::wheel_callback(int p_event_type, const Emscript
int button_flag = 1 << (ev->get_button_index() - 1);
ev->set_pressed(true);
- ev->set_button_mask(input->get_mouse_button_mask() | button_flag);
+ ev->set_button_mask(MouseButton(input->get_mouse_button_mask() | button_flag));
input->parse_input_event(ev);
ev->set_pressed(false);
- ev->set_button_mask(input->get_mouse_button_mask() & ~button_flag);
+ ev->set_button_mask(MouseButton(input->get_mouse_button_mask() & ~button_flag));
input->parse_input_event(ev);
return true;
@@ -491,7 +492,7 @@ EM_BOOL DisplayServerJavaScript::wheel_callback(int p_event_type, const Emscript
EM_BOOL DisplayServerJavaScript::touch_press_callback(int p_event_type, const EmscriptenTouchEvent *p_event, void *p_user_data) {
DisplayServerJavaScript *display = get_singleton();
Ref<InputEventScreenTouch> ev;
- ev.instance();
+ ev.instantiate();
int lowest_id_index = -1;
for (int i = 0; i < p_event->numTouches; ++i) {
const EmscriptenTouchPoint &touch = p_event->touches[i];
@@ -513,7 +514,7 @@ EM_BOOL DisplayServerJavaScript::touch_press_callback(int p_event_type, const Em
EM_BOOL DisplayServerJavaScript::touchmove_callback(int p_event_type, const EmscriptenTouchEvent *p_event, void *p_user_data) {
DisplayServerJavaScript *display = get_singleton();
Ref<InputEventScreenDrag> ev;
- ev.instance();
+ ev.instantiate();
int lowest_id_index = -1;
for (int i = 0; i < p_event->numTouches; ++i) {
const EmscriptenTouchPoint &touch = p_event->touches[i];
@@ -536,6 +537,43 @@ bool DisplayServerJavaScript::screen_is_touchscreen(int p_screen) const {
return godot_js_display_touchscreen_is_available();
}
+// Virtual Keyboard
+void DisplayServerJavaScript::vk_input_text_callback(const char *p_text, int p_cursor) {
+ DisplayServerJavaScript *ds = DisplayServerJavaScript::get_singleton();
+ if (!ds || ds->input_text_callback.is_null()) {
+ return;
+ }
+ // Call input_text
+ Variant event = String(p_text);
+ Variant *eventp = &event;
+ Variant ret;
+ Callable::CallError ce;
+ ds->input_text_callback.call((const Variant **)&eventp, 1, ret, ce);
+ // Insert key right to reach position.
+ Input *input = Input::get_singleton();
+ Ref<InputEventKey> k;
+ for (int i = 0; i < p_cursor; i++) {
+ k.instantiate();
+ k->set_pressed(true);
+ k->set_echo(false);
+ k->set_keycode(KEY_RIGHT);
+ input->parse_input_event(k);
+ k.instantiate();
+ k->set_pressed(false);
+ k->set_echo(false);
+ k->set_keycode(KEY_RIGHT);
+ input->parse_input_event(k);
+ }
+}
+
+void DisplayServerJavaScript::virtual_keyboard_show(const String &p_existing_text, const Rect2 &p_screen_rect, bool p_multiline, int p_max_input_length, int p_cursor_start, int p_cursor_end) {
+ godot_js_display_vk_show(p_existing_text.utf8().get_data(), p_multiline, p_cursor_start, p_cursor_end);
+}
+
+void DisplayServerJavaScript::virtual_keyboard_hide() {
+ godot_js_display_vk_hide();
+}
+
// Gamepad
void DisplayServerJavaScript::gamepad_callback(int p_index, int p_connected, const char *p_id, const char *p_guid) {
Input *input = Input::get_singleton();
@@ -564,20 +602,20 @@ void DisplayServerJavaScript::process_joypads() {
// Buttons 6 and 7 in the standard mapping need to be
// axis to be handled as JOY_AXIS_TRIGGER by Godot.
if (s_standard && (b == 6 || b == 7)) {
- Input::JoyAxis joy_axis;
+ Input::JoyAxisValue joy_axis;
joy_axis.min = 0;
joy_axis.value = value;
- int a = b == 6 ? JOY_AXIS_TRIGGER_LEFT : JOY_AXIS_TRIGGER_RIGHT;
+ JoyAxis a = b == 6 ? JOY_AXIS_TRIGGER_LEFT : JOY_AXIS_TRIGGER_RIGHT;
input->joy_axis(idx, a, joy_axis);
} else {
- input->joy_button(idx, b, value);
+ input->joy_button(idx, (JoyButton)b, value);
}
}
for (int a = 0; a < s_axes_num; a++) {
- Input::JoyAxis joy_axis;
+ Input::JoyAxisValue joy_axis;
joy_axis.min = -1;
joy_axis.value = s_axes[a];
- input->joy_axis(idx, a, joy_axis);
+ input->joy_axis(idx, (JoyAxis)a, joy_axis);
}
}
}
@@ -672,18 +710,18 @@ void DisplayServerJavaScript::_dispatch_input_event(const Ref<InputEvent> &p_eve
}
}
-DisplayServer *DisplayServerJavaScript::create_func(const String &p_rendering_driver, DisplayServer::WindowMode p_mode, uint32_t p_flags, const Vector2i &p_resolution, Error &r_error) {
- return memnew(DisplayServerJavaScript(p_rendering_driver, p_mode, p_flags, p_resolution, r_error));
+DisplayServer *DisplayServerJavaScript::create_func(const String &p_rendering_driver, WindowMode p_window_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Size2i &p_resolution, Error &r_error) {
+ return memnew(DisplayServerJavaScript(p_rendering_driver, p_window_mode, p_vsync_mode, p_flags, p_resolution, r_error));
}
-DisplayServerJavaScript::DisplayServerJavaScript(const String &p_rendering_driver, WindowMode p_mode, uint32_t p_flags, const Vector2i &p_resolution, Error &r_error) {
+DisplayServerJavaScript::DisplayServerJavaScript(const String &p_rendering_driver, WindowMode p_window_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Size2i &p_resolution, Error &r_error) {
r_error = OK; // Always succeeds for now.
// Ensure the canvas ID.
godot_js_config_canvas_id_get(canvas_id, 256);
// Handle contextmenu, webglcontextlost
- godot_js_display_setup_canvas(p_resolution.x, p_resolution.y, p_mode == WINDOW_MODE_FULLSCREEN);
+ godot_js_display_setup_canvas(p_resolution.x, p_resolution.y, p_window_mode == WINDOW_MODE_FULLSCREEN, OS::get_singleton()->is_hidpi_allowed() ? 1 : 0);
// Check if it's windows.
swap_cancel_ok = godot_js_display_is_swap_ok_cancel() == 1;
@@ -734,8 +772,8 @@ DisplayServerJavaScript::DisplayServerJavaScript(const String &p_rendering_drive
#define SET_EM_CALLBACK(target, ev, cb) \
result = emscripten_set_##ev##_callback(target, nullptr, true, &cb); \
EM_CHECK(ev)
-#define SET_EM_WINDOW_CALLBACK(ev, cb) \
- result = emscripten_set_##ev##_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, NULL, false, &cb); \
+#define SET_EM_WINDOW_CALLBACK(ev, cb) \
+ result = emscripten_set_##ev##_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, nullptr, false, &cb); \
EM_CHECK(ev)
// These callbacks from Emscripten's html5.h suffice to access most
// JavaScript APIs.
@@ -764,6 +802,7 @@ DisplayServerJavaScript::DisplayServerJavaScript(const String &p_rendering_drive
godot_js_display_paste_cb(update_clipboard_callback);
godot_js_display_drop_files_cb(drop_files_js_callback);
godot_js_display_gamepad_cb(&DisplayServerJavaScript::gamepad_callback);
+ godot_js_display_vk_cb(&vk_input_text_callback);
Input::get_singleton()->set_event_dispatch_function(_dispatch_input_event);
}
@@ -789,11 +828,11 @@ bool DisplayServerJavaScript::has_feature(Feature p_feature) const {
//case FEATURE_MOUSE_WARP:
//case FEATURE_NATIVE_DIALOG:
//case FEATURE_NATIVE_ICON:
- //case FEATURE_NATIVE_VIDEO:
//case FEATURE_WINDOW_TRANSPARENCY:
//case FEATURE_KEEP_SCREEN_ON:
//case FEATURE_ORIENTATION:
- //case FEATURE_VIRTUAL_KEYBOARD:
+ case FEATURE_VIRTUAL_KEYBOARD:
+ return godot_js_display_vk_available() != 0;
default:
return false;
}
@@ -866,7 +905,7 @@ void DisplayServerJavaScript::window_set_input_event_callback(const Callable &p_
}
void DisplayServerJavaScript::window_set_input_text_callback(const Callable &p_callable, WindowID p_window) {
- input_text_callback = p_callable; // TODO unused... do I need this?
+ input_text_callback = p_callable;
}
void DisplayServerJavaScript::window_set_drop_files_callback(const Callable &p_callable, WindowID p_window) {
diff --git a/platform/javascript/display_server_javascript.h b/platform/javascript/display_server_javascript.h
index 47e25ab2a0..3cad7e16a2 100644
--- a/platform/javascript/display_server_javascript.h
+++ b/platform/javascript/display_server_javascript.h
@@ -75,6 +75,8 @@ private:
static EM_BOOL keypress_callback(int p_event_type, const EmscriptenKeyboardEvent *p_event, void *p_user_data);
static EM_BOOL keyup_callback(int p_event_type, const EmscriptenKeyboardEvent *p_event, void *p_user_data);
+ static void vk_input_text_callback(const char *p_text, int p_cursor);
+
static EM_BOOL mousemove_callback(int p_event_type, const EmscriptenMouseEvent *p_event, void *p_user_data);
static EM_BOOL mouse_button_callback(int p_event_type, const EmscriptenMouseEvent *p_event, void *p_user_data);
@@ -87,7 +89,7 @@ private:
void process_joypads();
static Vector<String> get_rendering_drivers_func();
- static DisplayServer *create_func(const String &p_rendering_driver, DisplayServer::WindowMode p_mode, uint32_t p_flags, const Vector2i &p_resolution, Error &r_error);
+ static DisplayServer *create_func(const String &p_rendering_driver, WindowMode p_window_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i &p_resolution, Error &r_error);
static void _dispatch_input_event(const Ref<InputEvent> &p_event);
@@ -135,6 +137,9 @@ public:
int screen_get_dpi(int p_screen = SCREEN_OF_MAIN_WINDOW) const override;
float screen_get_scale(int p_screen = SCREEN_OF_MAIN_WINDOW) const override;
+ void virtual_keyboard_show(const String &p_existing_text, const Rect2 &p_screen_rect = Rect2(), bool p_multiline = false, int p_max_input_length = -1, int p_cursor_start = -1, int p_cursor_end = -1) override;
+ void virtual_keyboard_hide() override;
+
// windows
Vector<DisplayServer::WindowID> get_window_list() const override;
WindowID get_window_at_screen_position(const Point2i &p_position) const override;
@@ -196,7 +201,7 @@ public:
void swap_buffers() override;
static void register_javascript_driver();
- DisplayServerJavaScript(const String &p_rendering_driver, WindowMode p_mode, uint32_t p_flags, const Vector2i &p_resolution, Error &r_error);
+ DisplayServerJavaScript(const String &p_rendering_driver, WindowMode p_window_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Size2i &p_resolution, Error &r_error);
~DisplayServerJavaScript();
};
diff --git a/platform/javascript/dom_keys.inc b/platform/javascript/dom_keys.inc
index 7902efafe0..69340ff58c 100644
--- a/platform/javascript/dom_keys.inc
+++ b/platform/javascript/dom_keys.inc
@@ -159,8 +159,8 @@ int dom_code2godot_scancode(EM_UTF8 const p_code[32], EM_UTF8 const p_key[32], b
DOM2GODOT("Backspace", BACKSPACE);
DOM2GODOT("CapsLock", CAPSLOCK);
DOM2GODOT("ContextMenu", MENU);
- DOM2GODOT("ControlLeft", CONTROL);
- DOM2GODOT("ControlRight", CONTROL);
+ DOM2GODOT("ControlLeft", CTRL);
+ DOM2GODOT("ControlRight", CTRL);
DOM2GODOT("Enter", ENTER);
DOM2GODOT("MetaLeft", SUPER_L);
DOM2GODOT("MetaRight", SUPER_R);
diff --git a/platform/javascript/emscripten_helpers.py b/platform/javascript/emscripten_helpers.py
index d08555916b..ab98838e20 100644
--- a/platform/javascript/emscripten_helpers.py
+++ b/platform/javascript/emscripten_helpers.py
@@ -1,4 +1,4 @@
-import os
+import os, json
from SCons.Util import WhereIs
@@ -15,13 +15,17 @@ def run_closure_compiler(target, source, env, for_signature):
return " ".join(cmd)
-def get_build_version(env):
+def get_build_version():
import version
name = "custom_build"
if os.getenv("BUILD_NAME") != None:
name = os.getenv("BUILD_NAME")
- return "%d.%d.%d.%s.%s" % (version.major, version.minor, version.patch, version.status, name)
+ v = "%d.%d" % (version.major, version.minor)
+ if version.patch > 0:
+ v += ".%d" % version.patch
+ v += ".%s.%s" % (version.status, name)
+ return v
def create_engine_file(env, target, source, externs):
@@ -30,6 +34,85 @@ def create_engine_file(env, target, source, externs):
return env.Textfile(target, [env.File(s) for s in source])
+def create_template_zip(env, js, wasm, extra):
+ binary_name = "godot.tools" if env["tools"] else "godot"
+ zip_dir = env.Dir("#bin/.javascript_zip")
+ in_files = [
+ js,
+ wasm,
+ "#platform/javascript/js/libs/audio.worklet.js",
+ ]
+ out_files = [
+ zip_dir.File(binary_name + ".js"),
+ zip_dir.File(binary_name + ".wasm"),
+ zip_dir.File(binary_name + ".audio.worklet.js"),
+ ]
+ # GDNative/Threads specific
+ if env["gdnative_enabled"]:
+ in_files.append(extra) # Runtime
+ out_files.append(zip_dir.File(binary_name + ".side.wasm"))
+ elif env["threads_enabled"]:
+ in_files.append(extra) # Worker
+ out_files.append(zip_dir.File(binary_name + ".worker.js"))
+
+ service_worker = "#misc/dist/html/service-worker.js"
+ if env["tools"]:
+ # HTML
+ html = "#misc/dist/html/editor.html"
+ cache = [
+ "godot.tools.html",
+ "offline.html",
+ "godot.tools.js",
+ "godot.tools.worker.js",
+ "godot.tools.audio.worklet.js",
+ "logo.svg",
+ "favicon.png",
+ ]
+ opt_cache = ["godot.tools.wasm"]
+ subst_dict = {
+ "@GODOT_VERSION@": get_build_version(),
+ "@GODOT_NAME@": "GodotEngine",
+ "@GODOT_CACHE@": json.dumps(cache),
+ "@GODOT_OPT_CACHE@": json.dumps(opt_cache),
+ "@GODOT_OFFLINE_PAGE@": "offline.html",
+ }
+ html = env.Substfile(target="#bin/godot${PROGSUFFIX}.html", source=html, SUBST_DICT=subst_dict)
+ in_files.append(html)
+ out_files.append(zip_dir.File(binary_name + ".html"))
+ # And logo/favicon
+ in_files.append("#misc/dist/html/logo.svg")
+ out_files.append(zip_dir.File("logo.svg"))
+ in_files.append("#icon.png")
+ out_files.append(zip_dir.File("favicon.png"))
+ # PWA
+ service_worker = env.Substfile(
+ target="#bin/godot${PROGSUFFIX}.service.worker.js", source=service_worker, SUBST_DICT=subst_dict
+ )
+ in_files.append(service_worker)
+ out_files.append(zip_dir.File("service.worker.js"))
+ in_files.append("#misc/dist/html/manifest.json")
+ out_files.append(zip_dir.File("manifest.json"))
+ in_files.append("#misc/dist/html/offline.html")
+ out_files.append(zip_dir.File("offline.html"))
+ else:
+ # HTML
+ in_files.append("#misc/dist/html/full-size.html")
+ out_files.append(zip_dir.File(binary_name + ".html"))
+ in_files.append(service_worker)
+ out_files.append(zip_dir.File(binary_name + ".service.worker.js"))
+ in_files.append("#misc/dist/html/offline-export.html")
+ out_files.append(zip_dir.File("godot.offline.html"))
+
+ zip_files = env.InstallAs(out_files, in_files)
+ env.Zip(
+ "#bin/godot",
+ zip_files,
+ ZIPROOT=zip_dir,
+ ZIPSUFFIX="${PROGSUFFIX}${ZIPSUFFIX}",
+ ZIPCOMSTR="Archiving $SOURCES as $TARGET",
+ )
+
+
def add_js_libraries(env, libraries):
env.Append(JS_LIBS=env.File(libraries))
diff --git a/platform/javascript/export/export.cpp b/platform/javascript/export/export.cpp
index 353cc49ef8..bf4244eda4 100644
--- a/platform/javascript/export/export.cpp
+++ b/platform/javascript/export/export.cpp
@@ -28,7 +28,8 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#include "core/io/json.h"
+#include "core/io/image_loader.h"
+#include "core/io/stream_peer_ssl.h"
#include "core/io/tcp_server.h"
#include "core/io/zip_io.h"
#include "editor/editor_export.h"
@@ -37,24 +38,59 @@
#include "platform/javascript/logo.gen.h"
#include "platform/javascript/run_icon.gen.h"
-class EditorHTTPServer : public Reference {
+class EditorHTTPServer : public RefCounted {
private:
- Ref<TCP_Server> server;
- Ref<StreamPeerTCP> connection;
+ Ref<TCPServer> server;
+ Map<String, String> mimes;
+ Ref<StreamPeerTCP> tcp;
+ Ref<StreamPeerSSL> ssl;
+ Ref<StreamPeer> peer;
+ Ref<CryptoKey> key;
+ Ref<X509Certificate> cert;
+ bool use_ssl = false;
uint64_t time = 0;
uint8_t req_buf[4096];
int req_pos = 0;
void _clear_client() {
- connection = Ref<StreamPeerTCP>();
+ peer = Ref<StreamPeer>();
+ ssl = Ref<StreamPeerSSL>();
+ tcp = Ref<StreamPeerTCP>();
memset(req_buf, 0, sizeof(req_buf));
time = 0;
req_pos = 0;
}
+ void _set_internal_certs(Ref<Crypto> p_crypto) {
+ const String cache_path = EditorPaths::get_singleton()->get_cache_dir();
+ const String key_path = cache_path.plus_file("html5_server.key");
+ const String crt_path = cache_path.plus_file("html5_server.crt");
+ bool regen = !FileAccess::exists(key_path) || !FileAccess::exists(crt_path);
+ if (!regen) {
+ key = Ref<CryptoKey>(CryptoKey::create());
+ cert = Ref<X509Certificate>(X509Certificate::create());
+ if (key->load(key_path) != OK || cert->load(crt_path) != OK) {
+ regen = true;
+ }
+ }
+ if (regen) {
+ key = p_crypto->generate_rsa(2048);
+ key->save(key_path);
+ cert = p_crypto->generate_self_signed_certificate(key, "CN=godot-debug.local,O=A Game Dev,C=XXA", "20140101000000", "20340101000000");
+ cert->save(crt_path);
+ }
+ }
+
public:
EditorHTTPServer() {
- server.instance();
+ mimes["html"] = "text/html";
+ mimes["js"] = "application/javascript";
+ mimes["json"] = "application/json";
+ mimes["pck"] = "application/octet-stream";
+ mimes["png"] = "image/png";
+ mimes["svg"] = "image/svg";
+ mimes["wasm"] = "application/wasm";
+ server.instantiate();
stop();
}
@@ -63,7 +99,24 @@ public:
_clear_client();
}
- Error listen(int p_port, IP_Address p_address) {
+ Error listen(int p_port, IPAddress p_address, bool p_use_ssl, String p_ssl_key, String p_ssl_cert) {
+ use_ssl = p_use_ssl;
+ if (use_ssl) {
+ Ref<Crypto> crypto = Crypto::create();
+ if (crypto.is_null()) {
+ return ERR_UNAVAILABLE;
+ }
+ if (!p_ssl_key.is_empty() && !p_ssl_cert.is_empty()) {
+ key = Ref<CryptoKey>(CryptoKey::create());
+ Error err = key->load(p_ssl_key);
+ ERR_FAIL_COND_V(err != OK, err);
+ cert = Ref<X509Certificate>(X509Certificate::create());
+ err = cert->load(p_ssl_cert);
+ ERR_FAIL_COND_V(err != OK, err);
+ } else {
+ _set_internal_certs(crypto);
+ }
+ }
return server->listen(p_port, p_address);
}
@@ -82,51 +135,24 @@ public:
// Wrong protocol
ERR_FAIL_COND_MSG(req[0] != "GET" || req[2] != "HTTP/1.1", "Invalid method or HTTP version.");
- const String cache_path = EditorSettings::get_singleton()->get_cache_dir();
- const String basereq = "/tmp_js_export";
- String filepath;
- String ctype;
- if (req[1] == basereq + ".html") {
- filepath = cache_path.plus_file(req[1].get_file());
- ctype = "text/html";
- } else if (req[1] == basereq + ".js") {
- filepath = cache_path.plus_file(req[1].get_file());
- ctype = "application/javascript";
- } else if (req[1] == basereq + ".audio.worklet.js") {
- filepath = cache_path.plus_file(req[1].get_file());
- ctype = "application/javascript";
- } else if (req[1] == basereq + ".worker.js") {
- filepath = cache_path.plus_file(req[1].get_file());
- ctype = "application/javascript";
- } else if (req[1] == basereq + ".pck") {
- filepath = cache_path.plus_file(req[1].get_file());
- ctype = "application/octet-stream";
- } else if (req[1] == basereq + ".png" || req[1] == "/favicon.png") {
- // Also allow serving the generated favicon for a smoother loading experience.
- if (req[1] == "/favicon.png") {
- filepath = EditorSettings::get_singleton()->get_cache_dir().plus_file("favicon.png");
- } else {
- filepath = basereq + ".png";
- }
- ctype = "image/png";
- } else if (req[1] == basereq + ".side.wasm") {
- filepath = cache_path.plus_file(req[1].get_file());
- ctype = "application/wasm";
- } else if (req[1] == basereq + ".wasm") {
- filepath = cache_path.plus_file(req[1].get_file());
- ctype = "application/wasm";
- } else if (req[1].ends_with(".wasm")) {
- filepath = cache_path.plus_file(req[1].get_file()); // TODO dangerous?
- ctype = "application/wasm";
- }
- if (filepath.is_empty() || !FileAccess::exists(filepath)) {
+ const int query_index = req[1].find_char('?');
+ const String path = (query_index == -1) ? req[1] : req[1].substr(0, query_index);
+
+ const String req_file = path.get_file();
+ const String req_ext = path.get_extension();
+ const String cache_path = EditorPaths::get_singleton()->get_cache_dir().plus_file("web");
+ const String filepath = cache_path.plus_file(req_file);
+
+ if (!mimes.has(req_ext) || !FileAccess::exists(filepath)) {
String s = "HTTP/1.1 404 Not Found\r\n";
s += "Connection: Close\r\n";
s += "\r\n";
CharString cs = s.utf8();
- connection->put_data((const uint8_t *)cs.get_data(), cs.size() - 1);
+ peer->put_data((const uint8_t *)cs.get_data(), cs.size() - 1);
return;
}
+ const String ctype = mimes[req_ext];
+
FileAccess *f = FileAccess::open(filepath, FileAccess::READ);
ERR_FAIL_COND(!f);
String s = "HTTP/1.1 200 OK\r\n";
@@ -138,7 +164,7 @@ public:
s += "Cache-Control: no-store, max-age=0\r\n";
s += "\r\n";
CharString cs = s.utf8();
- Error err = connection->put_data((const uint8_t *)cs.get_data(), cs.size() - 1);
+ Error err = peer->put_data((const uint8_t *)cs.get_data(), cs.size() - 1);
if (err != OK) {
memdelete(f);
ERR_FAIL();
@@ -146,11 +172,11 @@ public:
while (true) {
uint8_t bytes[4096];
- int read = f->get_buffer(bytes, 4096);
- if (read < 1) {
+ uint64_t read = f->get_buffer(bytes, 4096);
+ if (read == 0) {
break;
}
- err = connection->put_data(bytes, read);
+ err = peer->put_data(bytes, read);
if (err != OK) {
memdelete(f);
ERR_FAIL();
@@ -163,21 +189,43 @@ public:
if (!server->is_listening()) {
return;
}
- if (connection.is_null()) {
+ if (tcp.is_null()) {
if (!server->is_connection_available()) {
return;
}
- connection = server->take_connection();
+ tcp = server->take_connection();
+ peer = tcp;
time = OS::get_singleton()->get_ticks_usec();
}
if (OS::get_singleton()->get_ticks_usec() - time > 1000000) {
_clear_client();
return;
}
- if (connection->get_status() != StreamPeerTCP::STATUS_CONNECTED) {
+ if (tcp->get_status() != StreamPeerTCP::STATUS_CONNECTED) {
return;
}
+ if (use_ssl) {
+ if (ssl.is_null()) {
+ ssl = Ref<StreamPeerSSL>(StreamPeerSSL::create());
+ peer = ssl;
+ ssl->set_blocking_handshake_enabled(false);
+ if (ssl->accept_stream(tcp, key, cert) != OK) {
+ _clear_client();
+ return;
+ }
+ }
+ ssl->poll();
+ if (ssl->get_status() == StreamPeerSSL::STATUS_HANDSHAKING) {
+ // Still handshaking, keep waiting.
+ return;
+ }
+ if (ssl->get_status() != StreamPeerSSL::STATUS_CONNECTED) {
+ _clear_client();
+ return;
+ }
+ }
+
while (true) {
char *r = (char *)req_buf;
int l = req_pos - 1;
@@ -189,7 +237,7 @@ public:
int read = 0;
ERR_FAIL_COND(req_pos >= 4096);
- Error err = connection->get_partial_data(&req_buf[req_pos], 1, read);
+ Error err = peer->get_partial_data(&req_buf[req_pos], 1, read);
if (err != OK) {
// Got an error
_clear_client();
@@ -242,7 +290,32 @@ class EditorExportPlatformJavaScript : public EditorExportPlatform {
return name;
}
- void _fix_html(Vector<uint8_t> &p_html, const Ref<EditorExportPreset> &p_preset, const String &p_name, bool p_debug, int p_flags, const Vector<SharedObject> p_shared_objects);
+ Ref<Image> _get_project_icon() const {
+ Ref<Image> icon;
+ icon.instantiate();
+ const String icon_path = String(GLOBAL_GET("application/config/icon")).strip_edges();
+ if (icon_path.is_empty() || ImageLoader::load_image(icon_path, icon) != OK) {
+ return EditorNode::get_singleton()->get_editor_theme()->get_icon("DefaultProjectIcon", "EditorIcons")->get_image();
+ }
+ return icon;
+ }
+
+ Ref<Image> _get_project_splash() const {
+ Ref<Image> splash;
+ splash.instantiate();
+ const String splash_path = String(GLOBAL_GET("application/boot_splash/image")).strip_edges();
+ if (splash_path.is_empty() || ImageLoader::load_image(splash_path, splash) != OK) {
+ return Ref<Image>(memnew(Image(boot_splash_png)));
+ }
+ return splash;
+ }
+
+ Error _extract_template(const String &p_template, const String &p_dir, const String &p_name, bool pwa);
+ void _replace_strings(Map<String, String> p_replaces, Vector<uint8_t> &r_template);
+ void _fix_html(Vector<uint8_t> &p_html, const Ref<EditorExportPreset> &p_preset, const String &p_name, bool p_debug, int p_flags, const Vector<SharedObject> p_shared_objects, const Dictionary &p_file_sizes);
+ Error _add_manifest_icon(const String &p_path, const String &p_icon, int p_size, Array &r_arr);
+ Error _build_pwa(const Ref<EditorExportPreset> &p_preset, const String p_path, const Vector<SharedObject> &p_shared_objects);
+ Error _write_or_error(const uint8_t *p_content, int p_len, String p_path);
static void _server_thread_poll(void *data);
@@ -281,10 +354,90 @@ public:
~EditorExportPlatformJavaScript();
};
-void EditorExportPlatformJavaScript::_fix_html(Vector<uint8_t> &p_html, const Ref<EditorExportPreset> &p_preset, const String &p_name, bool p_debug, int p_flags, const Vector<SharedObject> p_shared_objects) {
- String str_template = String::utf8(reinterpret_cast<const char *>(p_html.ptr()), p_html.size());
- String str_export;
+Error EditorExportPlatformJavaScript::_extract_template(const String &p_template, const String &p_dir, const String &p_name, bool pwa) {
+ FileAccess *src_f = nullptr;
+ zlib_filefunc_def io = zipio_create_io_from_file(&src_f);
+ unzFile pkg = unzOpen2(p_template.utf8().get_data(), &io);
+
+ if (!pkg) {
+ EditorNode::get_singleton()->show_warning(TTR("Could not open template for export:") + "\n" + p_template);
+ return ERR_FILE_NOT_FOUND;
+ }
+
+ if (unzGoToFirstFile(pkg) != UNZ_OK) {
+ EditorNode::get_singleton()->show_warning(TTR("Invalid export template:") + "\n" + p_template);
+ unzClose(pkg);
+ return ERR_FILE_CORRUPT;
+ }
+
+ do {
+ //get filename
+ unz_file_info info;
+ char fname[16384];
+ unzGetCurrentFileInfo(pkg, &info, fname, 16384, nullptr, 0, nullptr, 0);
+
+ String file = fname;
+
+ // Skip service worker and offline page if not exporting pwa.
+ if (!pwa && (file == "godot.service.worker.js" || file == "godot.offline.html")) {
+ continue;
+ }
+ Vector<uint8_t> data;
+ data.resize(info.uncompressed_size);
+
+ //read
+ unzOpenCurrentFile(pkg);
+ unzReadCurrentFile(pkg, data.ptrw(), data.size());
+ unzCloseCurrentFile(pkg);
+
+ //write
+ String dst = p_dir.plus_file(file.replace("godot", p_name));
+ FileAccess *f = FileAccess::open(dst, FileAccess::WRITE);
+ if (!f) {
+ EditorNode::get_singleton()->show_warning(TTR("Could not write file:") + "\n" + dst);
+ unzClose(pkg);
+ return ERR_FILE_CANT_WRITE;
+ }
+ f->store_buffer(data.ptr(), data.size());
+ memdelete(f);
+
+ } while (unzGoToNextFile(pkg) == UNZ_OK);
+ unzClose(pkg);
+ return OK;
+}
+
+Error EditorExportPlatformJavaScript::_write_or_error(const uint8_t *p_content, int p_size, String p_path) {
+ FileAccess *f = FileAccess::open(p_path, FileAccess::WRITE);
+ if (!f) {
+ EditorNode::get_singleton()->show_warning(TTR("Could not write file:") + "\n" + p_path);
+ return ERR_FILE_CANT_WRITE;
+ }
+ f->store_buffer(p_content, p_size);
+ memdelete(f);
+ return OK;
+}
+
+void EditorExportPlatformJavaScript::_replace_strings(Map<String, String> p_replaces, Vector<uint8_t> &r_template) {
+ String str_template = String::utf8(reinterpret_cast<const char *>(r_template.ptr()), r_template.size());
+ String out;
Vector<String> lines = str_template.split("\n");
+ for (int i = 0; i < lines.size(); i++) {
+ String current_line = lines[i];
+ for (Map<String, String>::Element *E = p_replaces.front(); E; E = E->next()) {
+ current_line = current_line.replace(E->key(), E->get());
+ }
+ out += current_line + "\n";
+ }
+ CharString cs = out.utf8();
+ r_template.resize(cs.length());
+ for (int i = 0; i < cs.length(); i++) {
+ r_template.write[i] = cs[i];
+ }
+}
+
+void EditorExportPlatformJavaScript::_fix_html(Vector<uint8_t> &p_html, const Ref<EditorExportPreset> &p_preset, const String &p_name, bool p_debug, int p_flags, const Vector<SharedObject> p_shared_objects, const Dictionary &p_file_sizes) {
+ // Engine.js config
+ Dictionary config;
Array libs;
for (int i = 0; i < p_shared_objects.size(); i++) {
libs.push_back(p_shared_objects[i].path.get_file());
@@ -295,27 +448,173 @@ void EditorExportPlatformJavaScript::_fix_html(Vector<uint8_t> &p_html, const Re
for (int i = 0; i < flags.size(); i++) {
args.push_back(flags[i]);
}
- Dictionary config;
config["canvasResizePolicy"] = p_preset->get("html/canvas_resize_policy");
+ config["experimentalVK"] = p_preset->get("html/experimental_virtual_keyboard");
+ config["focusCanvas"] = p_preset->get("html/focus_canvas_on_start");
config["gdnativeLibs"] = libs;
config["executable"] = p_name;
config["args"] = args;
- const String str_config = JSON::print(config);
+ config["fileSizes"] = p_file_sizes;
+
+ String head_include;
+ if (p_preset->get("html/export_icon")) {
+ head_include += "<link id='-gd-engine-icon' rel='icon' type='image/png' href='" + p_name + ".icon.png' />\n";
+ head_include += "<link rel='apple-touch-icon' href='" + p_name + ".apple-touch-icon.png'/>\n";
+ }
+ if (p_preset->get("progressive_web_app/enabled")) {
+ head_include += "<link rel='manifest' href='" + p_name + ".manifest.json'>\n";
+ head_include += "<script type='application/javascript'>window.addEventListener('load', () => {if ('serviceWorker' in navigator) {navigator.serviceWorker.register('" +
+ p_name + ".service.worker.js');}});</script>\n";
+ }
+
+ // Replaces HTML string
+ const String str_config = Variant(config).to_json_string();
+ const String custom_head_include = p_preset->get("html/head_include");
+ Map<String, String> replaces;
+ replaces["$GODOT_URL"] = p_name + ".js";
+ replaces["$GODOT_PROJECT_NAME"] = ProjectSettings::get_singleton()->get_setting("application/config/name");
+ replaces["$GODOT_HEAD_INCLUDE"] = head_include + custom_head_include;
+ replaces["$GODOT_CONFIG"] = str_config;
+ _replace_strings(replaces, p_html);
+}
- for (int i = 0; i < lines.size(); i++) {
- String current_line = lines[i];
- current_line = current_line.replace("$GODOT_URL", p_name + ".js");
- current_line = current_line.replace("$GODOT_PROJECT_NAME", ProjectSettings::get_singleton()->get_setting("application/config/name"));
- current_line = current_line.replace("$GODOT_HEAD_INCLUDE", p_preset->get("html/head_include"));
- current_line = current_line.replace("$GODOT_CONFIG", str_config);
- str_export += current_line + "\n";
+Error EditorExportPlatformJavaScript::_add_manifest_icon(const String &p_path, const String &p_icon, int p_size, Array &r_arr) {
+ const String name = p_path.get_file().get_basename();
+ const String icon_name = vformat("%s.%dx%d.png", name, p_size, p_size);
+ const String icon_dest = p_path.get_base_dir().plus_file(icon_name);
+
+ Ref<Image> icon;
+ if (!p_icon.is_empty()) {
+ icon.instantiate();
+ const Error err = ImageLoader::load_image(p_icon, icon);
+ if (err != OK) {
+ EditorNode::get_singleton()->show_warning(TTR("Could not read file:") + "\n" + p_icon);
+ return err;
+ }
+ if (icon->get_width() != p_size || icon->get_height() != p_size) {
+ icon->resize(p_size, p_size);
+ }
+ } else {
+ icon = _get_project_icon();
+ icon->resize(p_size, p_size);
}
+ const Error err = icon->save_png(icon_dest);
+ if (err != OK) {
+ EditorNode::get_singleton()->show_warning(TTR("Could not write file:") + "\n" + icon_dest);
+ return err;
+ }
+ Dictionary icon_dict;
+ icon_dict["sizes"] = vformat("%dx%d", p_size, p_size);
+ icon_dict["type"] = "image/png";
+ icon_dict["src"] = icon_name;
+ r_arr.push_back(icon_dict);
+ return err;
+}
- CharString cs = str_export.utf8();
- p_html.resize(cs.length());
- for (int i = 0; i < cs.length(); i++) {
- p_html.write[i] = cs[i];
+Error EditorExportPlatformJavaScript::_build_pwa(const Ref<EditorExportPreset> &p_preset, const String p_path, const Vector<SharedObject> &p_shared_objects) {
+ // Service worker
+ const String dir = p_path.get_base_dir();
+ const String name = p_path.get_file().get_basename();
+ const ExportMode mode = (ExportMode)(int)p_preset->get("variant/export_type");
+ Map<String, String> replaces;
+ replaces["@GODOT_VERSION@"] = "1";
+ replaces["@GODOT_NAME@"] = name;
+ replaces["@GODOT_OFFLINE_PAGE@"] = name + ".offline.html";
+ Array files;
+ replaces["@GODOT_OPT_CACHE@"] = Variant(files).to_json_string();
+ files.push_back(name + ".html");
+ files.push_back(name + ".js");
+ files.push_back(name + ".wasm");
+ files.push_back(name + ".pck");
+ files.push_back(name + ".offline.html");
+ if (p_preset->get("html/export_icon")) {
+ files.push_back(name + ".icon.png");
+ files.push_back(name + ".apple-touch-icon.png");
+ }
+ if (mode == EXPORT_MODE_THREADS) {
+ files.push_back(name + ".worker.js");
+ files.push_back(name + ".audio.worklet.js");
+ } else if (mode == EXPORT_MODE_GDNATIVE) {
+ files.push_back(name + ".side.wasm");
+ for (int i = 0; i < p_shared_objects.size(); i++) {
+ files.push_back(p_shared_objects[i].path.get_file());
+ }
+ }
+ replaces["@GODOT_CACHE@"] = Variant(files).to_json_string();
+
+ const String sw_path = dir.plus_file(name + ".service.worker.js");
+ Vector<uint8_t> sw;
+ {
+ FileAccess *f = FileAccess::open(sw_path, FileAccess::READ);
+ if (!f) {
+ EditorNode::get_singleton()->show_warning(TTR("Could not read file:") + "\n" + sw_path);
+ return ERR_FILE_CANT_READ;
+ }
+ sw.resize(f->get_length());
+ f->get_buffer(sw.ptrw(), sw.size());
+ memdelete(f);
+ f = nullptr;
+ }
+ _replace_strings(replaces, sw);
+ Error err = _write_or_error(sw.ptr(), sw.size(), dir.plus_file(name + ".service.worker.js"));
+ if (err != OK) {
+ return err;
+ }
+
+ // Custom offline page
+ const String offline_page = p_preset->get("progressive_web_app/offline_page");
+ if (!offline_page.is_empty()) {
+ DirAccess *da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
+ const String offline_dest = dir.plus_file(name + ".offline.html");
+ err = da->copy(ProjectSettings::get_singleton()->globalize_path(offline_page), offline_dest);
+ if (err != OK) {
+ EditorNode::get_singleton()->show_warning(TTR("Could not read file:") + "\n" + offline_dest);
+ return err;
+ }
+ }
+
+ // Manifest
+ const char *modes[4] = { "fullscreen", "standalone", "minimal-ui", "browser" };
+ const char *orientations[3] = { "any", "landscape", "portrait" };
+ const int display = CLAMP(int(p_preset->get("progressive_web_app/display")), 0, 4);
+ const int orientation = CLAMP(int(p_preset->get("progressive_web_app/orientation")), 0, 3);
+
+ Dictionary manifest;
+ String proj_name = ProjectSettings::get_singleton()->get_setting("application/config/name");
+ if (proj_name.is_empty()) {
+ proj_name = "Godot Game";
+ }
+ manifest["name"] = proj_name;
+ manifest["start_url"] = "./" + name + ".html";
+ manifest["display"] = String::utf8(modes[display]);
+ manifest["orientation"] = String::utf8(orientations[orientation]);
+ manifest["background_color"] = "#" + p_preset->get("progressive_web_app/background_color").operator Color().to_html(false);
+
+ Array icons_arr;
+ const String icon144_path = p_preset->get("progressive_web_app/icon_144x144");
+ err = _add_manifest_icon(p_path, icon144_path, 144, icons_arr);
+ if (err != OK) {
+ return err;
+ }
+ const String icon180_path = p_preset->get("progressive_web_app/icon_180x180");
+ err = _add_manifest_icon(p_path, icon180_path, 180, icons_arr);
+ if (err != OK) {
+ return err;
+ }
+ const String icon512_path = p_preset->get("progressive_web_app/icon_512x512");
+ err = _add_manifest_icon(p_path, icon512_path, 512, icons_arr);
+ if (err != OK) {
+ return err;
}
+ manifest["icons"] = icons_arr;
+
+ CharString cs = Variant(manifest).to_json_string().utf8();
+ err = _write_or_error((const uint8_t *)cs.get_data(), cs.length(), dir.plus_file(name + ".manifest.json"));
+ if (err != OK) {
+ return err;
+ }
+
+ return OK;
}
void EditorExportPlatformJavaScript::get_preset_features(const Ref<EditorExportPreset> &p_preset, List<String> *r_features) {
@@ -348,9 +647,20 @@ void EditorExportPlatformJavaScript::get_export_options(List<ExportOption> *r_op
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "vram_texture_compression/for_desktop"), true)); // S3TC
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "vram_texture_compression/for_mobile"), false)); // ETC or ETC2, depending on renderer
+ r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "html/export_icon"), true));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "html/custom_html_shell", PROPERTY_HINT_FILE, "*.html"), ""));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "html/head_include", PROPERTY_HINT_MULTILINE_TEXT), ""));
r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "html/canvas_resize_policy", PROPERTY_HINT_ENUM, "None,Project,Adaptive"), 2));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "html/focus_canvas_on_start"), true));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "html/experimental_virtual_keyboard"), false));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "progressive_web_app/enabled"), false));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "progressive_web_app/offline_page", PROPERTY_HINT_FILE, "*.html"), ""));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "progressive_web_app/display", PROPERTY_HINT_ENUM, "Fullscreen,Standalone,Minimal UI,Browser"), 1));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "progressive_web_app/orientation", PROPERTY_HINT_ENUM, "Any,Landscape,Portrait"), 0));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "progressive_web_app/icon_144x144", PROPERTY_HINT_FILE, "*.png,*.webp,*.svg,*.svgz"), ""));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "progressive_web_app/icon_180x180", PROPERTY_HINT_FILE, "*.png,*.webp,*.svg,*.svgz"), ""));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "progressive_web_app/icon_512x512", PROPERTY_HINT_FILE, "*.png,*.webp,*.svg,*.svgz"), ""));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::COLOR, "progressive_web_app/background_color", PROPERTY_HINT_COLOR_NO_ALPHA), Color()));
}
String EditorExportPlatformJavaScript::get_name() const {
@@ -416,20 +726,25 @@ List<String> EditorExportPlatformJavaScript::get_binary_extensions(const Ref<Edi
Error EditorExportPlatformJavaScript::export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags) {
ExportNotifier notifier(*this, p_preset, p_debug, p_path, p_flags);
- String custom_debug = p_preset->get("custom_template/debug");
- String custom_release = p_preset->get("custom_template/release");
- String custom_html = p_preset->get("html/custom_html_shell");
+ const String custom_debug = p_preset->get("custom_template/debug");
+ const String custom_release = p_preset->get("custom_template/release");
+ const String custom_html = p_preset->get("html/custom_html_shell");
+ const bool export_icon = p_preset->get("html/export_icon");
+ const bool pwa = p_preset->get("progressive_web_app/enabled");
- String template_path = p_debug ? custom_debug : custom_release;
+ const String base_dir = p_path.get_base_dir();
+ const String base_path = p_path.get_basename();
+ const String base_name = p_path.get_file().get_basename();
+ // Find the correct template
+ String template_path = p_debug ? custom_debug : custom_release;
template_path = template_path.strip_edges();
-
if (template_path == String()) {
ExportMode mode = (ExportMode)(int)p_preset->get("variant/export_type");
template_path = find_export_template(_get_template_name(mode, p_debug));
}
- if (!DirAccess::exists(p_path.get_base_dir())) {
+ if (!DirAccess::exists(base_dir)) {
return ERR_FILE_BAD_PATH;
}
@@ -438,8 +753,9 @@ Error EditorExportPlatformJavaScript::export_project(const Ref<EditorExportPrese
return ERR_FILE_NOT_FOUND;
}
+ // Export pck and shared objects
Vector<SharedObject> shared_objects;
- String pck_path = p_path.get_basename() + ".pck";
+ String pck_path = base_path + ".pck";
Error error = save_pack(p_preset, pck_path, &shared_objects);
if (error != OK) {
EditorNode::get_singleton()->show_warning(TTR("Could not write file:") + "\n" + pck_path);
@@ -447,7 +763,7 @@ Error EditorExportPlatformJavaScript::export_project(const Ref<EditorExportPrese
}
DirAccess *da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
for (int i = 0; i < shared_objects.size(); i++) {
- String dst = p_path.get_base_dir().plus_file(shared_objects[i].path.get_file());
+ String dst = base_dir.plus_file(shared_objects[i].path.get_file());
error = da->copy(shared_objects[i].path, dst);
if (error != OK) {
EditorNode::get_singleton()->show_warning(TTR("Could not write file:") + "\n" + shared_objects[i].path.get_file());
@@ -456,111 +772,54 @@ Error EditorExportPlatformJavaScript::export_project(const Ref<EditorExportPrese
}
}
memdelete(da);
+ da = nullptr;
- FileAccess *src_f = nullptr;
- zlib_filefunc_def io = zipio_create_io_from_file(&src_f);
- unzFile pkg = unzOpen2(template_path.utf8().get_data(), &io);
-
- if (!pkg) {
- EditorNode::get_singleton()->show_warning(TTR("Could not open template for export:") + "\n" + template_path);
- return ERR_FILE_NOT_FOUND;
- }
-
- if (unzGoToFirstFile(pkg) != UNZ_OK) {
- EditorNode::get_singleton()->show_warning(TTR("Invalid export template:") + "\n" + template_path);
- unzClose(pkg);
- return ERR_FILE_CORRUPT;
+ // Extract templates.
+ error = _extract_template(template_path, base_dir, base_name, pwa);
+ if (error) {
+ return error;
}
- do {
- //get filename
- unz_file_info info;
- char fname[16384];
- unzGetCurrentFileInfo(pkg, &info, fname, 16384, nullptr, 0, nullptr, 0);
-
- String file = fname;
-
- Vector<uint8_t> data;
- data.resize(info.uncompressed_size);
-
- //read
- unzOpenCurrentFile(pkg);
- unzReadCurrentFile(pkg, data.ptrw(), data.size());
- unzCloseCurrentFile(pkg);
-
- //write
-
- if (file == "godot.html") {
- if (!custom_html.is_empty()) {
- continue;
- }
- _fix_html(data, p_preset, p_path.get_file().get_basename(), p_debug, p_flags, shared_objects);
- file = p_path.get_file();
-
- } else if (file == "godot.js") {
- file = p_path.get_file().get_basename() + ".js";
-
- } else if (file == "godot.worker.js") {
- file = p_path.get_file().get_basename() + ".worker.js";
-
- } else if (file == "godot.side.wasm") {
- file = p_path.get_file().get_basename() + ".side.wasm";
-
- } else if (file == "godot.audio.worklet.js") {
- file = p_path.get_file().get_basename() + ".audio.worklet.js";
-
- } else if (file == "godot.wasm") {
- file = p_path.get_file().get_basename() + ".wasm";
- }
-
- String dst = p_path.get_base_dir().plus_file(file);
- FileAccess *f = FileAccess::open(dst, FileAccess::WRITE);
- if (!f) {
- EditorNode::get_singleton()->show_warning(TTR("Could not write file:") + "\n" + dst);
- unzClose(pkg);
- return ERR_FILE_CANT_WRITE;
- }
- f->store_buffer(data.ptr(), data.size());
- memdelete(f);
-
- } while (unzGoToNextFile(pkg) == UNZ_OK);
- unzClose(pkg);
-
- if (!custom_html.is_empty()) {
- FileAccess *f = FileAccess::open(custom_html, FileAccess::READ);
- if (!f) {
- EditorNode::get_singleton()->show_warning(TTR("Could not read custom HTML shell:") + "\n" + custom_html);
- return ERR_FILE_CANT_READ;
- }
- Vector<uint8_t> buf;
- buf.resize(f->get_len());
- f->get_buffer(buf.ptrw(), buf.size());
+ // Parse generated file sizes (pck and wasm, to help show a meaningful loading bar).
+ Dictionary file_sizes;
+ FileAccess *f = nullptr;
+ f = FileAccess::open(pck_path, FileAccess::READ);
+ if (f) {
+ file_sizes[pck_path.get_file()] = (uint64_t)f->get_length();
memdelete(f);
- _fix_html(buf, p_preset, p_path.get_file().get_basename(), p_debug, p_flags, shared_objects);
-
- f = FileAccess::open(p_path, FileAccess::WRITE);
- if (!f) {
- EditorNode::get_singleton()->show_warning(TTR("Could not write file:") + "\n" + p_path);
- return ERR_FILE_CANT_WRITE;
- }
- f->store_buffer(buf.ptr(), buf.size());
+ f = nullptr;
+ }
+ f = FileAccess::open(base_path + ".wasm", FileAccess::READ);
+ if (f) {
+ file_sizes[base_name + ".wasm"] = (uint64_t)f->get_length();
memdelete(f);
+ f = nullptr;
}
- Ref<Image> splash;
- const String splash_path = String(GLOBAL_GET("application/boot_splash/image")).strip_edges();
- if (!splash_path.is_empty()) {
- splash.instance();
- const Error err = splash->load(splash_path);
- if (err) {
- EditorNode::get_singleton()->show_warning(TTR("Could not read boot splash image file:") + "\n" + splash_path + "\n" + TTR("Using default boot splash image."));
- splash.unref();
- }
+ // Read the HTML shell file (custom or from template).
+ const String html_path = custom_html.is_empty() ? base_path + ".html" : custom_html;
+ Vector<uint8_t> html;
+ f = FileAccess::open(html_path, FileAccess::READ);
+ if (!f) {
+ EditorNode::get_singleton()->show_warning(TTR("Could not read HTML shell:") + "\n" + html_path);
+ return ERR_FILE_CANT_READ;
}
- if (splash.is_null()) {
- splash = Ref<Image>(memnew(Image(boot_splash_png)));
+ html.resize(f->get_length());
+ f->get_buffer(html.ptrw(), html.size());
+ memdelete(f);
+ f = nullptr;
+
+ // Generate HTML file with replaced strings.
+ _fix_html(html, p_preset, base_name, p_debug, p_flags, shared_objects, file_sizes);
+ Error err = _write_or_error(html.ptr(), html.size(), p_path);
+ if (err != OK) {
+ return err;
}
- const String splash_png_path = p_path.get_base_dir().plus_file(p_path.get_file().get_basename() + ".png");
+ html.resize(0);
+
+ // Export splash (why?)
+ Ref<Image> splash = _get_project_splash();
+ const String splash_png_path = base_path + ".png";
if (splash->save_png(splash_png_path) != OK) {
EditorNode::get_singleton()->show_warning(TTR("Could not write file:") + "\n" + splash_png_path);
return ERR_FILE_CANT_WRITE;
@@ -568,22 +827,27 @@ Error EditorExportPlatformJavaScript::export_project(const Ref<EditorExportPrese
// Save a favicon that can be accessed without waiting for the project to finish loading.
// This way, the favicon can be displayed immediately when loading the page.
- Ref<Image> favicon;
- const String favicon_path = String(GLOBAL_GET("application/config/icon")).strip_edges();
- if (!favicon_path.is_empty()) {
- favicon.instance();
- const Error err = favicon->load(favicon_path);
- if (err) {
- favicon.unref();
- }
- }
-
- if (favicon.is_valid()) {
- const String favicon_png_path = p_path.get_base_dir().plus_file("favicon.png");
+ if (export_icon) {
+ Ref<Image> favicon = _get_project_icon();
+ const String favicon_png_path = base_path + ".icon.png";
if (favicon->save_png(favicon_png_path) != OK) {
EditorNode::get_singleton()->show_warning(TTR("Could not write file:") + "\n" + favicon_png_path);
return ERR_FILE_CANT_WRITE;
}
+ favicon->resize(180, 180);
+ const String apple_icon_png_path = base_path + ".apple-touch-icon.png";
+ if (favicon->save_png(apple_icon_png_path) != OK) {
+ EditorNode::get_singleton()->show_warning(TTR("Could not write file:") + "\n" + apple_icon_png_path);
+ return ERR_FILE_CANT_WRITE;
+ }
+ }
+
+ // Generate the PWA worker and manifest
+ if (pwa) {
+ err = _build_pwa(p_preset, p_path, shared_objects);
+ if (err != OK) {
+ return err;
+ }
}
return OK;
@@ -628,26 +892,38 @@ Error EditorExportPlatformJavaScript::run(const Ref<EditorExportPreset> &p_prese
return OK;
}
- const String basepath = EditorSettings::get_singleton()->get_cache_dir().plus_file("tmp_js_export");
+ const String dest = EditorPaths::get_singleton()->get_cache_dir().plus_file("web");
+ DirAccessRef da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
+ if (!da->dir_exists(dest)) {
+ Error err = da->make_dir_recursive(dest);
+ if (err != OK) {
+ EditorNode::get_singleton()->show_warning(TTR("Could not create HTTP server directory:") + "\n" + dest);
+ return err;
+ }
+ }
+ const String basepath = dest.plus_file("tmp_js_export");
Error err = export_project(p_preset, true, basepath + ".html", p_debug_flags);
if (err != OK) {
// Export generates several files, clean them up on failure.
DirAccess::remove_file_or_error(basepath + ".html");
+ DirAccess::remove_file_or_error(basepath + ".offline.html");
DirAccess::remove_file_or_error(basepath + ".js");
DirAccess::remove_file_or_error(basepath + ".worker.js");
DirAccess::remove_file_or_error(basepath + ".audio.worklet.js");
+ DirAccess::remove_file_or_error(basepath + ".service.worker.js");
DirAccess::remove_file_or_error(basepath + ".pck");
DirAccess::remove_file_or_error(basepath + ".png");
DirAccess::remove_file_or_error(basepath + ".side.wasm");
DirAccess::remove_file_or_error(basepath + ".wasm");
- DirAccess::remove_file_or_error(EditorSettings::get_singleton()->get_cache_dir().plus_file("favicon.png"));
+ DirAccess::remove_file_or_error(basepath + ".icon.png");
+ DirAccess::remove_file_or_error(basepath + ".apple-touch-icon.png");
return err;
}
const uint16_t bind_port = EDITOR_GET("export/web/http_port");
// Resolve host if needed.
const String bind_host = EDITOR_GET("export/web/http_host");
- IP_Address bind_ip;
+ IPAddress bind_ip;
if (bind_host.is_valid_ip_address()) {
bind_ip = bind_host;
} else {
@@ -655,16 +931,23 @@ Error EditorExportPlatformJavaScript::run(const Ref<EditorExportPreset> &p_prese
}
ERR_FAIL_COND_V_MSG(!bind_ip.is_valid(), ERR_INVALID_PARAMETER, "Invalid editor setting 'export/web/http_host': '" + bind_host + "'. Try using '127.0.0.1'.");
+ const bool use_ssl = EDITOR_GET("export/web/use_ssl");
+ const String ssl_key = EDITOR_GET("export/web/ssl_key");
+ const String ssl_cert = EDITOR_GET("export/web/ssl_certificate");
+
// Restart server.
{
MutexLock lock(server_lock);
server->stop();
- err = server->listen(bind_port, bind_ip);
+ err = server->listen(bind_port, bind_ip, use_ssl, ssl_key, ssl_cert);
+ }
+ if (err != OK) {
+ EditorNode::get_singleton()->show_warning(TTR("Error starting HTTP server:") + "\n" + itos(err));
+ return err;
}
- ERR_FAIL_COND_V_MSG(err != OK, err, "Unable to start HTTP server.");
- OS::get_singleton()->shell_open(String("http://" + bind_host + ":" + itos(bind_port) + "/tmp_js_export.html"));
+ OS::get_singleton()->shell_open(String((use_ssl ? "https://" : "http://") + bind_host + ":" + itos(bind_port) + "/tmp_js_export.html"));
// FIXME: Find out how to clean up export files after running the successfully
// exported game. Might not be trivial.
return OK;
@@ -686,22 +969,22 @@ void EditorExportPlatformJavaScript::_server_thread_poll(void *data) {
}
EditorExportPlatformJavaScript::EditorExportPlatformJavaScript() {
- server.instance();
+ server.instantiate();
server_thread.start(_server_thread_poll, this);
Ref<Image> img = memnew(Image(_javascript_logo));
- logo.instance();
+ logo.instantiate();
logo->create_from_image(img);
img = Ref<Image>(memnew(Image(_javascript_run_icon)));
- run_icon.instance();
+ run_icon.instantiate();
run_icon->create_from_image(img);
Ref<Theme> theme = EditorNode::get_singleton()->get_editor_theme();
if (theme.is_valid()) {
stop_icon = theme->get_icon("Stop", "EditorIcons");
} else {
- stop_icon.instance();
+ stop_icon.instantiate();
}
}
@@ -714,9 +997,14 @@ EditorExportPlatformJavaScript::~EditorExportPlatformJavaScript() {
void register_javascript_exporter() {
EDITOR_DEF("export/web/http_host", "localhost");
EDITOR_DEF("export/web/http_port", 8060);
+ EDITOR_DEF("export/web/use_ssl", false);
+ EDITOR_DEF("export/web/ssl_key", "");
+ EDITOR_DEF("export/web/ssl_certificate", "");
EditorSettings::get_singleton()->add_property_hint(PropertyInfo(Variant::INT, "export/web/http_port", PROPERTY_HINT_RANGE, "1,65535,1"));
+ EditorSettings::get_singleton()->add_property_hint(PropertyInfo(Variant::STRING, "export/web/ssl_key", PROPERTY_HINT_GLOBAL_FILE, "*.key"));
+ EditorSettings::get_singleton()->add_property_hint(PropertyInfo(Variant::STRING, "export/web/ssl_certificate", PROPERTY_HINT_GLOBAL_FILE, "*.crt,*.pem"));
Ref<EditorExportPlatformJavaScript> platform;
- platform.instance();
+ platform.instantiate();
EditorExport::get_singleton()->add_export_platform(platform);
}
diff --git a/platform/javascript/godot_js.h b/platform/javascript/godot_js.h
index 5aa8677a54..d332af2c31 100644
--- a/platform/javascript/godot_js.h
+++ b/platform/javascript/godot_js.h
@@ -71,6 +71,7 @@ extern int godot_js_display_fullscreen_exit();
extern void godot_js_display_compute_position(int p_x, int p_y, int32_t *r_x, int32_t *r_y);
extern void godot_js_display_window_title_set(const char *p_text);
extern void godot_js_display_window_icon_set(const uint8_t *p_ptr, int p_len);
+extern int godot_js_display_has_webgl(int p_version);
// Display clipboard
extern int godot_js_display_clipboard_set(const char *p_text);
@@ -83,7 +84,7 @@ extern void godot_js_display_cursor_set_custom_shape(const char *p_shape, const
extern void godot_js_display_cursor_set_visible(int p_visible);
// Display gamepad
-extern char *godot_js_display_gamepad_cb(void (*p_on_change)(int p_index, int p_connected, const char *p_id, const char *p_guid));
+extern void godot_js_display_gamepad_cb(void (*p_on_change)(int p_index, int p_connected, const char *p_id, const char *p_guid));
extern int godot_js_display_gamepad_sample();
extern int godot_js_display_gamepad_sample_count();
extern int godot_js_display_gamepad_sample_get(int p_idx, float r_btns[16], int32_t *r_btns_num, float r_axes[10], int32_t *r_axes_num, int32_t *r_standard);
@@ -92,7 +93,14 @@ extern int godot_js_display_gamepad_sample_get(int p_idx, float r_btns[16], int3
extern void godot_js_display_notification_cb(void (*p_callback)(int p_notification), int p_enter, int p_exit, int p_in, int p_out);
extern void godot_js_display_paste_cb(void (*p_callback)(const char *p_text));
extern void godot_js_display_drop_files_cb(void (*p_callback)(char **p_filev, int p_filec));
-extern void godot_js_display_setup_canvas(int p_width, int p_height, int p_fullscreen);
+extern void godot_js_display_setup_canvas(int p_width, int p_height, int p_fullscreen, int p_hidpi);
+
+// Display Virtual Keyboard
+extern int godot_js_display_vk_available();
+extern void godot_js_display_vk_cb(void (*p_input)(const char *p_text, int p_cursor));
+extern void godot_js_display_vk_show(const char *p_text, int p_multiline, int p_start, int p_end);
+extern void godot_js_display_vk_hide();
+
#ifdef __cplusplus
}
#endif
diff --git a/platform/javascript/http_client.h.inc b/platform/javascript/http_client.h.inc
deleted file mode 100644
index 2ef1ad5b83..0000000000
--- a/platform/javascript/http_client.h.inc
+++ /dev/null
@@ -1,53 +0,0 @@
-/*************************************************************************/
-/* http_client.h.inc */
-/*************************************************************************/
-/* This file is part of: */
-/* GODOT ENGINE */
-/* https://godotengine.org */
-/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 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. */
-/*************************************************************************/
-
-// HTTPClient's additional private members in the javascript platform
-
-Error prepare_request(Method p_method, const String &p_url, const Vector<String> &p_headers);
-
-int xhr_id;
-int read_limit = 4096;
-int response_read_offset = 0;
-Status status = STATUS_DISCONNECTED;
-
-String host;
-int port = -1;
-bool use_tls = false;
-String username;
-String password;
-
-int polled_response_code = 0;
-String polled_response_header;
-PackedByteArray polled_response;
-
-#ifdef DEBUG_ENABLED
-bool has_polled = false;
-uint64_t last_polling_frame = 0;
-#endif
diff --git a/platform/javascript/http_client_javascript.cpp b/platform/javascript/http_client_javascript.cpp
index 1136ea299d..f7d78abcea 100644
--- a/platform/javascript/http_client_javascript.cpp
+++ b/platform/javascript/http_client_javascript.cpp
@@ -28,14 +28,19 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#include "core/io/http_client.h"
+#include "http_client_javascript.h"
-#include "http_request.h"
+void HTTPClientJavaScript::_parse_headers(int p_len, const char **p_headers, void *p_ref) {
+ HTTPClientJavaScript *client = static_cast<HTTPClientJavaScript *>(p_ref);
+ for (int i = 0; i < p_len; i++) {
+ client->response_headers.push_back(String::utf8(p_headers[i]));
+ }
+}
-Error HTTPClient::connect_to_host(const String &p_host, int p_port, bool p_ssl, bool p_verify_host) {
+Error HTTPClientJavaScript::connect_to_host(const String &p_host, int p_port, bool p_ssl, bool p_verify_host) {
close();
if (p_ssl && !p_verify_host) {
- WARN_PRINT("Disabling HTTPClient's host verification is not supported for the HTML5 platform, host will be verified");
+ WARN_PRINT("Disabling HTTPClientJavaScript's host verification is not supported for the HTML5 platform, host will be verified");
}
port = p_port;
@@ -66,15 +71,15 @@ Error HTTPClient::connect_to_host(const String &p_host, int p_port, bool p_ssl,
return OK;
}
-void HTTPClient::set_connection(const Ref<StreamPeer> &p_connection) {
- ERR_FAIL_MSG("Accessing an HTTPClient's StreamPeer is not supported for the HTML5 platform.");
+void HTTPClientJavaScript::set_connection(const Ref<StreamPeer> &p_connection) {
+ ERR_FAIL_MSG("Accessing an HTTPClientJavaScript's StreamPeer is not supported for the HTML5 platform.");
}
-Ref<StreamPeer> HTTPClient::get_connection() const {
- ERR_FAIL_V_MSG(REF(), "Accessing an HTTPClient's StreamPeer is not supported for the HTML5 platform.");
+Ref<StreamPeer> HTTPClientJavaScript::get_connection() const {
+ ERR_FAIL_V_MSG(REF(), "Accessing an HTTPClientJavaScript's StreamPeer is not supported for the HTML5 platform.");
}
-Error HTTPClient::prepare_request(Method p_method, const String &p_url, const Vector<String> &p_headers) {
+Error HTTPClientJavaScript::request(Method p_method, const String &p_url, const Vector<String> &p_headers, const uint8_t *p_body, int p_body_len) {
ERR_FAIL_INDEX_V(p_method, METHOD_MAX, ERR_INVALID_PARAMETER);
ERR_FAIL_COND_V_MSG(p_method == METHOD_TRACE || p_method == METHOD_CONNECT, ERR_UNAVAILABLE, "HTTP methods TRACE and CONNECT are not supported for the HTML5 platform.");
ERR_FAIL_COND_V(status != STATUS_CONNECTED, ERR_INVALID_PARAMETER);
@@ -83,127 +88,107 @@ Error HTTPClient::prepare_request(Method p_method, const String &p_url, const Ve
ERR_FAIL_COND_V(!p_url.begins_with("/"), ERR_INVALID_PARAMETER);
String url = (use_tls ? "https://" : "http://") + host + ":" + itos(port) + p_url;
- godot_xhr_reset(xhr_id);
- godot_xhr_open(xhr_id, _methods[p_method], url.utf8().get_data(),
- username.is_empty() ? nullptr : username.utf8().get_data(),
- password.is_empty() ? nullptr : password.utf8().get_data());
-
+ Vector<CharString> keeper;
+ Vector<const char *> c_strings;
for (int i = 0; i < p_headers.size(); i++) {
- int header_separator = p_headers[i].find(": ");
- ERR_FAIL_COND_V(header_separator < 0, ERR_INVALID_PARAMETER);
- godot_xhr_set_request_header(xhr_id,
- p_headers[i].left(header_separator).utf8().get_data(),
- p_headers[i].right(header_separator + 2).utf8().get_data());
+ keeper.push_back(p_headers[i].utf8());
+ c_strings.push_back(keeper[i].get_data());
}
- response_read_offset = 0;
- status = STATUS_REQUESTING;
- return OK;
-}
-
-Error HTTPClient::request_raw(Method p_method, const String &p_url, const Vector<String> &p_headers, const Vector<uint8_t> &p_body) {
- Error err = prepare_request(p_method, p_url, p_headers);
- if (err != OK)
- return err;
- if (p_body.is_empty()) {
- godot_xhr_send(xhr_id, nullptr, 0);
- } else {
- godot_xhr_send(xhr_id, p_body.ptr(), p_body.size());
- }
- return OK;
-}
-
-Error HTTPClient::request(Method p_method, const String &p_url, const Vector<String> &p_headers, const String &p_body) {
- Error err = prepare_request(p_method, p_url, p_headers);
- if (err != OK)
- return err;
- if (p_body.is_empty()) {
- godot_xhr_send(xhr_id, nullptr, 0);
- } else {
- const CharString cs = p_body.utf8();
- godot_xhr_send(xhr_id, cs.get_data(), cs.length());
+ if (js_id) {
+ godot_js_fetch_free(js_id);
}
+ js_id = godot_js_fetch_create(_methods[p_method], url.utf8().get_data(), c_strings.ptrw(), c_strings.size(), p_body, p_body_len);
+ status = STATUS_REQUESTING;
return OK;
}
-void HTTPClient::close() {
+void HTTPClientJavaScript::close() {
host = "";
port = -1;
use_tls = false;
status = STATUS_DISCONNECTED;
- polled_response.resize(0);
polled_response_code = 0;
- polled_response_header = String();
- godot_xhr_reset(xhr_id);
+ response_headers.resize(0);
+ response_buffer.resize(0);
+ if (js_id) {
+ godot_js_fetch_free(js_id);
+ js_id = 0;
+ }
}
-HTTPClient::Status HTTPClient::get_status() const {
+HTTPClientJavaScript::Status HTTPClientJavaScript::get_status() const {
return status;
}
-bool HTTPClient::has_response() const {
- return !polled_response_header.is_empty();
+bool HTTPClientJavaScript::has_response() const {
+ return response_headers.size() > 0;
}
-bool HTTPClient::is_response_chunked() const {
- // TODO evaluate using moz-chunked-arraybuffer, fetch & ReadableStream
- return false;
+bool HTTPClientJavaScript::is_response_chunked() const {
+ return godot_js_fetch_is_chunked(js_id);
}
-int HTTPClient::get_response_code() const {
+int HTTPClientJavaScript::get_response_code() const {
return polled_response_code;
}
-Error HTTPClient::get_response_headers(List<String> *r_response) {
- if (polled_response_header.is_empty())
+Error HTTPClientJavaScript::get_response_headers(List<String> *r_response) {
+ if (!response_headers.size()) {
return ERR_INVALID_PARAMETER;
-
- Vector<String> header_lines = polled_response_header.split("\r\n", false);
- for (int i = 0; i < header_lines.size(); ++i) {
- r_response->push_back(header_lines[i]);
}
- polled_response_header = String();
+ for (int i = 0; i < response_headers.size(); i++) {
+ r_response->push_back(response_headers[i]);
+ }
+ response_headers.clear();
return OK;
}
-int HTTPClient::get_response_body_length() const {
- return polled_response.size();
+int HTTPClientJavaScript::get_response_body_length() const {
+ return godot_js_fetch_body_length_get(js_id);
}
-PackedByteArray HTTPClient::read_response_body_chunk() {
+PackedByteArray HTTPClientJavaScript::read_response_body_chunk() {
ERR_FAIL_COND_V(status != STATUS_BODY, PackedByteArray());
- int to_read = MIN(read_limit, polled_response.size() - response_read_offset);
- PackedByteArray chunk;
- chunk.resize(to_read);
- memcpy(chunk.ptrw(), polled_response.ptr() + response_read_offset, to_read);
- response_read_offset += to_read;
-
- if (response_read_offset == polled_response.size()) {
- status = STATUS_CONNECTED;
- polled_response.resize(0);
- godot_xhr_reset(xhr_id);
+ if (response_buffer.size() != read_limit) {
+ response_buffer.resize(read_limit);
+ }
+ int read = godot_js_fetch_read_chunk(js_id, response_buffer.ptrw(), read_limit);
+
+ // Check if the stream is over.
+ godot_js_fetch_state_t state = godot_js_fetch_state_get(js_id);
+ if (state == GODOT_JS_FETCH_STATE_DONE) {
+ status = STATUS_DISCONNECTED;
+ } else if (state != GODOT_JS_FETCH_STATE_BODY) {
+ status = STATUS_CONNECTION_ERROR;
}
+ PackedByteArray chunk;
+ if (!read) {
+ return chunk;
+ }
+ chunk.resize(read);
+ memcpy(chunk.ptrw(), response_buffer.ptr(), read);
return chunk;
}
-void HTTPClient::set_blocking_mode(bool p_enable) {
- ERR_FAIL_COND_MSG(p_enable, "HTTPClient blocking mode is not supported for the HTML5 platform.");
+void HTTPClientJavaScript::set_blocking_mode(bool p_enable) {
+ ERR_FAIL_COND_MSG(p_enable, "HTTPClientJavaScript blocking mode is not supported for the HTML5 platform.");
}
-bool HTTPClient::is_blocking_mode_enabled() const {
+bool HTTPClientJavaScript::is_blocking_mode_enabled() const {
return false;
}
-void HTTPClient::set_read_chunk_size(int p_size) {
+void HTTPClientJavaScript::set_read_chunk_size(int p_size) {
read_limit = p_size;
}
-int HTTPClient::get_read_chunk_size() const {
+int HTTPClientJavaScript::get_read_chunk_size() const {
return read_limit;
}
-Error HTTPClient::poll() {
+Error HTTPClientJavaScript::poll() {
switch (status) {
case STATUS_DISCONNECTED:
return ERR_UNCONFIGURED;
@@ -217,48 +202,48 @@ Error HTTPClient::poll() {
return OK;
case STATUS_CONNECTED:
- case STATUS_BODY:
return OK;
+ case STATUS_BODY: {
+ godot_js_fetch_state_t state = godot_js_fetch_state_get(js_id);
+ if (state == GODOT_JS_FETCH_STATE_DONE) {
+ status = STATUS_DISCONNECTED;
+ } else if (state != GODOT_JS_FETCH_STATE_BODY) {
+ status = STATUS_CONNECTION_ERROR;
+ return ERR_CONNECTION_ERROR;
+ }
+ return OK;
+ }
+
case STATUS_CONNECTION_ERROR:
return ERR_CONNECTION_ERROR;
case STATUS_REQUESTING: {
#ifdef DEBUG_ENABLED
- if (!has_polled) {
- has_polled = true;
- } else {
- // forcing synchronous requests is not possible on the web
- if (last_polling_frame == Engine::get_singleton()->get_process_frames()) {
- WARN_PRINT("HTTPClient polled multiple times in one frame, "
- "but request cannot progress more than once per "
- "frame on the HTML5 platform.");
- }
+ // forcing synchronous requests is not possible on the web
+ if (last_polling_frame == Engine::get_singleton()->get_process_frames()) {
+ WARN_PRINT("HTTPClientJavaScript polled multiple times in one frame, "
+ "but request cannot progress more than once per "
+ "frame on the HTML5 platform.");
}
last_polling_frame = Engine::get_singleton()->get_process_frames();
#endif
- polled_response_code = godot_xhr_get_status(xhr_id);
- if (godot_xhr_get_ready_state(xhr_id) != XHR_READY_STATE_DONE) {
+ polled_response_code = godot_js_fetch_http_status_get(js_id);
+ godot_js_fetch_state_t js_state = godot_js_fetch_state_get(js_id);
+ if (js_state == GODOT_JS_FETCH_STATE_REQUESTING) {
return OK;
- } else if (!polled_response_code) {
+ } else if (js_state == GODOT_JS_FETCH_STATE_ERROR) {
+ // Fetch is in error state.
+ status = STATUS_CONNECTION_ERROR;
+ return ERR_CONNECTION_ERROR;
+ }
+ if (godot_js_fetch_read_headers(js_id, &_parse_headers, this)) {
+ // Failed to parse headers.
status = STATUS_CONNECTION_ERROR;
return ERR_CONNECTION_ERROR;
}
-
status = STATUS_BODY;
-
- PackedByteArray bytes;
- int len = godot_xhr_get_response_headers_length(xhr_id);
- bytes.resize(len + 1);
-
- godot_xhr_get_response_headers(xhr_id, reinterpret_cast<char *>(bytes.ptrw()), len);
- bytes.ptrw()[len] = 0;
-
- polled_response_header = String::utf8(reinterpret_cast<const char *>(bytes.ptr()));
-
- polled_response.resize(godot_xhr_get_response_length(xhr_id));
- godot_xhr_get_response(xhr_id, polled_response.ptrw(), polled_response.size());
break;
}
@@ -268,10 +253,15 @@ Error HTTPClient::poll() {
return OK;
}
-HTTPClient::HTTPClient() {
- xhr_id = godot_xhr_new();
+HTTPClient *HTTPClientJavaScript::_create_func() {
+ return memnew(HTTPClientJavaScript);
}
-HTTPClient::~HTTPClient() {
- godot_xhr_free(xhr_id);
+HTTPClient *(*HTTPClient::_create)() = HTTPClientJavaScript::_create_func;
+
+HTTPClientJavaScript::HTTPClientJavaScript() {
+}
+
+HTTPClientJavaScript::~HTTPClientJavaScript() {
+ close();
}
diff --git a/platform/javascript/http_client_javascript.h b/platform/javascript/http_client_javascript.h
new file mode 100644
index 0000000000..33f91f67b6
--- /dev/null
+++ b/platform/javascript/http_client_javascript.h
@@ -0,0 +1,108 @@
+/*************************************************************************/
+/* http_client_javascript.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 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 HTTP_CLIENT_JAVASCRIPT_H
+#define HTTP_CLIENT_JAVASCRIPT_H
+
+#include "core/io/http_client.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "stddef.h"
+
+typedef enum {
+ GODOT_JS_FETCH_STATE_REQUESTING = 0,
+ GODOT_JS_FETCH_STATE_BODY = 1,
+ GODOT_JS_FETCH_STATE_DONE = 2,
+ GODOT_JS_FETCH_STATE_ERROR = -1,
+} godot_js_fetch_state_t;
+
+extern int godot_js_fetch_create(const char *p_method, const char *p_url, const char **p_headers, int p_headers_len, const uint8_t *p_body, int p_body_len);
+extern int godot_js_fetch_read_headers(int p_id, void (*parse_callback)(int p_size, const char **p_headers, void *p_ref), void *p_ref);
+extern int godot_js_fetch_read_chunk(int p_id, uint8_t *p_buf, int p_buf_size);
+extern void godot_js_fetch_free(int p_id);
+extern godot_js_fetch_state_t godot_js_fetch_state_get(int p_id);
+extern int godot_js_fetch_body_length_get(int p_id);
+extern int godot_js_fetch_http_status_get(int p_id);
+extern int godot_js_fetch_is_chunked(int p_id);
+
+#ifdef __cplusplus
+}
+#endif
+
+class HTTPClientJavaScript : public HTTPClient {
+private:
+ int js_id = 0;
+ Status status = STATUS_DISCONNECTED;
+
+ // 64 KiB by default (favors fast download speeds at the cost of memory usage).
+ int read_limit = 65536;
+
+ String host;
+ int port = -1;
+ bool use_tls = false;
+
+ int polled_response_code = 0;
+ Vector<String> response_headers;
+ Vector<uint8_t> response_buffer;
+
+#ifdef DEBUG_ENABLED
+ uint64_t last_polling_frame = 0;
+#endif
+
+ static void _parse_headers(int p_len, const char **p_headers, void *p_ref);
+
+public:
+ static HTTPClient *_create_func();
+
+ Error request(Method p_method, const String &p_url, const Vector<String> &p_headers, const uint8_t *p_body, int p_body_size) override;
+
+ Error connect_to_host(const String &p_host, int p_port = -1, bool p_ssl = false, bool p_verify_host = true) override;
+ void set_connection(const Ref<StreamPeer> &p_connection) override;
+ Ref<StreamPeer> get_connection() const override;
+ void close() override;
+ Status get_status() const override;
+ bool has_response() const override;
+ bool is_response_chunked() const override;
+ int get_response_code() const override;
+ Error get_response_headers(List<String> *r_response) override;
+ int get_response_body_length() const override;
+ PackedByteArray read_response_body_chunk() override;
+ void set_blocking_mode(bool p_enable) override;
+ bool is_blocking_mode_enabled() const override;
+ void set_read_chunk_size(int p_size) override;
+ int get_read_chunk_size() const override;
+ Error poll() override;
+ HTTPClientJavaScript();
+ ~HTTPClientJavaScript();
+};
+#endif // HTTP_CLIENT_JAVASCRIPT_H
diff --git a/platform/javascript/http_request.h b/platform/javascript/http_request.h
deleted file mode 100644
index a9d6faade2..0000000000
--- a/platform/javascript/http_request.h
+++ /dev/null
@@ -1,73 +0,0 @@
-/*************************************************************************/
-/* http_request.h */
-/*************************************************************************/
-/* This file is part of: */
-/* GODOT ENGINE */
-/* https://godotengine.org */
-/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 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 HTTP_REQUEST_H
-#define HTTP_REQUEST_H
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#include "stddef.h"
-
-typedef enum {
- XHR_READY_STATE_UNSENT = 0,
- XHR_READY_STATE_OPENED = 1,
- XHR_READY_STATE_HEADERS_RECEIVED = 2,
- XHR_READY_STATE_LOADING = 3,
- XHR_READY_STATE_DONE = 4,
-} godot_xhr_ready_state_t;
-
-extern int godot_xhr_new();
-extern void godot_xhr_reset(int p_xhr_id);
-extern void godot_xhr_free(int p_xhr_id);
-
-extern int godot_xhr_open(int p_xhr_id, const char *p_method, const char *p_url, const char *p_user = nullptr, const char *p_password = nullptr);
-
-extern void godot_xhr_set_request_header(int p_xhr_id, const char *p_header, const char *p_value);
-
-extern void godot_xhr_send(int p_xhr_id, const void *p_data, int p_len);
-extern void godot_xhr_abort(int p_xhr_id);
-
-/* this is an HTTPClient::ResponseCode, not ::Status */
-extern int godot_xhr_get_status(int p_xhr_id);
-extern godot_xhr_ready_state_t godot_xhr_get_ready_state(int p_xhr_id);
-
-extern int godot_xhr_get_response_headers_length(int p_xhr_id);
-extern void godot_xhr_get_response_headers(int p_xhr_id, char *r_dst, int p_len);
-
-extern int godot_xhr_get_response_length(int p_xhr_id);
-extern void godot_xhr_get_response(int p_xhr_id, void *r_dst, int p_len);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* HTTP_REQUEST_H */
diff --git a/platform/javascript/javascript_eval.cpp b/platform/javascript/javascript_eval.cpp
deleted file mode 100644
index cb19dd20d4..0000000000
--- a/platform/javascript/javascript_eval.cpp
+++ /dev/null
@@ -1,79 +0,0 @@
-/*************************************************************************/
-/* javascript_eval.cpp */
-/*************************************************************************/
-/* This file is part of: */
-/* GODOT ENGINE */
-/* https://godotengine.org */
-/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 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 JAVASCRIPT_EVAL_ENABLED
-
-#include "api/javascript_eval.h"
-#include "emscripten.h"
-
-extern "C" {
-union js_eval_ret {
- uint32_t b;
- double d;
- char *s;
-};
-
-extern int godot_js_eval(const char *p_js, int p_use_global_ctx, union js_eval_ret *p_union_ptr, void *p_byte_arr, void *p_byte_arr_write, void *(*p_callback)(void *p_ptr, void *p_ptr2, int p_len));
-}
-
-void *resize_PackedByteArray_and_open_write(void *p_arr, void *r_write, int p_len) {
- PackedByteArray *arr = (PackedByteArray *)p_arr;
- VectorWriteProxy<uint8_t> *write = (VectorWriteProxy<uint8_t> *)r_write;
- arr->resize(p_len);
- *write = arr->write;
- return arr->ptrw();
-}
-
-Variant JavaScript::eval(const String &p_code, bool p_use_global_exec_context) {
- union js_eval_ret js_data;
- PackedByteArray arr;
- VectorWriteProxy<uint8_t> arr_write;
-
- Variant::Type return_type = static_cast<Variant::Type>(godot_js_eval(p_code.utf8().get_data(), p_use_global_exec_context, &js_data, &arr, &arr_write, resize_PackedByteArray_and_open_write));
-
- switch (return_type) {
- case Variant::BOOL:
- return js_data.b;
- case Variant::FLOAT:
- return js_data.d;
- case Variant::STRING: {
- String str = String::utf8(js_data.s);
- free(js_data.s); // Must free the string allocated in JS.
- return str;
- }
- case Variant::PACKED_BYTE_ARRAY:
- arr_write = VectorWriteProxy<uint8_t>();
- return arr;
- default:
- return Variant();
- }
-}
-
-#endif // JAVASCRIPT_EVAL_ENABLED
diff --git a/platform/javascript/javascript_main.cpp b/platform/javascript/javascript_main.cpp
index 0fe95b0a8f..40771d1882 100644
--- a/platform/javascript/javascript_main.cpp
+++ b/platform/javascript/javascript_main.cpp
@@ -66,6 +66,11 @@ void main_loop_callback() {
int target_fps = Engine::get_singleton()->get_target_fps();
if (target_fps > 0) {
+ if (current_ticks - target_ticks > 1000000) {
+ // When the window loses focus, we stop getting updates and accumulate delay.
+ // For this reason, if the difference is too big, we reset target ticks to the current ticks.
+ target_ticks = current_ticks;
+ }
target_ticks += (uint64_t)(1000000 / target_fps);
}
if (os->main_loop_iterate()) {
diff --git a/platform/javascript/javascript_singleton.cpp b/platform/javascript/javascript_singleton.cpp
new file mode 100644
index 0000000000..1dd73ef8e9
--- /dev/null
+++ b/platform/javascript/javascript_singleton.cpp
@@ -0,0 +1,357 @@
+/*************************************************************************/
+/* javascript_singleton.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 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 "api/javascript_singleton.h"
+#include "emscripten.h"
+
+extern "C" {
+extern void godot_js_os_download_buffer(const uint8_t *p_buf, int p_buf_size, const char *p_name, const char *p_mime);
+}
+
+#ifdef JAVASCRIPT_EVAL_ENABLED
+
+extern "C" {
+typedef union {
+ int64_t i;
+ double r;
+ void *p;
+} godot_js_wrapper_ex;
+
+typedef int (*GodotJSWrapperVariant2JSCallback)(const void **p_args, int p_pos, godot_js_wrapper_ex *r_val, void **p_lock);
+typedef void (*GodotJSWrapperFreeLockCallback)(void **p_lock, int p_type);
+extern int godot_js_wrapper_interface_get(const char *p_name);
+extern int godot_js_wrapper_object_call(int p_id, const char *p_method, void **p_args, int p_argc, GodotJSWrapperVariant2JSCallback p_variant2js_callback, godot_js_wrapper_ex *p_cb_rval, void **p_lock, GodotJSWrapperFreeLockCallback p_lock_callback);
+extern int godot_js_wrapper_object_get(int p_id, godot_js_wrapper_ex *p_val, const char *p_prop);
+extern int godot_js_wrapper_object_getvar(int p_id, int p_type, godot_js_wrapper_ex *p_val);
+extern int godot_js_wrapper_object_setvar(int p_id, int p_key_type, godot_js_wrapper_ex *p_key_ex, int p_val_type, godot_js_wrapper_ex *p_val_ex);
+extern void godot_js_wrapper_object_set(int p_id, const char *p_name, int p_type, godot_js_wrapper_ex *p_val);
+extern void godot_js_wrapper_object_unref(int p_id);
+extern int godot_js_wrapper_create_cb(void *p_ref, void (*p_callback)(void *p_ref, int p_arg_id, int p_argc));
+extern void godot_js_wrapper_object_set_cb_ret(int p_type, godot_js_wrapper_ex *p_val);
+extern int godot_js_wrapper_create_object(const char *p_method, void **p_args, int p_argc, GodotJSWrapperVariant2JSCallback p_variant2js_callback, godot_js_wrapper_ex *p_cb_rval, void **p_lock, GodotJSWrapperFreeLockCallback p_lock_callback);
+};
+
+class JavaScriptObjectImpl : public JavaScriptObject {
+private:
+ friend class JavaScript;
+
+ int _js_id = 0;
+ Callable _callable;
+
+ static int _variant2js(const void **p_args, int p_pos, godot_js_wrapper_ex *r_val, void **p_lock);
+ static void _free_lock(void **p_lock, int p_type);
+ static Variant _js2variant(int p_type, godot_js_wrapper_ex *p_val);
+ static void *_alloc_variants(int p_size);
+ static void _callback(void *p_ref, int p_arg_id, int p_argc);
+
+protected:
+ bool _set(const StringName &p_name, const Variant &p_value) override;
+ bool _get(const StringName &p_name, Variant &r_ret) const override;
+ void _get_property_list(List<PropertyInfo> *p_list) const override;
+
+public:
+ Variant getvar(const Variant &p_key, bool *r_valid = nullptr) const override;
+ void setvar(const Variant &p_key, const Variant &p_value, bool *r_valid = nullptr) override;
+ Variant call(const StringName &p_method, const Variant **p_args, int p_argc, Callable::CallError &r_error) override;
+ JavaScriptObjectImpl() {}
+ JavaScriptObjectImpl(int p_id) { _js_id = p_id; }
+ ~JavaScriptObjectImpl() {
+ if (_js_id) {
+ godot_js_wrapper_object_unref(_js_id);
+ }
+ }
+};
+
+bool JavaScriptObjectImpl::_set(const StringName &p_name, const Variant &p_value) {
+ ERR_FAIL_COND_V_MSG(!_js_id, false, "Invalid JS instance");
+ const String name = p_name;
+ godot_js_wrapper_ex exchange;
+ void *lock = nullptr;
+ const Variant *v = &p_value;
+ int type = _variant2js((const void **)&v, 0, &exchange, &lock);
+ godot_js_wrapper_object_set(_js_id, name.utf8().get_data(), type, &exchange);
+ if (lock) {
+ _free_lock(&lock, type);
+ }
+ return true;
+}
+
+bool JavaScriptObjectImpl::_get(const StringName &p_name, Variant &r_ret) const {
+ ERR_FAIL_COND_V_MSG(!_js_id, false, "Invalid JS instance");
+ const String name = p_name;
+ godot_js_wrapper_ex exchange;
+ int type = godot_js_wrapper_object_get(_js_id, &exchange, name.utf8().get_data());
+ r_ret = _js2variant(type, &exchange);
+ return true;
+}
+
+Variant JavaScriptObjectImpl::getvar(const Variant &p_key, bool *r_valid) const {
+ if (r_valid) {
+ *r_valid = false;
+ }
+ godot_js_wrapper_ex exchange;
+ void *lock = nullptr;
+ const Variant *v = &p_key;
+ int prop_type = _variant2js((const void **)&v, 0, &exchange, &lock);
+ int type = godot_js_wrapper_object_getvar(_js_id, prop_type, &exchange);
+ if (lock) {
+ _free_lock(&lock, prop_type);
+ }
+ if (type < 0) {
+ return Variant();
+ }
+ if (r_valid) {
+ *r_valid = true;
+ }
+ return _js2variant(type, &exchange);
+}
+
+void JavaScriptObjectImpl::setvar(const Variant &p_key, const Variant &p_value, bool *r_valid) {
+ if (r_valid) {
+ *r_valid = false;
+ }
+ godot_js_wrapper_ex kex, vex;
+ void *klock = nullptr;
+ void *vlock = nullptr;
+ const Variant *kv = &p_key;
+ const Variant *vv = &p_value;
+ int ktype = _variant2js((const void **)&kv, 0, &kex, &klock);
+ int vtype = _variant2js((const void **)&vv, 0, &vex, &vlock);
+ int ret = godot_js_wrapper_object_setvar(_js_id, ktype, &kex, vtype, &vex);
+ if (klock) {
+ _free_lock(&klock, ktype);
+ }
+ if (vlock) {
+ _free_lock(&vlock, vtype);
+ }
+ if (ret == 0 && r_valid) {
+ *r_valid = true;
+ }
+}
+
+void JavaScriptObjectImpl::_get_property_list(List<PropertyInfo> *p_list) const {
+}
+
+void JavaScriptObjectImpl::_free_lock(void **p_lock, int p_type) {
+ ERR_FAIL_COND_MSG(*p_lock == nullptr, "No lock to free!");
+ const Variant::Type type = (Variant::Type)p_type;
+ switch (type) {
+ case Variant::STRING: {
+ CharString *cs = (CharString *)(*p_lock);
+ memdelete(cs);
+ *p_lock = nullptr;
+ } break;
+ default:
+ ERR_FAIL_MSG("Unknown lock type to free. Likely a bug.");
+ }
+}
+
+Variant JavaScriptObjectImpl::_js2variant(int p_type, godot_js_wrapper_ex *p_val) {
+ Variant::Type type = (Variant::Type)p_type;
+ switch (type) {
+ case Variant::BOOL:
+ return Variant((bool)p_val->i);
+ case Variant::INT:
+ return p_val->i;
+ case Variant::FLOAT:
+ return p_val->r;
+ case Variant::STRING: {
+ String out = String::utf8((const char *)p_val->p);
+ free(p_val->p);
+ return out;
+ }
+ case Variant::OBJECT: {
+ return memnew(JavaScriptObjectImpl(p_val->i));
+ }
+ default:
+ return Variant();
+ }
+}
+
+int JavaScriptObjectImpl::_variant2js(const void **p_args, int p_pos, godot_js_wrapper_ex *r_val, void **p_lock) {
+ const Variant **args = (const Variant **)p_args;
+ const Variant *v = args[p_pos];
+ Variant::Type type = v->get_type();
+ switch (type) {
+ case Variant::BOOL:
+ r_val->i = v->operator bool() ? 1 : 0;
+ break;
+ case Variant::INT: {
+ const int64_t tmp = v->operator int64_t();
+ if (tmp >= 1 << 31) {
+ r_val->r = (double)tmp;
+ return Variant::FLOAT;
+ }
+ r_val->i = v->operator int64_t();
+ } break;
+ case Variant::FLOAT:
+ r_val->r = v->operator real_t();
+ break;
+ case Variant::STRING: {
+ CharString *cs = memnew(CharString(v->operator String().utf8()));
+ r_val->p = (void *)cs->get_data();
+ *p_lock = (void *)cs;
+ } break;
+ case Variant::OBJECT: {
+ JavaScriptObject *js_obj = Object::cast_to<JavaScriptObject>(v->operator Object *());
+ r_val->i = js_obj != nullptr ? ((JavaScriptObjectImpl *)js_obj)->_js_id : 0;
+ } break;
+ default:
+ break;
+ }
+ return type;
+}
+
+Variant JavaScriptObjectImpl::call(const StringName &p_method, const Variant **p_args, int p_argc, Callable::CallError &r_error) {
+ godot_js_wrapper_ex exchange;
+ const String method = p_method;
+ void *lock = nullptr;
+ const int type = godot_js_wrapper_object_call(_js_id, method.utf8().get_data(), (void **)p_args, p_argc, &_variant2js, &exchange, &lock, &_free_lock);
+ r_error.error = Callable::CallError::CALL_OK;
+ if (type < 0) {
+ r_error.error = Callable::CallError::CALL_ERROR_INSTANCE_IS_NULL;
+ return Variant();
+ }
+ return _js2variant(type, &exchange);
+}
+
+void JavaScriptObjectImpl::_callback(void *p_ref, int p_args_id, int p_argc) {
+ const JavaScriptObjectImpl *obj = (JavaScriptObjectImpl *)p_ref;
+ ERR_FAIL_COND_MSG(obj->_callable.is_null(), "JavaScript callback failed.");
+ Vector<const Variant *> argp;
+ Array arg_arr;
+ for (int i = 0; i < p_argc; i++) {
+ godot_js_wrapper_ex exchange;
+ exchange.i = i;
+ int type = godot_js_wrapper_object_getvar(p_args_id, Variant::INT, &exchange);
+ arg_arr.push_back(_js2variant(type, &exchange));
+ }
+ Variant arg = arg_arr;
+ const Variant *argv[1] = { &arg };
+ Callable::CallError err;
+ Variant ret;
+ obj->_callable.call(argv, 1, ret, err);
+
+ // Set return value
+ godot_js_wrapper_ex exchange;
+ void *lock = nullptr;
+ const Variant *v = &ret;
+ int type = _variant2js((const void **)&v, 0, &exchange, &lock);
+ godot_js_wrapper_object_set_cb_ret(type, &exchange);
+ if (lock) {
+ _free_lock(&lock, type);
+ }
+}
+
+Ref<JavaScriptObject> JavaScript::create_callback(const Callable &p_callable) {
+ Ref<JavaScriptObjectImpl> out = memnew(JavaScriptObjectImpl);
+ out->_callable = p_callable;
+ out->_js_id = godot_js_wrapper_create_cb(out.ptr(), JavaScriptObjectImpl::_callback);
+ return out;
+}
+
+Ref<JavaScriptObject> JavaScript::get_interface(const String &p_interface) {
+ int js_id = godot_js_wrapper_interface_get(p_interface.utf8().get_data());
+ ERR_FAIL_COND_V_MSG(!js_id, Ref<JavaScriptObject>(), "No interface '" + p_interface + "' registered.");
+ return Ref<JavaScriptObject>(memnew(JavaScriptObjectImpl(js_id)));
+}
+
+Variant JavaScript::_create_object_bind(const Variant **p_args, int p_argcount, Callable::CallError &r_error) {
+ if (p_argcount < 1) {
+ r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS;
+ r_error.argument = 0;
+ return Ref<JavaScriptObject>();
+ }
+ if (p_args[0]->get_type() != Variant::STRING) {
+ r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
+ r_error.argument = 0;
+ r_error.expected = Variant::STRING;
+ return Ref<JavaScriptObject>();
+ }
+ godot_js_wrapper_ex exchange;
+ const String object = *p_args[0];
+ void *lock = nullptr;
+ const Variant **args = p_argcount > 1 ? &p_args[1] : nullptr;
+ const int type = godot_js_wrapper_create_object(object.utf8().get_data(), (void **)args, p_argcount - 1, &JavaScriptObjectImpl::_variant2js, &exchange, &lock, &JavaScriptObjectImpl::_free_lock);
+ r_error.error = Callable::CallError::CALL_OK;
+ if (type < 0) {
+ r_error.error = Callable::CallError::CALL_ERROR_INSTANCE_IS_NULL;
+ return Ref<JavaScriptObject>();
+ }
+ return JavaScriptObjectImpl::_js2variant(type, &exchange);
+}
+
+extern "C" {
+union js_eval_ret {
+ uint32_t b;
+ double d;
+ char *s;
+};
+
+extern int godot_js_eval(const char *p_js, int p_use_global_ctx, union js_eval_ret *p_union_ptr, void *p_byte_arr, void *p_byte_arr_write, void *(*p_callback)(void *p_ptr, void *p_ptr2, int p_len));
+}
+
+void *resize_PackedByteArray_and_open_write(void *p_arr, void *r_write, int p_len) {
+ PackedByteArray *arr = (PackedByteArray *)p_arr;
+ VectorWriteProxy<uint8_t> *write = (VectorWriteProxy<uint8_t> *)r_write;
+ arr->resize(p_len);
+ *write = arr->write;
+ return arr->ptrw();
+}
+
+Variant JavaScript::eval(const String &p_code, bool p_use_global_exec_context) {
+ union js_eval_ret js_data;
+ PackedByteArray arr;
+ VectorWriteProxy<uint8_t> arr_write;
+
+ Variant::Type return_type = static_cast<Variant::Type>(godot_js_eval(p_code.utf8().get_data(), p_use_global_exec_context, &js_data, &arr, &arr_write, resize_PackedByteArray_and_open_write));
+
+ switch (return_type) {
+ case Variant::BOOL:
+ return js_data.b;
+ case Variant::FLOAT:
+ return js_data.d;
+ case Variant::STRING: {
+ String str = String::utf8(js_data.s);
+ free(js_data.s); // Must free the string allocated in JS.
+ return str;
+ }
+ case Variant::PACKED_BYTE_ARRAY:
+ arr_write = VectorWriteProxy<uint8_t>();
+ return arr;
+ default:
+ return Variant();
+ }
+}
+#endif // JAVASCRIPT_EVAL_ENABLED
+
+void JavaScript::download_buffer(Vector<uint8_t> p_arr, const String &p_name, const String &p_mime) {
+ godot_js_os_download_buffer(p_arr.ptr(), p_arr.size(), p_name.utf8().get_data(), p_mime.utf8().get_data());
+}
diff --git a/platform/javascript/js/engine/config.js b/platform/javascript/js/engine/config.js
index bba20dc360..ba61b14eb7 100644
--- a/platform/javascript/js/engine/config.js
+++ b/platform/javascript/js/engine/config.js
@@ -91,16 +91,42 @@ const InternalConfig = function (initConfig) { // eslint-disable-line no-unused-
*/
args: [],
/**
+ * When enabled, the game canvas will automatically grab the focus when the engine starts.
+ *
+ * @memberof EngineConfig
+ * @type {boolean}
+ * @default
+ */
+ focusCanvas: true,
+ /**
+ * When enabled, this will turn on experimental virtual keyboard support on mobile.
+ *
+ * @memberof EngineConfig
+ * @type {boolean}
+ * @default
+ */
+ experimentalVK: false,
+ /**
* @ignore
* @type {Array.<string>}
*/
persistentPaths: ['/userfs'],
/**
* @ignore
+ * @type {boolean}
+ */
+ persistentDrops: false,
+ /**
+ * @ignore
* @type {Array.<string>}
*/
gdnativeLibs: [],
/**
+ * @ignore
+ * @type {Array.<string>}
+ */
+ fileSizes: [],
+ /**
* A callback function for handling Godot's ``OS.execute`` calls.
*
* This is for example used in the Web Editor template to switch between project manager and editor, and for running the game.
@@ -218,7 +244,11 @@ const InternalConfig = function (initConfig) { // eslint-disable-line no-unused-
this.locale = parse('locale', this.locale);
this.canvasResizePolicy = parse('canvasResizePolicy', this.canvasResizePolicy);
this.persistentPaths = parse('persistentPaths', this.persistentPaths);
+ this.persistentDrops = parse('persistentDrops', this.persistentDrops);
+ this.experimentalVK = parse('experimentalVK', this.experimentalVK);
+ this.focusCanvas = parse('focusCanvas', this.focusCanvas);
this.gdnativeLibs = parse('gdnativeLibs', this.gdnativeLibs);
+ this.fileSizes = parse('fileSizes', this.fileSizes);
this.args = parse('args', this.args);
this.onExecute = parse('onExecute', this.onExecute);
this.onExit = parse('onExit', this.onExit);
@@ -227,10 +257,10 @@ const InternalConfig = function (initConfig) { // eslint-disable-line no-unused-
/**
* @ignore
* @param {string} loadPath
- * @param {Promise} loadPromise
+ * @param {Response} response
*/
- Config.prototype.getModuleConfig = function (loadPath, loadPromise) {
- let loader = loadPromise;
+ Config.prototype.getModuleConfig = function (loadPath, response) {
+ let r = response;
return {
'print': this.onPrint,
'printErr': this.onPrintError,
@@ -238,12 +268,17 @@ const InternalConfig = function (initConfig) { // eslint-disable-line no-unused-
'noExitRuntime': true,
'dynamicLibraries': [`${loadPath}.side.wasm`],
'instantiateWasm': function (imports, onSuccess) {
- loader.then(function (xhr) {
- WebAssembly.instantiate(xhr.response, imports).then(function (result) {
- onSuccess(result['instance'], result['module']);
+ function done(result) {
+ onSuccess(result['instance'], result['module']);
+ }
+ if (typeof (WebAssembly.instantiateStreaming) !== 'undefined') {
+ WebAssembly.instantiateStreaming(Promise.resolve(r), imports).then(done);
+ } else {
+ r.arrayBuffer().then(function (buffer) {
+ WebAssembly.instantiate(buffer, imports).then(done);
});
- });
- loader = null;
+ }
+ r = null;
return {};
},
'locateFile': function (path) {
@@ -296,6 +331,9 @@ const InternalConfig = function (initConfig) { // eslint-disable-line no-unused-
'canvas': this.canvas,
'canvasResizePolicy': this.canvasResizePolicy,
'locale': locale,
+ 'persistentDrops': this.persistentDrops,
+ 'virtualKeyboard': this.experimentalVK,
+ 'focusCanvas': this.focusCanvas,
'onExecute': this.onExecute,
'onExit': function (p_code) {
cleanup(); // We always need to call the cleanup callback to free memory.
diff --git a/platform/javascript/js/engine/engine.externs.js b/platform/javascript/js/engine/engine.externs.js
index 1a94dd15ec..35a66a93ae 100644
--- a/platform/javascript/js/engine/engine.externs.js
+++ b/platform/javascript/js/engine/engine.externs.js
@@ -1,3 +1,4 @@
var Godot;
var WebAssembly = {};
WebAssembly.instantiate = function(buffer, imports) {};
+WebAssembly.instantiateStreaming = function(response, imports) {};
diff --git a/platform/javascript/js/engine/engine.js b/platform/javascript/js/engine/engine.js
index c51955ed3d..17a8df9e29 100644
--- a/platform/javascript/js/engine/engine.js
+++ b/platform/javascript/js/engine/engine.js
@@ -35,14 +35,15 @@ const Engine = (function () {
* Load the engine from the specified base path.
*
* @param {string} basePath Base path of the engine to load.
+ * @param {number=} [size=0] The file size if known.
* @returns {Promise} A Promise that resolves once the engine is loaded.
*
* @function Engine.load
*/
- Engine.load = function (basePath) {
+ Engine.load = function (basePath, size) {
if (loadPromise == null) {
loadPath = basePath;
- loadPromise = preloader.loadPromise(`${loadPath}.wasm`);
+ loadPromise = preloader.loadPromise(`${loadPath}.wasm`, size, true);
requestAnimationFrame(preloader.animateProgress);
}
return loadPromise;
@@ -96,23 +97,31 @@ const Engine = (function () {
initPromise = Promise.reject(new Error('A base path must be provided when calling `init` and the engine is not loaded.'));
return initPromise;
}
- Engine.load(basePath);
+ Engine.load(basePath, this.config.fileSizes[`${basePath}.wasm`]);
}
- preloader.setProgressFunc(this.config.onProgress);
- let config = this.config.getModuleConfig(loadPath, loadPromise);
const me = this;
- initPromise = new Promise(function (resolve, reject) {
- Godot(config).then(function (module) {
- module['initFS'](me.config.persistentPaths).then(function (fs_err) {
- me.rtenv = module;
- if (me.config.unloadAfterInit) {
- Engine.unload();
- }
- resolve();
- config = null;
+ function doInit(promise) {
+ // Care! Promise chaining is bogus with old emscripten versions.
+ // This caused a regression with the Mono build (which uses an older emscripten version).
+ // Make sure to test that when refactoring.
+ return new Promise(function (resolve, reject) {
+ promise.then(function (response) {
+ const cloned = new Response(response.clone().body, { 'headers': [['content-type', 'application/wasm']] });
+ Godot(me.config.getModuleConfig(loadPath, cloned)).then(function (module) {
+ const paths = me.config.persistentPaths;
+ module['initFS'](paths).then(function (err) {
+ me.rtenv = module;
+ if (me.config.unloadAfterInit) {
+ Engine.unload();
+ }
+ resolve();
+ });
+ });
});
});
- });
+ }
+ preloader.setProgressFunc(this.config.onProgress);
+ initPromise = doInit(loadPromise);
return initPromise;
},
@@ -133,7 +142,7 @@ const Engine = (function () {
* @returns {Promise} A Promise that resolves once the file is loaded.
*/
preloadFile: function (file, path) {
- return preloader.preload(file, path);
+ return preloader.preload(file, path, this.config.fileSizes[file]);
},
/**
diff --git a/platform/javascript/js/engine/preloader.js b/platform/javascript/js/engine/preloader.js
index ec34fb93f2..564c68d264 100644
--- a/platform/javascript/js/engine/preloader.js
+++ b/platform/javascript/js/engine/preloader.js
@@ -1,54 +1,62 @@
const Preloader = /** @constructor */ function () { // eslint-disable-line no-unused-vars
- const loadXHR = function (resolve, reject, file, tracker, attempts) {
- const xhr = new XMLHttpRequest();
+ function getTrackedResponse(response, load_status) {
+ function onloadprogress(reader, controller) {
+ return reader.read().then(function (result) {
+ if (load_status.done) {
+ return Promise.resolve();
+ }
+ if (result.value) {
+ controller.enqueue(result.value);
+ load_status.loaded += result.value.length;
+ }
+ if (!result.done) {
+ return onloadprogress(reader, controller);
+ }
+ load_status.done = true;
+ return Promise.resolve();
+ });
+ }
+ const reader = response.body.getReader();
+ return new Response(new ReadableStream({
+ start: function (controller) {
+ onloadprogress(reader, controller).then(function () {
+ controller.close();
+ });
+ },
+ }), { headers: response.headers });
+ }
+
+ function loadFetch(file, tracker, fileSize, raw) {
tracker[file] = {
- total: 0,
+ total: fileSize || 0,
loaded: 0,
- final: false,
+ done: false,
};
- xhr.onerror = function () {
+ return fetch(file).then(function (response) {
+ if (!response.ok) {
+ return Promise.reject(new Error(`Failed loading file '${file}'`));
+ }
+ const tr = getTrackedResponse(response, tracker[file]);
+ if (raw) {
+ return Promise.resolve(tr);
+ }
+ return tr.arrayBuffer();
+ });
+ }
+
+ function retry(func, attempts = 1) {
+ function onerror(err) {
if (attempts <= 1) {
- reject(new Error(`Failed loading file '${file}'`));
- } else {
+ return Promise.reject(err);
+ }
+ return new Promise(function (resolve, reject) {
setTimeout(function () {
- loadXHR(resolve, reject, file, tracker, attempts - 1);
+ retry(func, attempts - 1).then(resolve).catch(reject);
}, 1000);
- }
- };
- xhr.onabort = function () {
- tracker[file].final = true;
- reject(new Error(`Loading file '${file}' was aborted.`));
- };
- xhr.onloadstart = function (ev) {
- tracker[file].total = ev.total;
- tracker[file].loaded = ev.loaded;
- };
- xhr.onprogress = function (ev) {
- tracker[file].loaded = ev.loaded;
- tracker[file].total = ev.total;
- };
- xhr.onload = function () {
- if (xhr.status >= 400) {
- if (xhr.status < 500 || attempts <= 1) {
- reject(new Error(`Failed loading file '${file}': ${xhr.statusText}`));
- xhr.abort();
- } else {
- setTimeout(function () {
- loadXHR(resolve, reject, file, tracker, attempts - 1);
- }, 1000);
- }
- } else {
- tracker[file].final = true;
- resolve(xhr);
- }
- };
- // Make request.
- xhr.open('GET', file);
- if (!file.endsWith('.js')) {
- xhr.responseType = 'arraybuffer';
+ });
}
- xhr.send();
- };
+ return func().catch(onerror);
+ }
const DOWNLOAD_ATTEMPTS_MAX = 4;
const loadingFiles = {};
@@ -63,7 +71,7 @@ const Preloader = /** @constructor */ function () { // eslint-disable-line no-un
Object.keys(loadingFiles).forEach(function (file) {
const stat = loadingFiles[file];
- if (!stat.final) {
+ if (!stat.done) {
progressIsFinal = false;
}
if (!totalIsValid || stat.total === 0) {
@@ -92,21 +100,19 @@ const Preloader = /** @constructor */ function () { // eslint-disable-line no-un
progressFunc = callback;
};
- this.loadPromise = function (file) {
- return new Promise(function (resolve, reject) {
- loadXHR(resolve, reject, file, loadingFiles, DOWNLOAD_ATTEMPTS_MAX);
- });
+ this.loadPromise = function (file, fileSize, raw = false) {
+ return retry(loadFetch.bind(null, file, loadingFiles, fileSize, raw), DOWNLOAD_ATTEMPTS_MAX);
};
this.preloadedFiles = [];
- this.preload = function (pathOrBuffer, destPath) {
+ this.preload = function (pathOrBuffer, destPath, fileSize) {
let buffer = null;
if (typeof pathOrBuffer === 'string') {
const me = this;
- return this.loadPromise(pathOrBuffer).then(function (xhr) {
+ return this.loadPromise(pathOrBuffer, fileSize).then(function (buf) {
me.preloadedFiles.push({
path: destPath || pathOrBuffer,
- buffer: xhr.response,
+ buffer: buf,
});
return Promise.resolve();
});
diff --git a/platform/javascript/js/libs/library_godot_audio.js b/platform/javascript/js/libs/library_godot_audio.js
index 8e385e9176..45c3a3fe2e 100644
--- a/platform/javascript/js/libs/library_godot_audio.js
+++ b/platform/javascript/js/libs/library_godot_audio.js
@@ -59,7 +59,7 @@ const GodotAudio = {
}
onstatechange(state);
};
- ctx.onstatechange(); // Immeditately notify state.
+ ctx.onstatechange(); // Immediately notify state.
// Update computed latency
GodotAudio.interval = setInterval(function () {
let computed_latency = 0;
@@ -238,6 +238,9 @@ const GodotAudioWorklet = {
close: function () {
return new Promise(function (resolve, reject) {
+ if (GodotAudioWorklet.promise === null) {
+ return;
+ }
GodotAudioWorklet.promise.then(function () {
GodotAudioWorklet.worklet.port.postMessage({
'cmd': 'stop',
@@ -247,7 +250,7 @@ const GodotAudioWorklet = {
GodotAudioWorklet.worklet = null;
GodotAudioWorklet.promise = null;
resolve();
- });
+ }).catch(function (err) { /* aborted? */ });
});
},
},
diff --git a/platform/javascript/js/libs/library_godot_display.js b/platform/javascript/js/libs/library_godot_display.js
index fdb5cc0ec2..affae90370 100644
--- a/platform/javascript/js/libs/library_godot_display.js
+++ b/platform/javascript/js/libs/library_godot_display.js
@@ -192,33 +192,45 @@ const GodotDisplayDragDrop = {
GodotDisplayDragDrop.promises = [];
GodotDisplayDragDrop.pending_files = [];
callback(drops);
- const dirs = [DROP.substr(0, DROP.length - 1)];
- // Remove temporary files
- files.forEach(function (file) {
- FS.unlink(file);
- let dir = file.replace(DROP, '');
- let idx = dir.lastIndexOf('/');
- while (idx > 0) {
- dir = dir.substr(0, idx);
- if (dirs.indexOf(DROP + dir) === -1) {
- dirs.push(DROP + dir);
- }
- idx = dir.lastIndexOf('/');
- }
- });
- // Remove dirs.
- dirs.sort(function (a, b) {
- const al = (a.match(/\//g) || []).length;
- const bl = (b.match(/\//g) || []).length;
- if (al > bl) {
- return -1;
- } else if (al < bl) {
- return 1;
+ if (GodotConfig.persistent_drops) {
+ // Delay removal at exit.
+ GodotOS.atexit(function (resolve, reject) {
+ GodotDisplayDragDrop.remove_drop(files, DROP);
+ resolve();
+ });
+ } else {
+ GodotDisplayDragDrop.remove_drop(files, DROP);
+ }
+ });
+ },
+
+ remove_drop: function (files, drop_path) {
+ const dirs = [drop_path.substr(0, drop_path.length - 1)];
+ // Remove temporary files
+ files.forEach(function (file) {
+ FS.unlink(file);
+ let dir = file.replace(drop_path, '');
+ let idx = dir.lastIndexOf('/');
+ while (idx > 0) {
+ dir = dir.substr(0, idx);
+ if (dirs.indexOf(drop_path + dir) === -1) {
+ dirs.push(drop_path + dir);
}
- return 0;
- }).forEach(function (dir) {
- FS.rmdir(dir);
- });
+ idx = dir.lastIndexOf('/');
+ }
+ });
+ // Remove dirs.
+ dirs.sort(function (a, b) {
+ const al = (a.match(/\//g) || []).length;
+ const bl = (b.match(/\//g) || []).length;
+ if (al > bl) {
+ return -1;
+ } else if (al < bl) {
+ return 1;
+ }
+ return 0;
+ }).forEach(function (dir) {
+ FS.rmdir(dir);
});
},
@@ -231,6 +243,105 @@ const GodotDisplayDragDrop = {
};
mergeInto(LibraryManager.library, GodotDisplayDragDrop);
+const GodotDisplayVK = {
+
+ $GodotDisplayVK__deps: ['$GodotRuntime', '$GodotConfig', '$GodotDisplayListeners'],
+ $GodotDisplayVK__postset: 'GodotOS.atexit(function(resolve, reject) { GodotDisplayVK.clear(); resolve(); });',
+ $GodotDisplayVK: {
+ textinput: null,
+ textarea: null,
+
+ available: function () {
+ return GodotConfig.virtual_keyboard && 'ontouchstart' in window;
+ },
+
+ init: function (input_cb) {
+ function create(what) {
+ const elem = document.createElement(what);
+ elem.style.display = 'none';
+ elem.style.position = 'absolute';
+ elem.style.zIndex = '-1';
+ elem.style.background = 'transparent';
+ elem.style.padding = '0px';
+ elem.style.margin = '0px';
+ elem.style.overflow = 'hidden';
+ elem.style.width = '0px';
+ elem.style.height = '0px';
+ elem.style.border = '0px';
+ elem.style.outline = 'none';
+ elem.readonly = true;
+ elem.disabled = true;
+ GodotDisplayListeners.add(elem, 'input', function (evt) {
+ const c_str = GodotRuntime.allocString(elem.value);
+ input_cb(c_str, elem.selectionEnd);
+ GodotRuntime.free(c_str);
+ }, false);
+ GodotDisplayListeners.add(elem, 'blur', function (evt) {
+ elem.style.display = 'none';
+ elem.readonly = true;
+ elem.disabled = true;
+ }, false);
+ GodotConfig.canvas.insertAdjacentElement('beforebegin', elem);
+ return elem;
+ }
+ GodotDisplayVK.textinput = create('input');
+ GodotDisplayVK.textarea = create('textarea');
+ GodotDisplayVK.updateSize();
+ },
+ show: function (text, multiline, start, end) {
+ if (!GodotDisplayVK.textinput || !GodotDisplayVK.textarea) {
+ return;
+ }
+ if (GodotDisplayVK.textinput.style.display !== '' || GodotDisplayVK.textarea.style.display !== '') {
+ GodotDisplayVK.hide();
+ }
+ GodotDisplayVK.updateSize();
+ const elem = multiline ? GodotDisplayVK.textarea : GodotDisplayVK.textinput;
+ elem.readonly = false;
+ elem.disabled = false;
+ elem.value = text;
+ elem.style.display = 'block';
+ elem.focus();
+ elem.setSelectionRange(start, end);
+ },
+ hide: function () {
+ if (!GodotDisplayVK.textinput || !GodotDisplayVK.textarea) {
+ return;
+ }
+ [GodotDisplayVK.textinput, GodotDisplayVK.textarea].forEach(function (elem) {
+ elem.blur();
+ elem.style.display = 'none';
+ elem.value = '';
+ });
+ },
+ updateSize: function () {
+ if (!GodotDisplayVK.textinput || !GodotDisplayVK.textarea) {
+ return;
+ }
+ const rect = GodotConfig.canvas.getBoundingClientRect();
+ function update(elem) {
+ elem.style.left = `${rect.left}px`;
+ elem.style.top = `${rect.top}px`;
+ elem.style.width = `${rect.width}px`;
+ elem.style.height = `${rect.height}px`;
+ }
+ update(GodotDisplayVK.textinput);
+ update(GodotDisplayVK.textarea);
+ },
+ clear: function () {
+ if (GodotDisplayVK.textinput) {
+ GodotDisplayVK.textinput.remove();
+ GodotDisplayVK.textinput = null;
+ }
+ if (GodotDisplayVK.textarea) {
+ GodotDisplayVK.textarea.remove();
+ GodotDisplayVK.textarea = null;
+ }
+ },
+ },
+};
+mergeInto(LibraryManager.library, GodotDisplayVK);
+
/*
* Display server cursor helper.
* Keeps track of cursor status and custom shapes.
@@ -400,6 +511,10 @@ const GodotDisplayScreen = {
$GodotDisplayScreen__deps: ['$GodotConfig', '$GodotOS', '$GL', 'emscripten_webgl_get_current_context'],
$GodotDisplayScreen: {
desired_size: [0, 0],
+ hidpi: true,
+ getPixelRatio: function () {
+ return GodotDisplayScreen.hidpi ? window.devicePixelRatio || 1 : 1;
+ },
isFullscreen: function () {
const elem = document.fullscreenElement || document.mozFullscreenElement
|| document.webkitFullscreenElement || document.msFullscreenElement;
@@ -477,7 +592,7 @@ const GodotDisplayScreen = {
}
return 0;
}
- const scale = window.devicePixelRatio || 1;
+ const scale = GodotDisplayScreen.getPixelRatio();
if (isFullscreen || wantsFullWindow) {
// We need to match screen size.
width = window.innerWidth * scale;
@@ -507,7 +622,7 @@ mergeInto(LibraryManager.library, GodotDisplayScreen);
* Exposes all the functions needed by DisplayServer implementation.
*/
const GodotDisplay = {
- $GodotDisplay__deps: ['$GodotConfig', '$GodotRuntime', '$GodotDisplayCursor', '$GodotDisplayListeners', '$GodotDisplayDragDrop', '$GodotDisplayGamepads', '$GodotDisplayScreen'],
+ $GodotDisplay__deps: ['$GodotConfig', '$GodotRuntime', '$GodotDisplayCursor', '$GodotDisplayListeners', '$GodotDisplayDragDrop', '$GodotDisplayGamepads', '$GodotDisplayScreen', '$GodotDisplayVK'],
$GodotDisplay: {
window_icon: '',
findDPI: function () {
@@ -555,7 +670,7 @@ const GodotDisplay = {
godot_js_display_pixel_ratio_get__sig: 'f',
godot_js_display_pixel_ratio_get: function () {
- return window.devicePixelRatio || 1;
+ return GodotDisplayScreen.getPixelRatio();
},
godot_js_display_fullscreen_request__sig: 'i',
@@ -568,7 +683,7 @@ const GodotDisplay = {
return GodotDisplayScreen.exitFullscreen();
},
- godot_js_display_desired_size_set__sig: 'v',
+ godot_js_display_desired_size_set__sig: 'vii',
godot_js_display_desired_size_set: function (width, height) {
GodotDisplayScreen.desired_size = [width, height];
GodotDisplayScreen.updateSize();
@@ -576,12 +691,16 @@ const GodotDisplay = {
godot_js_display_size_update__sig: 'i',
godot_js_display_size_update: function () {
- return GodotDisplayScreen.updateSize();
+ const updated = GodotDisplayScreen.updateSize();
+ if (updated) {
+ GodotDisplayVK.updateSize();
+ }
+ return updated;
},
godot_js_display_screen_size_get__sig: 'vii',
godot_js_display_screen_size_get: function (width, height) {
- const scale = window.devicePixelRatio || 1;
+ const scale = GodotDisplayScreen.getPixelRatio();
GodotRuntime.setHeapValue(width, window.screen.width * scale, 'i32');
GodotRuntime.setHeapValue(height, window.screen.height * scale, 'i32');
},
@@ -600,6 +719,17 @@ const GodotDisplay = {
GodotRuntime.setHeapValue(r_y, (y - rect.y) * rh, 'i32');
},
+ godot_js_display_has_webgl__sig: 'ii',
+ godot_js_display_has_webgl: function (p_version) {
+ if (p_version !== 1 && p_version !== 2) {
+ return false;
+ }
+ try {
+ return !!document.createElement('canvas').getContext(p_version === 2 ? 'webgl2' : 'webgl');
+ } catch (e) { /* Not available */ }
+ return false;
+ },
+
/*
* Canvas
*/
@@ -671,7 +801,7 @@ const GodotDisplay = {
document.head.appendChild(link);
}
const old_icon = GodotDisplay.window_icon;
- const png = new Blob([GodotRuntime.heapCopy(HEAPU8, p_ptr, p_len)], { type: 'image/png' });
+ const png = new Blob([GodotRuntime.heapSlice(HEAPU8, p_ptr, p_len)], { type: 'image/png' });
GodotDisplay.window_icon = URL.createObjectURL(png);
link.href = GodotDisplay.window_icon;
if (old_icon) {
@@ -711,7 +841,7 @@ const GodotDisplay = {
const shape = GodotRuntime.parseString(p_shape);
const old_shape = GodotDisplayCursor.cursors[shape];
if (p_len > 0) {
- const png = new Blob([GodotRuntime.heapCopy(HEAPU8, p_ptr, p_len)], { type: 'image/png' });
+ const png = new Blob([GodotRuntime.heapSlice(HEAPU8, p_ptr, p_len)], { type: 'image/png' });
const url = URL.createObjectURL(png);
GodotDisplayCursor.cursors[shape] = {
url: url,
@@ -739,7 +869,7 @@ const GodotDisplay = {
const notif = [p_enter, p_exit, p_in, p_out];
['mouseover', 'mouseleave', 'focus', 'blur'].forEach(function (evt_name, idx) {
GodotDisplayListeners.add(canvas, evt_name, function () {
- func.bind(null, notif[idx]);
+ func(notif[idx]);
}, true);
});
},
@@ -776,8 +906,8 @@ const GodotDisplay = {
GodotDisplayListeners.add(canvas, 'drop', GodotDisplayDragDrop.handler(dropFiles));
},
- godot_js_display_setup_canvas__sig: 'viii',
- godot_js_display_setup_canvas: function (p_width, p_height, p_fullscreen) {
+ godot_js_display_setup_canvas__sig: 'viiii',
+ godot_js_display_setup_canvas: function (p_width, p_height, p_fullscreen, p_hidpi) {
const canvas = GodotConfig.canvas;
GodotDisplayListeners.add(canvas, 'contextmenu', function (ev) {
ev.preventDefault();
@@ -786,6 +916,7 @@ const GodotDisplay = {
alert('WebGL context lost, please reload the page'); // eslint-disable-line no-alert
ev.preventDefault();
}, false);
+ GodotDisplayScreen.hidpi = !!p_hidpi;
switch (GodotConfig.canvas_resize_policy) {
case 0: // None
GodotDisplayScreen.desired_size = [canvas.width, canvas.height];
@@ -800,12 +931,42 @@ const GodotDisplay = {
canvas.style.left = 0;
break;
}
+ GodotDisplayScreen.updateSize();
if (p_fullscreen) {
GodotDisplayScreen.requestFullscreen();
}
},
/*
+ * Virtual Keyboard
+ */
+ godot_js_display_vk_show__sig: 'viiii',
+ godot_js_display_vk_show: function (p_text, p_multiline, p_start, p_end) {
+ const text = GodotRuntime.parseString(p_text);
+ const start = p_start > 0 ? p_start : 0;
+ const end = p_end > 0 ? p_end : start;
+ GodotDisplayVK.show(text, p_multiline, start, end);
+ },
+
+ godot_js_display_vk_hide__sig: 'v',
+ godot_js_display_vk_hide: function () {
+ GodotDisplayVK.hide();
+ },
+
+ godot_js_display_vk_available__sig: 'i',
+ godot_js_display_vk_available: function () {
+ return GodotDisplayVK.available();
+ },
+
+ godot_js_display_vk_cb__sig: 'vi',
+ godot_js_display_vk_cb: function (p_input_cb) {
+ const input_cb = GodotRuntime.get_func(p_input_cb);
+ if (GodotDisplayVK.available()) {
+ GodotDisplayVK.init(input_cb);
+ }
+ },
+
+ /*
* Gamepads
*/
godot_js_display_gamepad_cb__sig: 'vi',
diff --git a/platform/javascript/js/libs/library_godot_editor_tools.js b/platform/javascript/js/libs/library_godot_editor_tools.js
deleted file mode 100644
index d7f1ad5ea1..0000000000
--- a/platform/javascript/js/libs/library_godot_editor_tools.js
+++ /dev/null
@@ -1,57 +0,0 @@
-/*************************************************************************/
-/* library_godot_editor_tools.js */
-/*************************************************************************/
-/* This file is part of: */
-/* GODOT ENGINE */
-/* https://godotengine.org */
-/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 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. */
-/*************************************************************************/
-
-const GodotEditorTools = {
- godot_js_editor_download_file__deps: ['$FS'],
- godot_js_editor_download_file__sig: 'viii',
- godot_js_editor_download_file: function (p_path, p_name, p_mime) {
- const path = GodotRuntime.parseString(p_path);
- const name = GodotRuntime.parseString(p_name);
- const mime = GodotRuntime.parseString(p_mime);
- const size = FS.stat(path)['size'];
- const buf = new Uint8Array(size);
- const fd = FS.open(path, 'r');
- FS.read(fd, buf, 0, size);
- FS.close(fd);
- FS.unlink(path);
- const blob = new Blob([buf], { type: mime });
- const url = window.URL.createObjectURL(blob);
- const a = document.createElement('a');
- a.href = url;
- a.download = name;
- a.style.display = 'none';
- document.body.appendChild(a);
- a.click();
- a.remove();
- window.URL.revokeObjectURL(url);
- },
-};
-
-mergeInto(LibraryManager.library, GodotEditorTools);
diff --git a/platform/javascript/js/libs/library_godot_eval.js b/platform/javascript/js/libs/library_godot_eval.js
deleted file mode 100644
index 9ab392b813..0000000000
--- a/platform/javascript/js/libs/library_godot_eval.js
+++ /dev/null
@@ -1,86 +0,0 @@
-/*************************************************************************/
-/* library_godot_eval.js */
-/*************************************************************************/
-/* This file is part of: */
-/* GODOT ENGINE */
-/* https://godotengine.org */
-/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 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. */
-/*************************************************************************/
-
-const GodotEval = {
- godot_js_eval__deps: ['$GodotRuntime'],
- godot_js_eval__sig: 'iiiiiii',
- godot_js_eval: function (p_js, p_use_global_ctx, p_union_ptr, p_byte_arr, p_byte_arr_write, p_callback) {
- const js_code = GodotRuntime.parseString(p_js);
- let eval_ret = null;
- try {
- if (p_use_global_ctx) {
- // indirect eval call grants global execution context
- const global_eval = eval; // eslint-disable-line no-eval
- eval_ret = global_eval(js_code);
- } else {
- eval_ret = eval(js_code); // eslint-disable-line no-eval
- }
- } catch (e) {
- GodotRuntime.error(e);
- }
-
- switch (typeof eval_ret) {
- case 'boolean':
- GodotRuntime.setHeapValue(p_union_ptr, eval_ret, 'i32');
- return 1; // BOOL
-
- case 'number':
- GodotRuntime.setHeapValue(p_union_ptr, eval_ret, 'double');
- return 3; // REAL
-
- case 'string':
- GodotRuntime.setHeapValue(p_union_ptr, GodotRuntime.allocString(eval_ret), '*');
- return 4; // STRING
-
- case 'object':
- if (eval_ret === null) {
- break;
- }
-
- if (ArrayBuffer.isView(eval_ret) && !(eval_ret instanceof Uint8Array)) {
- eval_ret = new Uint8Array(eval_ret.buffer);
- } else if (eval_ret instanceof ArrayBuffer) {
- eval_ret = new Uint8Array(eval_ret);
- }
- if (eval_ret instanceof Uint8Array) {
- const func = GodotRuntime.get_func(p_callback);
- const bytes_ptr = func(p_byte_arr, p_byte_arr_write, eval_ret.length);
- HEAPU8.set(eval_ret, bytes_ptr);
- return 20; // POOL_BYTE_ARRAY
- }
- break;
-
- // no default
- }
- return 0; // NIL
- },
-};
-
-mergeInto(LibraryManager.library, GodotEval);
diff --git a/platform/javascript/js/libs/library_godot_fetch.js b/platform/javascript/js/libs/library_godot_fetch.js
new file mode 100644
index 0000000000..615f9de8b0
--- /dev/null
+++ b/platform/javascript/js/libs/library_godot_fetch.js
@@ -0,0 +1,247 @@
+/*************************************************************************/
+/* library_godot_fetch.js */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 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. */
+/*************************************************************************/
+
+const GodotFetch = {
+ $GodotFetch__deps: ['$IDHandler', '$GodotRuntime'],
+ $GodotFetch: {
+
+ onread: function (id, result) {
+ const obj = IDHandler.get(id);
+ if (!obj) {
+ return;
+ }
+ if (result.value) {
+ obj.chunks.push(result.value);
+ }
+ obj.reading = false;
+ obj.done = result.done;
+ },
+
+ onresponse: function (id, response) {
+ const obj = IDHandler.get(id);
+ if (!obj) {
+ return;
+ }
+ let chunked = false;
+ response.headers.forEach(function (value, header) {
+ const v = value.toLowerCase().trim();
+ const h = header.toLowerCase().trim();
+ if (h === 'transfer-encoding' && v === 'chunked') {
+ chunked = true;
+ }
+ });
+ obj.status = response.status;
+ obj.response = response;
+ obj.reader = response.body.getReader();
+ obj.chunked = chunked;
+ },
+
+ onerror: function (id, err) {
+ GodotRuntime.error(err);
+ const obj = IDHandler.get(id);
+ if (!obj) {
+ return;
+ }
+ obj.error = err;
+ },
+
+ create: function (method, url, headers, body) {
+ const obj = {
+ request: null,
+ response: null,
+ reader: null,
+ error: null,
+ done: false,
+ reading: false,
+ status: 0,
+ chunks: [],
+ bodySize: -1,
+ };
+ const id = IDHandler.add(obj);
+ const init = {
+ method: method,
+ headers: headers,
+ body: body,
+ };
+ obj.request = fetch(url, init);
+ obj.request.then(GodotFetch.onresponse.bind(null, id)).catch(GodotFetch.onerror.bind(null, id));
+ return id;
+ },
+
+ free: function (id) {
+ const obj = IDHandler.get(id);
+ if (!obj) {
+ return;
+ }
+ IDHandler.remove(id);
+ if (!obj.request) {
+ return;
+ }
+ // Try to abort
+ obj.request.then(function (response) {
+ response.abort();
+ }).catch(function (e) { /* nothing to do */ });
+ },
+
+ read: function (id) {
+ const obj = IDHandler.get(id);
+ if (!obj) {
+ return;
+ }
+ if (obj.reader && !obj.reading) {
+ if (obj.done) {
+ obj.reader = null;
+ return;
+ }
+ obj.reading = true;
+ obj.reader.read().then(GodotFetch.onread.bind(null, id)).catch(GodotFetch.onerror.bind(null, id));
+ }
+ },
+ },
+
+ godot_js_fetch_create__sig: 'iiiiiii',
+ godot_js_fetch_create: function (p_method, p_url, p_headers, p_headers_size, p_body, p_body_size) {
+ const method = GodotRuntime.parseString(p_method);
+ const url = GodotRuntime.parseString(p_url);
+ const headers = GodotRuntime.parseStringArray(p_headers, p_headers_size);
+ const body = p_body_size ? GodotRuntime.heapSlice(HEAP8, p_body, p_body_size) : null;
+ return GodotFetch.create(method, url, headers.map(function (hv) {
+ const idx = hv.indexOf(':');
+ if (idx <= 0) {
+ return [];
+ }
+ return [
+ hv.slice(0, idx).trim(),
+ hv.slice(idx + 1).trim(),
+ ];
+ }).filter(function (v) {
+ return v.length === 2;
+ }), body);
+ },
+
+ godot_js_fetch_state_get__sig: 'ii',
+ godot_js_fetch_state_get: function (p_id) {
+ const obj = IDHandler.get(p_id);
+ if (!obj) {
+ return -1;
+ }
+ if (obj.error) {
+ return -1;
+ }
+ if (!obj.response) {
+ return 0;
+ }
+ if (obj.reader) {
+ return 1;
+ }
+ if (obj.done) {
+ return 2;
+ }
+ return -1;
+ },
+
+ godot_js_fetch_http_status_get__sig: 'ii',
+ godot_js_fetch_http_status_get: function (p_id) {
+ const obj = IDHandler.get(p_id);
+ if (!obj || !obj.response) {
+ return 0;
+ }
+ return obj.status;
+ },
+
+ godot_js_fetch_read_headers__sig: 'iiii',
+ godot_js_fetch_read_headers: function (p_id, p_parse_cb, p_ref) {
+ const obj = IDHandler.get(p_id);
+ if (!obj || !obj.response) {
+ return 1;
+ }
+ const cb = GodotRuntime.get_func(p_parse_cb);
+ const arr = [];
+ obj.response.headers.forEach(function (v, h) {
+ arr.push(`${h}:${v}`);
+ });
+ const c_ptr = GodotRuntime.allocStringArray(arr);
+ cb(arr.length, c_ptr, p_ref);
+ GodotRuntime.freeStringArray(c_ptr, arr.length);
+ return 0;
+ },
+
+ godot_js_fetch_read_chunk__sig: 'iiii',
+ godot_js_fetch_read_chunk: function (p_id, p_buf, p_buf_size) {
+ const obj = IDHandler.get(p_id);
+ if (!obj || !obj.response) {
+ return 0;
+ }
+ let to_read = p_buf_size;
+ const chunks = obj.chunks;
+ while (to_read && chunks.length) {
+ const chunk = obj.chunks[0];
+ if (chunk.length > to_read) {
+ GodotRuntime.heapCopy(HEAP8, chunk.slice(0, to_read), p_buf);
+ chunks[0] = chunk.slice(to_read);
+ to_read = 0;
+ } else {
+ GodotRuntime.heapCopy(HEAP8, chunk, p_buf);
+ to_read -= chunk.length;
+ chunks.pop();
+ }
+ }
+ if (!chunks.length) {
+ GodotFetch.read(p_id);
+ }
+ return p_buf_size - to_read;
+ },
+
+ godot_js_fetch_body_length_get__sig: 'ii',
+ godot_js_fetch_body_length_get: function (p_id) {
+ const obj = IDHandler.get(p_id);
+ if (!obj || !obj.response) {
+ return -1;
+ }
+ return obj.bodySize;
+ },
+
+ godot_js_fetch_is_chunked__sig: 'ii',
+ godot_js_fetch_is_chunked: function (p_id) {
+ const obj = IDHandler.get(p_id);
+ if (!obj || !obj.response) {
+ return -1;
+ }
+ return obj.chunked ? 1 : 0;
+ },
+
+ godot_js_fetch_free__sig: 'vi',
+ godot_js_fetch_free: function (id) {
+ GodotFetch.free(id);
+ },
+};
+
+autoAddDeps(GodotFetch, '$GodotFetch');
+mergeInto(LibraryManager.library, GodotFetch);
diff --git a/platform/javascript/js/libs/library_godot_http_request.js b/platform/javascript/js/libs/library_godot_http_request.js
deleted file mode 100644
index 7dc2a2df29..0000000000
--- a/platform/javascript/js/libs/library_godot_http_request.js
+++ /dev/null
@@ -1,142 +0,0 @@
-/*************************************************************************/
-/* http_request.js */
-/*************************************************************************/
-/* This file is part of: */
-/* GODOT ENGINE */
-/* https://godotengine.org */
-/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 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. */
-/*************************************************************************/
-
-const GodotHTTPRequest = {
- $GodotHTTPRequest__deps: ['$GodotRuntime'],
- $GodotHTTPRequest: {
- requests: [],
-
- getUnusedRequestId: function () {
- const idMax = GodotHTTPRequest.requests.length;
- for (let potentialId = 0; potentialId < idMax; ++potentialId) {
- if (GodotHTTPRequest.requests[potentialId] instanceof XMLHttpRequest) {
- continue;
- }
- return potentialId;
- }
- GodotHTTPRequest.requests.push(null);
- return idMax;
- },
-
- setupRequest: function (xhr) {
- xhr.responseType = 'arraybuffer';
- },
- },
-
- godot_xhr_new__sig: 'i',
- godot_xhr_new: function () {
- const newId = GodotHTTPRequest.getUnusedRequestId();
- GodotHTTPRequest.requests[newId] = new XMLHttpRequest();
- GodotHTTPRequest.setupRequest(GodotHTTPRequest.requests[newId]);
- return newId;
- },
-
- godot_xhr_reset__sig: 'vi',
- godot_xhr_reset: function (xhrId) {
- GodotHTTPRequest.requests[xhrId] = new XMLHttpRequest();
- GodotHTTPRequest.setupRequest(GodotHTTPRequest.requests[xhrId]);
- },
-
- godot_xhr_free__sig: 'vi',
- godot_xhr_free: function (xhrId) {
- GodotHTTPRequest.requests[xhrId].abort();
- GodotHTTPRequest.requests[xhrId] = null;
- },
-
- godot_xhr_open__sig: 'viiiii',
- godot_xhr_open: function (xhrId, method, url, p_user, p_password) {
- const user = p_user > 0 ? GodotRuntime.parseString(p_user) : null;
- const password = p_password > 0 ? GodotRuntime.parseString(p_password) : null;
- GodotHTTPRequest.requests[xhrId].open(GodotRuntime.parseString(method), GodotRuntime.parseString(url), true, user, password);
- },
-
- godot_xhr_set_request_header__sig: 'viii',
- godot_xhr_set_request_header: function (xhrId, header, value) {
- GodotHTTPRequest.requests[xhrId].setRequestHeader(GodotRuntime.parseString(header), GodotRuntime.parseString(value));
- },
-
- godot_xhr_send__sig: 'viii',
- godot_xhr_send: function (xhrId, p_ptr, p_len) {
- let data = null;
- if (p_ptr && p_len) {
- data = GodotRuntime.heapCopy(HEAP8, p_ptr, p_len);
- }
- GodotHTTPRequest.requests[xhrId].send(data);
- },
-
- godot_xhr_abort__sig: 'vi',
- godot_xhr_abort: function (xhrId) {
- GodotHTTPRequest.requests[xhrId].abort();
- },
-
- godot_xhr_get_status__sig: 'ii',
- godot_xhr_get_status: function (xhrId) {
- return GodotHTTPRequest.requests[xhrId].status;
- },
-
- godot_xhr_get_ready_state__sig: 'ii',
- godot_xhr_get_ready_state: function (xhrId) {
- return GodotHTTPRequest.requests[xhrId].readyState;
- },
-
- godot_xhr_get_response_headers_length__sig: 'ii',
- godot_xhr_get_response_headers_length: function (xhrId) {
- const headers = GodotHTTPRequest.requests[xhrId].getAllResponseHeaders();
- return headers === null ? 0 : GodotRuntime.strlen(headers);
- },
-
- godot_xhr_get_response_headers__sig: 'viii',
- godot_xhr_get_response_headers: function (xhrId, dst, len) {
- const str = GodotHTTPRequest.requests[xhrId].getAllResponseHeaders();
- if (str === null) {
- return;
- }
- GodotRuntime.stringToHeap(str, dst, len);
- },
-
- godot_xhr_get_response_length__sig: 'ii',
- godot_xhr_get_response_length: function (xhrId) {
- const body = GodotHTTPRequest.requests[xhrId].response;
- return body === null ? 0 : body.byteLength;
- },
-
- godot_xhr_get_response__sig: 'viii',
- godot_xhr_get_response: function (xhrId, dst, len) {
- let buf = GodotHTTPRequest.requests[xhrId].response;
- if (buf === null) {
- return;
- }
- buf = new Uint8Array(buf).subarray(0, len);
- HEAPU8.set(buf, dst);
- },
-};
-
-autoAddDeps(GodotHTTPRequest, '$GodotHTTPRequest');
-mergeInto(LibraryManager.library, GodotHTTPRequest);
diff --git a/platform/javascript/js/libs/library_godot_javascript_singleton.js b/platform/javascript/js/libs/library_godot_javascript_singleton.js
new file mode 100644
index 0000000000..22ce003cd2
--- /dev/null
+++ b/platform/javascript/js/libs/library_godot_javascript_singleton.js
@@ -0,0 +1,346 @@
+/*************************************************************************/
+/* library_godot_eval.js */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 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. */
+/*************************************************************************/
+
+const GodotJSWrapper = {
+
+ $GodotJSWrapper__deps: ['$GodotRuntime', '$IDHandler'],
+ $GodotJSWrapper__postset: 'GodotJSWrapper.proxies = new Map();',
+ $GodotJSWrapper: {
+ proxies: null,
+ cb_ret: null,
+
+ MyProxy: function (val) {
+ const id = IDHandler.add(this);
+ GodotJSWrapper.proxies.set(val, id);
+ let refs = 1;
+ this.ref = function () {
+ refs++;
+ };
+ this.unref = function () {
+ refs--;
+ if (refs === 0) {
+ IDHandler.remove(id);
+ GodotJSWrapper.proxies.delete(val);
+ }
+ };
+ this.get_val = function () {
+ return val;
+ };
+ this.get_id = function () {
+ return id;
+ };
+ },
+
+ get_proxied: function (val) {
+ const id = GodotJSWrapper.proxies.get(val);
+ if (id === undefined) {
+ const proxy = new GodotJSWrapper.MyProxy(val);
+ return proxy.get_id();
+ }
+ IDHandler.get(id).ref();
+ return id;
+ },
+
+ get_proxied_value: function (id) {
+ const proxy = IDHandler.get(id);
+ if (proxy === undefined) {
+ return undefined;
+ }
+ return proxy.get_val();
+ },
+
+ variant2js: function (type, val) {
+ switch (type) {
+ case 0:
+ return null;
+ case 1:
+ return !!GodotRuntime.getHeapValue(val, 'i64');
+ case 2:
+ return GodotRuntime.getHeapValue(val, 'i64');
+ case 3:
+ return GodotRuntime.getHeapValue(val, 'double');
+ case 4:
+ return GodotRuntime.parseString(GodotRuntime.getHeapValue(val, '*'));
+ case 21: // OBJECT
+ return GodotJSWrapper.get_proxied_value(GodotRuntime.getHeapValue(val, 'i64'));
+ default:
+ return undefined;
+ }
+ },
+
+ js2variant: function (p_val, p_exchange) {
+ if (p_val === undefined || p_val === null) {
+ return 0; // NIL
+ }
+ const type = typeof (p_val);
+ if (type === 'boolean') {
+ GodotRuntime.setHeapValue(p_exchange, p_val, 'i64');
+ return 1; // BOOL
+ } else if (type === 'number') {
+ if (Number.isInteger(p_val)) {
+ GodotRuntime.setHeapValue(p_exchange, p_val, 'i64');
+ return 2; // INT
+ }
+ GodotRuntime.setHeapValue(p_exchange, p_val, 'double');
+ return 3; // REAL
+ } else if (type === 'string') {
+ const c_str = GodotRuntime.allocString(p_val);
+ GodotRuntime.setHeapValue(p_exchange, c_str, '*');
+ return 4; // STRING
+ }
+ const id = GodotJSWrapper.get_proxied(p_val);
+ GodotRuntime.setHeapValue(p_exchange, id, 'i64');
+ return 21;
+ },
+ },
+
+ godot_js_wrapper_interface_get__sig: 'ii',
+ godot_js_wrapper_interface_get: function (p_name) {
+ const name = GodotRuntime.parseString(p_name);
+ if (typeof (window[name]) !== 'undefined') {
+ return GodotJSWrapper.get_proxied(window[name]);
+ }
+ return 0;
+ },
+
+ godot_js_wrapper_object_get__sig: 'iiii',
+ godot_js_wrapper_object_get: function (p_id, p_exchange, p_prop) {
+ const obj = GodotJSWrapper.get_proxied_value(p_id);
+ if (obj === undefined) {
+ return 0;
+ }
+ if (p_prop) {
+ const prop = GodotRuntime.parseString(p_prop);
+ try {
+ return GodotJSWrapper.js2variant(obj[prop], p_exchange);
+ } catch (e) {
+ GodotRuntime.error(`Error getting variable ${prop} on object`, obj);
+ return 0; // NIL
+ }
+ }
+ return GodotJSWrapper.js2variant(obj, p_exchange);
+ },
+
+ godot_js_wrapper_object_set__sig: 'viiii',
+ godot_js_wrapper_object_set: function (p_id, p_name, p_type, p_exchange) {
+ const obj = GodotJSWrapper.get_proxied_value(p_id);
+ if (obj === undefined) {
+ return;
+ }
+ const name = GodotRuntime.parseString(p_name);
+ try {
+ obj[name] = GodotJSWrapper.variant2js(p_type, p_exchange);
+ } catch (e) {
+ GodotRuntime.error(`Error setting variable ${name} on object`, obj);
+ }
+ },
+
+ godot_js_wrapper_object_call__sig: 'iiiiiiiii',
+ godot_js_wrapper_object_call: function (p_id, p_method, p_args, p_argc, p_convert_callback, p_exchange, p_lock, p_free_lock_callback) {
+ const obj = GodotJSWrapper.get_proxied_value(p_id);
+ if (obj === undefined) {
+ return -1;
+ }
+ const method = GodotRuntime.parseString(p_method);
+ const convert = GodotRuntime.get_func(p_convert_callback);
+ const freeLock = GodotRuntime.get_func(p_free_lock_callback);
+ const args = new Array(p_argc);
+ for (let i = 0; i < p_argc; i++) {
+ const type = convert(p_args, i, p_exchange, p_lock);
+ const lock = GodotRuntime.getHeapValue(p_lock, '*');
+ args[i] = GodotJSWrapper.variant2js(type, p_exchange);
+ if (lock) {
+ freeLock(p_lock, type);
+ }
+ }
+ try {
+ const res = obj[method](...args);
+ return GodotJSWrapper.js2variant(res, p_exchange);
+ } catch (e) {
+ GodotRuntime.error(`Error calling method ${method} on:`, obj, 'error:', e);
+ return -1;
+ }
+ },
+
+ godot_js_wrapper_object_unref__sig: 'vi',
+ godot_js_wrapper_object_unref: function (p_id) {
+ const proxy = IDHandler.get(p_id);
+ if (proxy !== undefined) {
+ proxy.unref();
+ }
+ },
+
+ godot_js_wrapper_create_cb__sig: 'iii',
+ godot_js_wrapper_create_cb: function (p_ref, p_func) {
+ const func = GodotRuntime.get_func(p_func);
+ let id = 0;
+ const cb = function () {
+ if (!GodotJSWrapper.get_proxied_value(id)) {
+ return undefined;
+ }
+ // The callback will store the returned value in this variable via
+ // "godot_js_wrapper_object_set_cb_ret" upon calling the user function.
+ // This is safe! JavaScript is single threaded (and using it in threads is not a good idea anyway).
+ GodotJSWrapper.cb_ret = null;
+ const args = Array.from(arguments);
+ func(p_ref, GodotJSWrapper.get_proxied(args), args.length);
+ const ret = GodotJSWrapper.cb_ret;
+ GodotJSWrapper.cb_ret = null;
+ return ret;
+ };
+ id = GodotJSWrapper.get_proxied(cb);
+ return id;
+ },
+
+ godot_js_wrapper_object_set_cb_ret__sig: 'vii',
+ godot_js_wrapper_object_set_cb_ret: function (p_val_type, p_val_ex) {
+ GodotJSWrapper.cb_ret = GodotJSWrapper.variant2js(p_val_type, p_val_ex);
+ },
+
+ godot_js_wrapper_object_getvar__sig: 'iiii',
+ godot_js_wrapper_object_getvar: function (p_id, p_type, p_exchange) {
+ const obj = GodotJSWrapper.get_proxied_value(p_id);
+ if (obj === undefined) {
+ return -1;
+ }
+ const prop = GodotJSWrapper.variant2js(p_type, p_exchange);
+ if (prop === undefined || prop === null) {
+ return -1;
+ }
+ try {
+ return GodotJSWrapper.js2variant(obj[prop], p_exchange);
+ } catch (e) {
+ GodotRuntime.error(`Error getting variable ${prop} on object`, obj, e);
+ return -1;
+ }
+ },
+
+ godot_js_wrapper_object_setvar__sig: 'iiiiii',
+ godot_js_wrapper_object_setvar: function (p_id, p_key_type, p_key_ex, p_val_type, p_val_ex) {
+ const obj = GodotJSWrapper.get_proxied_value(p_id);
+ if (obj === undefined) {
+ return -1;
+ }
+ const key = GodotJSWrapper.variant2js(p_key_type, p_key_ex);
+ try {
+ obj[key] = GodotJSWrapper.variant2js(p_val_type, p_val_ex);
+ return 0;
+ } catch (e) {
+ GodotRuntime.error(`Error setting variable ${key} on object`, obj);
+ return -1;
+ }
+ },
+
+ godot_js_wrapper_create_object__sig: 'iiiiiiii',
+ godot_js_wrapper_create_object: function (p_object, p_args, p_argc, p_convert_callback, p_exchange, p_lock, p_free_lock_callback) {
+ const name = GodotRuntime.parseString(p_object);
+ if (typeof (window[name]) === 'undefined') {
+ return -1;
+ }
+ const convert = GodotRuntime.get_func(p_convert_callback);
+ const freeLock = GodotRuntime.get_func(p_free_lock_callback);
+ const args = new Array(p_argc);
+ for (let i = 0; i < p_argc; i++) {
+ const type = convert(p_args, i, p_exchange, p_lock);
+ const lock = GodotRuntime.getHeapValue(p_lock, '*');
+ args[i] = GodotJSWrapper.variant2js(type, p_exchange);
+ if (lock) {
+ freeLock(p_lock, type);
+ }
+ }
+ try {
+ const res = new window[name](...args);
+ return GodotJSWrapper.js2variant(res, p_exchange);
+ } catch (e) {
+ GodotRuntime.error(`Error calling constructor ${name} with args:`, args, 'error:', e);
+ return -1;
+ }
+ },
+};
+
+autoAddDeps(GodotJSWrapper, '$GodotJSWrapper');
+mergeInto(LibraryManager.library, GodotJSWrapper);
+
+const GodotEval = {
+ godot_js_eval__deps: ['$GodotRuntime'],
+ godot_js_eval__sig: 'iiiiiii',
+ godot_js_eval: function (p_js, p_use_global_ctx, p_union_ptr, p_byte_arr, p_byte_arr_write, p_callback) {
+ const js_code = GodotRuntime.parseString(p_js);
+ let eval_ret = null;
+ try {
+ if (p_use_global_ctx) {
+ // indirect eval call grants global execution context
+ const global_eval = eval; // eslint-disable-line no-eval
+ eval_ret = global_eval(js_code);
+ } else {
+ eval_ret = eval(js_code); // eslint-disable-line no-eval
+ }
+ } catch (e) {
+ GodotRuntime.error(e);
+ }
+
+ switch (typeof eval_ret) {
+ case 'boolean':
+ GodotRuntime.setHeapValue(p_union_ptr, eval_ret, 'i32');
+ return 1; // BOOL
+
+ case 'number':
+ GodotRuntime.setHeapValue(p_union_ptr, eval_ret, 'double');
+ return 3; // REAL
+
+ case 'string':
+ GodotRuntime.setHeapValue(p_union_ptr, GodotRuntime.allocString(eval_ret), '*');
+ return 4; // STRING
+
+ case 'object':
+ if (eval_ret === null) {
+ break;
+ }
+
+ if (ArrayBuffer.isView(eval_ret) && !(eval_ret instanceof Uint8Array)) {
+ eval_ret = new Uint8Array(eval_ret.buffer);
+ } else if (eval_ret instanceof ArrayBuffer) {
+ eval_ret = new Uint8Array(eval_ret);
+ }
+ if (eval_ret instanceof Uint8Array) {
+ const func = GodotRuntime.get_func(p_callback);
+ const bytes_ptr = func(p_byte_arr, p_byte_arr_write, eval_ret.length);
+ HEAPU8.set(eval_ret, bytes_ptr);
+ return 20; // POOL_BYTE_ARRAY
+ }
+ break;
+
+ // no default
+ }
+ return 0; // NIL
+ },
+};
+
+mergeInto(LibraryManager.library, GodotEval);
diff --git a/platform/javascript/js/libs/library_godot_os.js b/platform/javascript/js/libs/library_godot_os.js
index 0f189b013c..5aa750757c 100644
--- a/platform/javascript/js/libs/library_godot_os.js
+++ b/platform/javascript/js/libs/library_godot_os.js
@@ -59,6 +59,8 @@ const GodotConfig = {
canvas: null,
locale: 'en',
canvas_resize_policy: 2, // Adaptive
+ virtual_keyboard: false,
+ persistent_drops: false,
on_execute: null,
on_exit: null,
@@ -66,8 +68,13 @@ const GodotConfig = {
GodotConfig.canvas_resize_policy = p_opts['canvasResizePolicy'];
GodotConfig.canvas = p_opts['canvas'];
GodotConfig.locale = p_opts['locale'] || GodotConfig.locale;
+ GodotConfig.virtual_keyboard = p_opts['virtualKeyboard'];
+ GodotConfig.persistent_drops = !!p_opts['persistentDrops'];
GodotConfig.on_execute = p_opts['onExecute'];
GodotConfig.on_exit = p_opts['onExit'];
+ if (p_opts['focusCanvas']) {
+ GodotConfig.canvas.focus();
+ }
},
locate_file: function (file) {
@@ -77,6 +84,8 @@ const GodotConfig = {
GodotConfig.canvas = null;
GodotConfig.locale = 'en';
GodotConfig.canvas_resize_policy = 2;
+ GodotConfig.virtual_keyboard = false;
+ GodotConfig.persistent_drops = false;
GodotConfig.on_execute = null;
GodotConfig.on_exit = null;
},
@@ -298,6 +307,23 @@ const GodotOS = {
godot_js_os_hw_concurrency_get: function () {
return navigator.hardwareConcurrency || 1;
},
+
+ godot_js_os_download_buffer__sig: 'viiii',
+ godot_js_os_download_buffer: function (p_ptr, p_size, p_name, p_mime) {
+ const buf = GodotRuntime.heapSlice(HEAP8, p_ptr, p_size);
+ const name = GodotRuntime.parseString(p_name);
+ const mime = GodotRuntime.parseString(p_mime);
+ const blob = new Blob([buf], { type: mime });
+ const url = window.URL.createObjectURL(blob);
+ const a = document.createElement('a');
+ a.href = url;
+ a.download = name;
+ a.style.display = 'none';
+ document.body.appendChild(a);
+ a.click();
+ a.remove();
+ window.URL.revokeObjectURL(url);
+ },
};
autoAddDeps(GodotOS, '$GodotOS');
diff --git a/platform/javascript/js/libs/library_godot_runtime.js b/platform/javascript/js/libs/library_godot_runtime.js
index 7e36ff8ab5..3da1ed8f06 100644
--- a/platform/javascript/js/libs/library_godot_runtime.js
+++ b/platform/javascript/js/libs/library_godot_runtime.js
@@ -72,11 +72,16 @@ const GodotRuntime = {
return p_heap.subarray(p_ptr / bytes, p_ptr / bytes + p_len);
},
- heapCopy: function (p_heap, p_ptr, p_len) {
+ heapSlice: function (p_heap, p_ptr, p_len) {
const bytes = p_heap.BYTES_PER_ELEMENT;
return p_heap.slice(p_ptr / bytes, p_ptr / bytes + p_len);
},
+ heapCopy: function (p_dst, p_src, p_ptr) {
+ const bytes = p_src.BYTES_PER_ELEMENT;
+ return p_dst.set(p_src, p_ptr / bytes);
+ },
+
/*
* Strings
*/
@@ -84,6 +89,15 @@ const GodotRuntime = {
return UTF8ToString(p_ptr); // eslint-disable-line no-undef
},
+ parseStringArray: function (p_ptr, p_size) {
+ const strings = [];
+ const ptrs = GodotRuntime.heapSub(HEAP32, p_ptr, p_size); // TODO wasm64
+ ptrs.forEach(function (ptr) {
+ strings.push(GodotRuntime.parseString(ptr));
+ });
+ return strings;
+ },
+
strlen: function (p_str) {
return lengthBytesUTF8(p_str); // eslint-disable-line no-undef
},
diff --git a/platform/javascript/os_javascript.cpp b/platform/javascript/os_javascript.cpp
index 0b1650076c..260bfad7a5 100644
--- a/platform/javascript/os_javascript.cpp
+++ b/platform/javascript/os_javascript.cpp
@@ -31,13 +31,12 @@
#include "os_javascript.h"
#include "core/debugger/engine_debugger.h"
-#include "core/io/json.h"
#include "drivers/unix/dir_access_unix.h"
#include "drivers/unix/file_access_unix.h"
#include "main/main.h"
-#include "modules/modules_enabled.gen.h"
#include "platform/javascript/display_server_javascript.h"
+#include "modules/modules_enabled.gen.h"
#ifdef MODULE_WEBSOCKET_ENABLED
#include "modules/websocket/remote_debugger_peer_websocket.h"
#endif
@@ -115,7 +114,7 @@ Error OS_JavaScript::create_process(const String &p_path, const List<String> &p_
for (const List<String>::Element *E = p_arguments.front(); E; E = E->next()) {
args.push_back(E->get());
}
- String json_args = JSON::print(args);
+ String json_args = Variant(args).to_json_string();
int failed = godot_js_os_execute(json_args.utf8().get_data());
ERR_FAIL_COND_V_MSG(failed, ERR_UNAVAILABLE, "OS::execute() or create_process() must be implemented in JavaScript via 'engine.setOnExecute' if required.");
return OK;
diff --git a/platform/javascript/package-lock.json b/platform/javascript/package-lock.json
index b8c434b3dd..8003619576 100644
--- a/platform/javascript/package-lock.json
+++ b/platform/javascript/package-lock.json
@@ -5,27 +5,27 @@
"requires": true,
"dependencies": {
"@babel/code-frame": {
- "version": "7.10.4",
- "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.10.4.tgz",
- "integrity": "sha512-vG6SvB6oYEhvgisZNFRmRCUkLz11c7rp+tbNTynGqc6mS1d5ATd/sGyV6W0KZZnXRKMTzZDRgQT3Ou9jhpAfUg==",
+ "version": "7.12.11",
+ "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.11.tgz",
+ "integrity": "sha512-Zt1yodBx1UcyiePMSkWnU4hPqhwq7hGi2nFL1LeA3EUl+q2LQx16MISgJ0+z7dnmgvP9QtIleuETGOiOH1RcIw==",
"dev": true,
"requires": {
"@babel/highlight": "^7.10.4"
}
},
"@babel/helper-validator-identifier": {
- "version": "7.10.4",
- "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.4.tgz",
- "integrity": "sha512-3U9y+43hz7ZM+rzG24Qe2mufW5KhvFg/NhnNph+i9mgCtdTCtMJuI1TMkrIUiK7Ix4PYlRF9I5dhqaLYA/ADXw==",
+ "version": "7.14.5",
+ "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.14.5.tgz",
+ "integrity": "sha512-5lsetuxCLilmVGyiLEfoHBRX8UCFD+1m2x3Rj97WrW3V7H3u4RWRXA4evMjImCsin2J2YT0QaVDGf+z8ondbAg==",
"dev": true
},
"@babel/highlight": {
- "version": "7.10.4",
- "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.10.4.tgz",
- "integrity": "sha512-i6rgnR/YgPEQzZZnbTHHuZdlE8qyoBNalD6F+q4vAFlcMEcqmkoG+mPqJYJCo63qPf74+Y1UZsl3l6f7/RIkmA==",
+ "version": "7.14.5",
+ "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.14.5.tgz",
+ "integrity": "sha512-qf9u2WFWVV0MppaL877j2dBtQIDgmidgjGk5VIMw3OadXvYaXn66U1BFlH2t4+t3i+8PhedppRv+i40ABzd+gg==",
"dev": true,
"requires": {
- "@babel/helper-validator-identifier": "^7.10.4",
+ "@babel/helper-validator-identifier": "^7.14.5",
"chalk": "^2.0.0",
"js-tokens": "^4.0.0"
},
@@ -40,39 +40,38 @@
"escape-string-regexp": "^1.0.5",
"supports-color": "^5.3.0"
}
+ },
+ "escape-string-regexp": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
+ "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=",
+ "dev": true
}
}
},
"@babel/parser": {
- "version": "7.13.4",
- "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.13.4.tgz",
- "integrity": "sha512-uvoOulWHhI+0+1f9L4BoozY7U5cIkZ9PgJqvb041d6vypgUmtVPG4vmGm4pSggjl8BELzvHyUeJSUyEMY6b+qA==",
+ "version": "7.14.5",
+ "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.14.5.tgz",
+ "integrity": "sha512-TM8C+xtH/9n1qzX+JNHi7AN2zHMTiPUtspO0ZdHflW8KaskkALhMmuMHb4bCmNdv9VAPzJX3/bXqkVLnAvsPfg==",
"dev": true
},
"@eslint/eslintrc": {
- "version": "0.1.3",
- "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.1.3.tgz",
- "integrity": "sha512-4YVwPkANLeNtRjMekzux1ci8hIaH5eGKktGqR0d3LWsKNn5B2X/1Z6Trxy7jQXl9EBGE6Yj02O+t09FMeRllaA==",
+ "version": "0.4.2",
+ "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.4.2.tgz",
+ "integrity": "sha512-8nmGq/4ycLpIwzvhI4tNDmQztZ8sp+hI7cyG8i1nQDhkAbRzHpXPidRAHlNvCZQpJTKw5ItIpMw9RSToGF00mg==",
"dev": true,
"requires": {
"ajv": "^6.12.4",
"debug": "^4.1.1",
"espree": "^7.3.0",
- "globals": "^12.1.0",
+ "globals": "^13.9.0",
"ignore": "^4.0.6",
"import-fresh": "^3.2.1",
"js-yaml": "^3.13.1",
- "lodash": "^4.17.19",
"minimatch": "^3.0.4",
"strip-json-comments": "^3.1.1"
}
},
- "@types/color-name": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/@types/color-name/-/color-name-1.1.1.tgz",
- "integrity": "sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ==",
- "dev": true
- },
"@types/json5": {
"version": "0.0.29",
"resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz",
@@ -80,9 +79,9 @@
"dev": true
},
"acorn": {
- "version": "7.4.0",
- "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.0.tgz",
- "integrity": "sha512-+G7P8jJmCHr+S+cLfQxygbWhXy+8YTVGzAkpEbcLo2mLoL7tij/VG41QSHACSf5QgYRhMZYHuNc6drJaO0Da+w==",
+ "version": "7.4.1",
+ "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz",
+ "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==",
"dev": true
},
"acorn-jsx": {
@@ -92,9 +91,9 @@
"dev": true
},
"ajv": {
- "version": "6.12.5",
- "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.5.tgz",
- "integrity": "sha512-lRF8RORchjpKG50/WFf8xmg7sgCLFiYNNnqdKflk63whMQcWR5ngGjiSXkL9bjxy6B2npOK2HSMN49jEBMSkag==",
+ "version": "6.12.6",
+ "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
+ "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
"dev": true,
"requires": {
"fast-deep-equal": "^3.1.1",
@@ -134,78 +133,39 @@
}
},
"array-includes": {
- "version": "3.1.1",
- "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.1.tgz",
- "integrity": "sha512-c2VXaCHl7zPsvpkFsw4nxvFie4fh1ur9bpcgsVkIjqn0H/Xwdg+7fv3n2r/isyS8EBj5b06M9kHyZuIr4El6WQ==",
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.3.tgz",
+ "integrity": "sha512-gcem1KlBU7c9rB+Rq8/3PPKsK2kjqeEBa3bD5kkQo4nYlOHQCJqIJFqBXDEfwaRuYTT4E+FxA9xez7Gf/e3Q7A==",
"dev": true,
"requires": {
+ "call-bind": "^1.0.2",
"define-properties": "^1.1.3",
- "es-abstract": "^1.17.0",
+ "es-abstract": "^1.18.0-next.2",
+ "get-intrinsic": "^1.1.1",
"is-string": "^1.0.5"
- },
- "dependencies": {
- "es-abstract": {
- "version": "1.17.6",
- "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.6.tgz",
- "integrity": "sha512-Fr89bON3WFyUi5EvAeI48QTWX0AyekGgLA8H+c+7fbfCkJwRWRMLd8CQedNEyJuoYYhmtEqY92pgte1FAhBlhw==",
- "dev": true,
- "requires": {
- "es-to-primitive": "^1.2.1",
- "function-bind": "^1.1.1",
- "has": "^1.0.3",
- "has-symbols": "^1.0.1",
- "is-callable": "^1.2.0",
- "is-regex": "^1.1.0",
- "object-inspect": "^1.7.0",
- "object-keys": "^1.1.1",
- "object.assign": "^4.1.0",
- "string.prototype.trimend": "^1.0.1",
- "string.prototype.trimstart": "^1.0.1"
- }
- }
}
},
"array.prototype.flat": {
- "version": "1.2.3",
- "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.2.3.tgz",
- "integrity": "sha512-gBlRZV0VSmfPIeWfuuy56XZMvbVfbEUnOXUvt3F/eUUUSyzlgLxhEX4YAEpxNAogRGehPSnfXyPtYyKAhkzQhQ==",
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.2.4.tgz",
+ "integrity": "sha512-4470Xi3GAPAjZqFcljX2xzckv1qeKPizoNkiS0+O4IoPR2ZNpcjE0pkhdihlDouK+x6QOast26B4Q/O9DJnwSg==",
"dev": true,
"requires": {
+ "call-bind": "^1.0.0",
"define-properties": "^1.1.3",
- "es-abstract": "^1.17.0-next.1"
- },
- "dependencies": {
- "es-abstract": {
- "version": "1.17.6",
- "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.6.tgz",
- "integrity": "sha512-Fr89bON3WFyUi5EvAeI48QTWX0AyekGgLA8H+c+7fbfCkJwRWRMLd8CQedNEyJuoYYhmtEqY92pgte1FAhBlhw==",
- "dev": true,
- "requires": {
- "es-to-primitive": "^1.2.1",
- "function-bind": "^1.1.1",
- "has": "^1.0.3",
- "has-symbols": "^1.0.1",
- "is-callable": "^1.2.0",
- "is-regex": "^1.1.0",
- "object-inspect": "^1.7.0",
- "object-keys": "^1.1.1",
- "object.assign": "^4.1.0",
- "string.prototype.trimend": "^1.0.1",
- "string.prototype.trimstart": "^1.0.1"
- }
- }
+ "es-abstract": "^1.18.0-next.1"
}
},
"astral-regex": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-1.0.0.tgz",
- "integrity": "sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg==",
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz",
+ "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==",
"dev": true
},
"balanced-match": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz",
- "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=",
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
+ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
"dev": true
},
"bluebird": {
@@ -224,6 +184,16 @@
"concat-map": "0.0.1"
}
},
+ "call-bind": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz",
+ "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==",
+ "dev": true,
+ "requires": {
+ "function-bind": "^1.1.1",
+ "get-intrinsic": "^1.0.2"
+ }
+ },
"callsites": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
@@ -231,18 +201,18 @@
"dev": true
},
"catharsis": {
- "version": "0.8.11",
- "resolved": "https://registry.npmjs.org/catharsis/-/catharsis-0.8.11.tgz",
- "integrity": "sha512-a+xUyMV7hD1BrDQA/3iPV7oc+6W26BgVJO05PGEoatMyIuPScQKsde6i3YorWX1qs+AZjnJ18NqdKoCtKiNh1g==",
+ "version": "0.9.0",
+ "resolved": "https://registry.npmjs.org/catharsis/-/catharsis-0.9.0.tgz",
+ "integrity": "sha512-prMTQVpcns/tzFgFVkVp6ak6RykZyWb3gu8ckUpd6YkTlacOd3DXGJjIpD4Q6zJirizvaiAjSSHlOsA+6sNh2A==",
"dev": true,
"requires": {
- "lodash": "^4.17.14"
+ "lodash": "^4.17.15"
}
},
"chalk": {
- "version": "4.1.0",
- "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz",
- "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==",
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz",
+ "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==",
"dev": true,
"requires": {
"ansi-styles": "^4.1.0",
@@ -250,12 +220,11 @@
},
"dependencies": {
"ansi-styles": {
- "version": "4.2.1",
- "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz",
- "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==",
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+ "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
"dev": true,
"requires": {
- "@types/color-name": "^1.1.1",
"color-convert": "^2.0.1"
}
},
@@ -313,15 +282,9 @@
"dev": true
},
"confusing-browser-globals": {
- "version": "1.0.9",
- "resolved": "https://registry.npmjs.org/confusing-browser-globals/-/confusing-browser-globals-1.0.9.tgz",
- "integrity": "sha512-KbS1Y0jMtyPgIxjO7ZzMAuUpAKMt1SzCL9fsrKsX6b0zJPTaT0SiSPmewwVZg9UAO83HVIlEhZF84LIjZ0lmAw==",
- "dev": true
- },
- "contains-path": {
- "version": "0.1.0",
- "resolved": "https://registry.npmjs.org/contains-path/-/contains-path-0.1.0.tgz",
- "integrity": "sha1-/ozxhP9mcLa67wGp1IYaXL7EEgo=",
+ "version": "1.0.10",
+ "resolved": "https://registry.npmjs.org/confusing-browser-globals/-/confusing-browser-globals-1.0.10.tgz",
+ "integrity": "sha512-gNld/3lySHwuhaVluJUKLePYirM3QNCKzVxqAdhJII9/WXKVX5PURzMVJspS1jTslSqjeuG4KMVTSouit5YPHA==",
"dev": true
},
"cross-spawn": {
@@ -336,12 +299,12 @@
}
},
"debug": {
- "version": "4.1.1",
- "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
- "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
+ "version": "4.3.1",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz",
+ "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==",
"dev": true,
"requires": {
- "ms": "^2.1.1"
+ "ms": "2.1.2"
}
},
"deep-is": {
@@ -369,9 +332,9 @@
}
},
"emoji-regex": {
- "version": "7.0.3",
- "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz",
- "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==",
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
+ "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
"dev": true
},
"enquirer": {
@@ -399,23 +362,27 @@
}
},
"es-abstract": {
- "version": "1.18.0-next.0",
- "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.18.0-next.0.tgz",
- "integrity": "sha512-elZXTZXKn51hUBdJjSZGYRujuzilgXo8vSPQzjGYXLvSlGiCo8VO8ZGV3kjo9a0WNJJ57hENagwbtlRuHuzkcQ==",
+ "version": "1.18.3",
+ "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.18.3.tgz",
+ "integrity": "sha512-nQIr12dxV7SSxE6r6f1l3DtAeEYdsGpps13dR0TwJg1S8gyp4ZPgy3FZcHBgbiQqnoqSTb+oC+kO4UQ0C/J8vw==",
"dev": true,
"requires": {
+ "call-bind": "^1.0.2",
"es-to-primitive": "^1.2.1",
"function-bind": "^1.1.1",
+ "get-intrinsic": "^1.1.1",
"has": "^1.0.3",
- "has-symbols": "^1.0.1",
- "is-callable": "^1.2.0",
- "is-negative-zero": "^2.0.0",
- "is-regex": "^1.1.1",
- "object-inspect": "^1.8.0",
+ "has-symbols": "^1.0.2",
+ "is-callable": "^1.2.3",
+ "is-negative-zero": "^2.0.1",
+ "is-regex": "^1.1.3",
+ "is-string": "^1.0.6",
+ "object-inspect": "^1.10.3",
"object-keys": "^1.1.1",
- "object.assign": "^4.1.0",
- "string.prototype.trimend": "^1.0.1",
- "string.prototype.trimstart": "^1.0.1"
+ "object.assign": "^4.1.2",
+ "string.prototype.trimend": "^1.0.4",
+ "string.prototype.trimstart": "^1.0.4",
+ "unbox-primitive": "^1.0.1"
}
},
"es-to-primitive": {
@@ -430,35 +397,37 @@
}
},
"escape-string-regexp": {
- "version": "1.0.5",
- "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
- "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=",
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
+ "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==",
"dev": true
},
"eslint": {
- "version": "7.9.0",
- "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.9.0.tgz",
- "integrity": "sha512-V6QyhX21+uXp4T+3nrNfI3hQNBDa/P8ga7LoQOenwrlEFXrEnUEE+ok1dMtaS3b6rmLXhT1TkTIsG75HMLbknA==",
+ "version": "7.28.0",
+ "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.28.0.tgz",
+ "integrity": "sha512-UMfH0VSjP0G4p3EWirscJEQ/cHqnT/iuH6oNZOB94nBjWbMnhGEPxsZm1eyIW0C/9jLI0Fow4W5DXLjEI7mn1g==",
"dev": true,
"requires": {
- "@babel/code-frame": "^7.0.0",
- "@eslint/eslintrc": "^0.1.3",
+ "@babel/code-frame": "7.12.11",
+ "@eslint/eslintrc": "^0.4.2",
"ajv": "^6.10.0",
"chalk": "^4.0.0",
"cross-spawn": "^7.0.2",
"debug": "^4.0.1",
"doctrine": "^3.0.0",
"enquirer": "^2.3.5",
- "eslint-scope": "^5.1.0",
+ "escape-string-regexp": "^4.0.0",
+ "eslint-scope": "^5.1.1",
"eslint-utils": "^2.1.0",
- "eslint-visitor-keys": "^1.3.0",
- "espree": "^7.3.0",
- "esquery": "^1.2.0",
+ "eslint-visitor-keys": "^2.0.0",
+ "espree": "^7.3.1",
+ "esquery": "^1.4.0",
"esutils": "^2.0.2",
- "file-entry-cache": "^5.0.1",
+ "fast-deep-equal": "^3.1.3",
+ "file-entry-cache": "^6.0.1",
"functional-red-black-tree": "^1.0.1",
- "glob-parent": "^5.0.0",
- "globals": "^12.1.0",
+ "glob-parent": "^5.1.2",
+ "globals": "^13.6.0",
"ignore": "^4.0.6",
"import-fresh": "^3.0.0",
"imurmurhash": "^0.1.4",
@@ -466,7 +435,7 @@
"js-yaml": "^3.13.1",
"json-stable-stringify-without-jsonify": "^1.0.1",
"levn": "^0.4.1",
- "lodash": "^4.17.19",
+ "lodash.merge": "^4.6.2",
"minimatch": "^3.0.4",
"natural-compare": "^1.4.0",
"optionator": "^0.9.1",
@@ -475,19 +444,19 @@
"semver": "^7.2.1",
"strip-ansi": "^6.0.0",
"strip-json-comments": "^3.1.0",
- "table": "^5.2.3",
+ "table": "^6.0.9",
"text-table": "^0.2.0",
"v8-compile-cache": "^2.0.3"
}
},
"eslint-config-airbnb-base": {
- "version": "14.2.0",
- "resolved": "https://registry.npmjs.org/eslint-config-airbnb-base/-/eslint-config-airbnb-base-14.2.0.tgz",
- "integrity": "sha512-Snswd5oC6nJaevs3nZoLSTvGJBvzTfnBqOIArkf3cbyTyq9UD79wOk8s+RiL6bhca0p/eRO6veczhf6A/7Jy8Q==",
+ "version": "14.2.1",
+ "resolved": "https://registry.npmjs.org/eslint-config-airbnb-base/-/eslint-config-airbnb-base-14.2.1.tgz",
+ "integrity": "sha512-GOrQyDtVEc1Xy20U7vsB2yAoB4nBlfH5HZJeatRXHleO+OS5Ot+MWij4Dpltw4/DyIkqUfqz1epfhVR5XWWQPA==",
"dev": true,
"requires": {
- "confusing-browser-globals": "^1.0.9",
- "object.assign": "^4.1.0",
+ "confusing-browser-globals": "^1.0.10",
+ "object.assign": "^4.1.2",
"object.entries": "^1.1.2"
}
},
@@ -519,50 +488,46 @@
}
},
"eslint-module-utils": {
- "version": "2.6.0",
- "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.6.0.tgz",
- "integrity": "sha512-6j9xxegbqe8/kZY8cYpcp0xhbK0EgJlg3g9mib3/miLaExuuwc3n5UEfSnU6hWMbT0FAYVvDbL9RrRgpUeQIvA==",
+ "version": "2.6.1",
+ "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.6.1.tgz",
+ "integrity": "sha512-ZXI9B8cxAJIH4nfkhTwcRTEAnrVfobYqwjWy/QMCZ8rHkZHFjf9yO4BzpiF9kCSfNlMG54eKigISHpX0+AaT4A==",
"dev": true,
"requires": {
- "debug": "^2.6.9",
+ "debug": "^3.2.7",
"pkg-dir": "^2.0.0"
},
"dependencies": {
"debug": {
- "version": "2.6.9",
- "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
- "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+ "version": "3.2.7",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz",
+ "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==",
"dev": true,
"requires": {
- "ms": "2.0.0"
+ "ms": "^2.1.1"
}
- },
- "ms": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
- "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
- "dev": true
}
}
},
"eslint-plugin-import": {
- "version": "2.22.0",
- "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.22.0.tgz",
- "integrity": "sha512-66Fpf1Ln6aIS5Gr/55ts19eUuoDhAbZgnr6UxK5hbDx6l/QgQgx61AePq+BV4PP2uXQFClgMVzep5zZ94qqsxg==",
+ "version": "2.23.4",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.23.4.tgz",
+ "integrity": "sha512-6/wP8zZRsnQFiR3iaPFgh5ImVRM1WN5NUWfTIRqwOdeiGJlBcSk82o1FEVq8yXmy4lkIzTo7YhHCIxlU/2HyEQ==",
"dev": true,
"requires": {
- "array-includes": "^3.1.1",
- "array.prototype.flat": "^1.2.3",
- "contains-path": "^0.1.0",
+ "array-includes": "^3.1.3",
+ "array.prototype.flat": "^1.2.4",
"debug": "^2.6.9",
- "doctrine": "1.5.0",
- "eslint-import-resolver-node": "^0.3.3",
- "eslint-module-utils": "^2.6.0",
+ "doctrine": "^2.1.0",
+ "eslint-import-resolver-node": "^0.3.4",
+ "eslint-module-utils": "^2.6.1",
+ "find-up": "^2.0.0",
"has": "^1.0.3",
+ "is-core-module": "^2.4.0",
"minimatch": "^3.0.4",
- "object.values": "^1.1.1",
- "read-pkg-up": "^2.0.0",
- "resolve": "^1.17.0",
+ "object.values": "^1.1.3",
+ "pkg-up": "^2.0.0",
+ "read-pkg-up": "^3.0.0",
+ "resolve": "^1.20.0",
"tsconfig-paths": "^3.9.0"
},
"dependencies": {
@@ -576,13 +541,12 @@
}
},
"doctrine": {
- "version": "1.5.0",
- "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-1.5.0.tgz",
- "integrity": "sha1-N53Ocw9hZvds76TmcHoVmwLFpvo=",
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz",
+ "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==",
"dev": true,
"requires": {
- "esutils": "^2.0.2",
- "isarray": "^1.0.0"
+ "esutils": "^2.0.2"
}
},
"ms": {
@@ -610,23 +574,39 @@
"dev": true,
"requires": {
"eslint-visitor-keys": "^1.1.0"
+ },
+ "dependencies": {
+ "eslint-visitor-keys": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz",
+ "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==",
+ "dev": true
+ }
}
},
"eslint-visitor-keys": {
- "version": "1.3.0",
- "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz",
- "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==",
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz",
+ "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==",
"dev": true
},
"espree": {
- "version": "7.3.0",
- "resolved": "https://registry.npmjs.org/espree/-/espree-7.3.0.tgz",
- "integrity": "sha512-dksIWsvKCixn1yrEXO8UosNSxaDoSYpq9reEjZSbHLpT5hpaCAKTLBwq0RHtLrIr+c0ByiYzWT8KTMRzoRCNlw==",
+ "version": "7.3.1",
+ "resolved": "https://registry.npmjs.org/espree/-/espree-7.3.1.tgz",
+ "integrity": "sha512-v3JCNCE64umkFpmkFGqzVKsOT0tN1Zr+ueqLZfpV1Ob8e+CEgPWa+OxCoGH3tnhimMKIaBm4m/vaRpJ/krRz2g==",
"dev": true,
"requires": {
"acorn": "^7.4.0",
- "acorn-jsx": "^5.2.0",
+ "acorn-jsx": "^5.3.1",
"eslint-visitor-keys": "^1.3.0"
+ },
+ "dependencies": {
+ "eslint-visitor-keys": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz",
+ "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==",
+ "dev": true
+ }
}
},
"esprima": {
@@ -636,9 +616,9 @@
"dev": true
},
"esquery": {
- "version": "1.3.1",
- "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.3.1.tgz",
- "integrity": "sha512-olpvt9QG0vniUBZspVRN6lwB7hOZoTRtT+jzR+tS4ffYx2mzbw+z0XCOk44aaLYKApNX5nMm+E+P6o25ip/DHQ==",
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz",
+ "integrity": "sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==",
"dev": true,
"requires": {
"estraverse": "^5.1.0"
@@ -700,12 +680,12 @@
"dev": true
},
"file-entry-cache": {
- "version": "5.0.1",
- "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-5.0.1.tgz",
- "integrity": "sha512-bCg29ictuBaKUwwArK4ouCaqDgLZcysCFLmM/Yn/FDoqndh/9vNuQfXRDvTuXKLxfD/JtZQGKFT8MGcJBK644g==",
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz",
+ "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==",
"dev": true,
"requires": {
- "flat-cache": "^2.0.1"
+ "flat-cache": "^3.0.4"
}
},
"find-up": {
@@ -718,20 +698,19 @@
}
},
"flat-cache": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-2.0.1.tgz",
- "integrity": "sha512-LoQe6yDuUMDzQAEH8sgmh4Md6oZnc/7PjtwjNFSzveXqSHt6ka9fPBuso7IGf9Rz4uqnSnWiFH2B/zj24a5ReA==",
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz",
+ "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==",
"dev": true,
"requires": {
- "flatted": "^2.0.0",
- "rimraf": "2.6.3",
- "write": "1.0.3"
+ "flatted": "^3.1.0",
+ "rimraf": "^3.0.2"
}
},
"flatted": {
- "version": "2.0.2",
- "resolved": "https://registry.npmjs.org/flatted/-/flatted-2.0.2.tgz",
- "integrity": "sha512-r5wGx7YeOwNWNlCA0wQ86zKyDLMQr+/RB8xy74M4hTphfmjlijTSSXGuH8rnvKZnfT9i+75zmd8jcKdMR4O6jA==",
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.1.1.tgz",
+ "integrity": "sha512-zAoAQiudy+r5SvnSw3KJy5os/oRJYHzrzja/tBDqrZtNhUw8bt6y8OBzMWcjWr+8liV8Eb6yOhw8WZ7VFZ5ZzA==",
"dev": true
},
"fs.realpath": {
@@ -752,10 +731,21 @@
"integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=",
"dev": true
},
+ "get-intrinsic": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz",
+ "integrity": "sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==",
+ "dev": true,
+ "requires": {
+ "function-bind": "^1.1.1",
+ "has": "^1.0.3",
+ "has-symbols": "^1.0.1"
+ }
+ },
"glob": {
- "version": "7.1.6",
- "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz",
- "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==",
+ "version": "7.1.7",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz",
+ "integrity": "sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==",
"dev": true,
"requires": {
"fs.realpath": "^1.0.0",
@@ -767,27 +757,27 @@
}
},
"glob-parent": {
- "version": "5.1.1",
- "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.1.tgz",
- "integrity": "sha512-FnI+VGOpnlGHWZxthPGR+QhR78fuiK0sNLkHQv+bL9fQi57lNNdquIbna/WrfROrolq8GK5Ek6BiMwqL/voRYQ==",
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
+ "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
"dev": true,
"requires": {
"is-glob": "^4.0.1"
}
},
"globals": {
- "version": "12.4.0",
- "resolved": "https://registry.npmjs.org/globals/-/globals-12.4.0.tgz",
- "integrity": "sha512-BWICuzzDvDoH54NHKCseDanAhE3CeDorgDL5MT6LMXXj2WCnd9UC2szdk4AWLfjdgNBCXLUanXYcpBBKOSWGwg==",
+ "version": "13.9.0",
+ "resolved": "https://registry.npmjs.org/globals/-/globals-13.9.0.tgz",
+ "integrity": "sha512-74/FduwI/JaIrr1H8e71UbDE+5x7pIPs1C2rrwC52SszOo043CsWOZEMW7o2Y58xwm9b+0RBKDxY5n2sUpEFxA==",
"dev": true,
"requires": {
- "type-fest": "^0.8.1"
+ "type-fest": "^0.20.2"
}
},
"graceful-fs": {
- "version": "4.2.4",
- "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz",
- "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==",
+ "version": "4.2.6",
+ "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.6.tgz",
+ "integrity": "sha512-nTnJ528pbqxYanhpDYsi4Rd8MAeaBA67+RZ10CM1m3bTAVFEDcd5AuA4a6W5YkGZ1iNXHzZz8T6TBKLeBuNriQ==",
"dev": true
},
"has": {
@@ -799,6 +789,12 @@
"function-bind": "^1.1.1"
}
},
+ "has-bigints": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.1.tgz",
+ "integrity": "sha512-LSBS2LjbNBTf6287JEbEzvJgftkF5qFkmCo9hDRpAzKhUOlJ+hx8dd4USs00SgsUNwc4617J9ki5YtEClM2ffA==",
+ "dev": true
+ },
"has-flag": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
@@ -806,15 +802,15 @@
"dev": true
},
"has-symbols": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz",
- "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==",
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.2.tgz",
+ "integrity": "sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw==",
"dev": true
},
"hosted-git-info": {
- "version": "2.8.8",
- "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.8.tgz",
- "integrity": "sha512-f/wzC2QaWBs7t9IYqB4T3sR1xviIViXJRJTWBlx2Gf3g0Xi5vI7Yy4koXQ1c9OYDGHN9sBy1DQ2AB8fqZBWhUg==",
+ "version": "2.8.9",
+ "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz",
+ "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==",
"dev": true
},
"ignore": {
@@ -824,9 +820,9 @@
"dev": true
},
"import-fresh": {
- "version": "3.2.1",
- "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.2.1.tgz",
- "integrity": "sha512-6e1q1cnWP2RXD9/keSkxHScg508CdXqXWgWBaETNhyuBFz+kUZlKboh+ISK+bU++DmbHimVBrOz/zzPe0sZ3sQ==",
+ "version": "3.3.0",
+ "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz",
+ "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==",
"dev": true,
"requires": {
"parent-module": "^1.0.0",
@@ -861,16 +857,40 @@
"integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=",
"dev": true
},
+ "is-bigint": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.2.tgz",
+ "integrity": "sha512-0JV5+SOCQkIdzjBK9buARcV804Ddu7A0Qet6sHi3FimE9ne6m4BGQZfRn+NZiXbBk4F4XmHfDZIipLj9pX8dSA==",
+ "dev": true
+ },
+ "is-boolean-object": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.1.tgz",
+ "integrity": "sha512-bXdQWkECBUIAcCkeH1unwJLIpZYaa5VvuygSyS/c2lf719mTKZDU5UdDRlpd01UjADgmW8RfqaP+mRaVPdr/Ng==",
+ "dev": true,
+ "requires": {
+ "call-bind": "^1.0.2"
+ }
+ },
"is-callable": {
- "version": "1.2.1",
- "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.1.tgz",
- "integrity": "sha512-wliAfSzx6V+6WfMOmus1xy0XvSgf/dlStkvTfq7F0g4bOIW0PSUbnyse3NhDwdyYS1ozfUtAAySqTws3z9Eqgg==",
+ "version": "1.2.3",
+ "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.3.tgz",
+ "integrity": "sha512-J1DcMe8UYTBSrKezuIUTUwjXsho29693unXM2YhJUTR2txK/eG47bvNa/wipPFmZFgr/N6f1GA66dv0mEyTIyQ==",
"dev": true
},
+ "is-core-module": {
+ "version": "2.4.0",
+ "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.4.0.tgz",
+ "integrity": "sha512-6A2fkfq1rfeQZjxrZJGerpLCTHRNEBiSgnu0+obeJpEPZRUooHgsizvzv0ZjJwOz3iWIHdJtVWJ/tmPr3D21/A==",
+ "dev": true,
+ "requires": {
+ "has": "^1.0.3"
+ }
+ },
"is-date-object": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.2.tgz",
- "integrity": "sha512-USlDT524woQ08aoZFzh3/Z6ch9Y/EWXEHQ/AaRN0SkKq4t2Jw2R2339tSXmwuVoY7LLlBCbOIlx2myP/L5zk0g==",
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.4.tgz",
+ "integrity": "sha512-/b4ZVsG7Z5XVtIxs/h9W8nvfLgSAyKYdtGWQLbqy6jA1icmgjf8WCoTKgeS4wy5tYaPePouzFMANbnj94c2Z+A==",
"dev": true
},
"is-extglob": {
@@ -880,9 +900,9 @@
"dev": true
},
"is-fullwidth-code-point": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz",
- "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=",
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
+ "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
"dev": true
},
"is-glob": {
@@ -895,41 +915,42 @@
}
},
"is-negative-zero": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.0.tgz",
- "integrity": "sha1-lVOxIbD6wohp2p7UWeIMdUN4hGE=",
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.1.tgz",
+ "integrity": "sha512-2z6JzQvZRa9A2Y7xC6dQQm4FSTSTNWjKIYYTt4246eMTJmIo0Q+ZyOsU66X8lxK1AbB92dFeglPLrhwpeRKO6w==",
+ "dev": true
+ },
+ "is-number-object": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.5.tgz",
+ "integrity": "sha512-RU0lI/n95pMoUKu9v1BZP5MBcZuNSVJkMkAG2dJqC4z2GlkGUNeH68SuHuBKBD/XFe+LHZ+f9BKkLET60Niedw==",
"dev": true
},
"is-regex": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.1.tgz",
- "integrity": "sha512-1+QkEcxiLlB7VEyFtyBg94e08OAsvq7FUBgApTq/w2ymCLyKJgDPsybBENVtA7XCQEgEXxKPonG+mvYRxh/LIg==",
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.3.tgz",
+ "integrity": "sha512-qSVXFz28HM7y+IWX6vLCsexdlvzT1PJNFSBuaQLQ5o0IEw8UDYW6/2+eCMVyIsbM8CNLX2a/QWmSpyxYEHY7CQ==",
"dev": true,
"requires": {
- "has-symbols": "^1.0.1"
+ "call-bind": "^1.0.2",
+ "has-symbols": "^1.0.2"
}
},
"is-string": {
- "version": "1.0.5",
- "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.5.tgz",
- "integrity": "sha512-buY6VNRjhQMiF1qWDouloZlQbRhDPCebwxSjxMjxgemYT46YMd2NR0/H+fBhEfWX4A/w9TBJ+ol+okqJKFE6vQ==",
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.6.tgz",
+ "integrity": "sha512-2gdzbKUuqtQ3lYNrUTQYoClPhm7oQu4UdpSZMp1/DGgkHBT8E2Z1l0yMdb6D4zNAxwDiMv8MdulKROJGNl0Q0w==",
"dev": true
},
"is-symbol": {
- "version": "1.0.3",
- "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.3.tgz",
- "integrity": "sha512-OwijhaRSgqvhm/0ZdAcXNZt9lYdKFpcRDT5ULUuYXPoT794UNOdU+gpT6Rzo7b4V2HUl/op6GqY894AZwv9faQ==",
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz",
+ "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==",
"dev": true,
"requires": {
- "has-symbols": "^1.0.1"
+ "has-symbols": "^1.0.2"
}
},
- "isarray": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
- "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=",
- "dev": true
- },
"isexe": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
@@ -943,9 +964,9 @@
"dev": true
},
"js-yaml": {
- "version": "3.14.0",
- "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.0.tgz",
- "integrity": "sha512-/4IbIeHcD9VMHFqDR/gQ7EdZdLimOvW2DdcxFjdyyZ9NsbS+ccrXqVWDtab/lRl5AlUqmpBx8EhPaWR+OtY17A==",
+ "version": "3.14.1",
+ "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz",
+ "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==",
"dev": true,
"requires": {
"argparse": "^1.0.7",
@@ -962,25 +983,25 @@
}
},
"jsdoc": {
- "version": "3.6.6",
- "resolved": "https://registry.npmjs.org/jsdoc/-/jsdoc-3.6.6.tgz",
- "integrity": "sha512-znR99e1BHeyEkSvgDDpX0sTiTu+8aQyDl9DawrkOGZTTW8hv0deIFXx87114zJ7gRaDZKVQD/4tr1ifmJp9xhQ==",
+ "version": "3.6.7",
+ "resolved": "https://registry.npmjs.org/jsdoc/-/jsdoc-3.6.7.tgz",
+ "integrity": "sha512-sxKt7h0vzCd+3Y81Ey2qinupL6DpRSZJclS04ugHDNmRUXGzqicMJ6iwayhSA0S0DwwX30c5ozyUthr1QKF6uw==",
"dev": true,
"requires": {
"@babel/parser": "^7.9.4",
"bluebird": "^3.7.2",
- "catharsis": "^0.8.11",
+ "catharsis": "^0.9.0",
"escape-string-regexp": "^2.0.0",
"js2xmlparser": "^4.0.1",
"klaw": "^3.0.0",
"markdown-it": "^10.0.0",
"markdown-it-anchor": "^5.2.7",
- "marked": "^0.8.2",
+ "marked": "^2.0.3",
"mkdirp": "^1.0.4",
"requizzle": "^0.2.3",
"strip-json-comments": "^3.1.0",
"taffydb": "2.6.2",
- "underscore": "~1.10.2"
+ "underscore": "~1.13.1"
},
"dependencies": {
"escape-string-regexp": {
@@ -988,15 +1009,15 @@
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz",
"integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==",
"dev": true
- },
- "mkdirp": {
- "version": "1.0.4",
- "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz",
- "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==",
- "dev": true
}
}
},
+ "json-parse-better-errors": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz",
+ "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==",
+ "dev": true
+ },
"json-schema-traverse": {
"version": "0.4.1",
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
@@ -1047,14 +1068,14 @@
}
},
"load-json-file": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-2.0.0.tgz",
- "integrity": "sha1-eUfkIUmvgNaWy/eXvKq8/h/inKg=",
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz",
+ "integrity": "sha1-L19Fq5HjMhYjT9U62rZo607AmTs=",
"dev": true,
"requires": {
"graceful-fs": "^4.1.2",
- "parse-json": "^2.2.0",
- "pify": "^2.0.0",
+ "parse-json": "^4.0.0",
+ "pify": "^3.0.0",
"strip-bom": "^3.0.0"
}
},
@@ -1069,11 +1090,38 @@
}
},
"lodash": {
- "version": "4.17.20",
- "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz",
- "integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==",
+ "version": "4.17.21",
+ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
+ "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
+ "dev": true
+ },
+ "lodash.clonedeep": {
+ "version": "4.5.0",
+ "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz",
+ "integrity": "sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8=",
+ "dev": true
+ },
+ "lodash.merge": {
+ "version": "4.6.2",
+ "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
+ "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==",
+ "dev": true
+ },
+ "lodash.truncate": {
+ "version": "4.4.2",
+ "resolved": "https://registry.npmjs.org/lodash.truncate/-/lodash.truncate-4.4.2.tgz",
+ "integrity": "sha1-WjUNoLERO4N+z//VgSy+WNbq4ZM=",
"dev": true
},
+ "lru-cache": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
+ "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
+ "dev": true,
+ "requires": {
+ "yallist": "^4.0.0"
+ }
+ },
"markdown-it": {
"version": "10.0.0",
"resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-10.0.0.tgz",
@@ -1094,9 +1142,9 @@
"dev": true
},
"marked": {
- "version": "0.8.2",
- "resolved": "https://registry.npmjs.org/marked/-/marked-0.8.2.tgz",
- "integrity": "sha512-EGwzEeCcLniFX51DhTpmTom+dSA/MG/OBUDjnWtHbEnjAH180VzUeAw+oE4+Zv+CoYBWyRlYOTR0N8SO9R1PVw==",
+ "version": "2.0.7",
+ "resolved": "https://registry.npmjs.org/marked/-/marked-2.0.7.tgz",
+ "integrity": "sha512-BJXxkuIfJchcXOJWTT2DOL+yFWifFv2yGYOUzvXg8Qz610QKw+sHCvTMYwA+qWGhlA2uivBezChZ/pBy1tWdkQ==",
"dev": true
},
"mdurl": {
@@ -1121,13 +1169,10 @@
"dev": true
},
"mkdirp": {
- "version": "0.5.5",
- "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz",
- "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==",
- "dev": true,
- "requires": {
- "minimist": "^1.2.5"
- }
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz",
+ "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==",
+ "dev": true
},
"ms": {
"version": "2.1.2",
@@ -1162,9 +1207,9 @@
}
},
"object-inspect": {
- "version": "1.8.0",
- "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.8.0.tgz",
- "integrity": "sha512-jLdtEOB112fORuypAyl/50VRVIBIdVQOSUUGQHzJ4xBSbit81zRarz7GThkEFZy1RceYrWYcPcBFPQwHyAc1gA==",
+ "version": "1.10.3",
+ "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.10.3.tgz",
+ "integrity": "sha512-e5mCJlSH7poANfC8z8S9s9S2IN5/4Zb3aZ33f5s8YqoazCFzNLloLU8r5VCG+G7WoqLvAAZoVMcy3tp/3X0Plw==",
"dev": true
},
"object-keys": {
@@ -1174,80 +1219,37 @@
"dev": true
},
"object.assign": {
- "version": "4.1.1",
- "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.1.tgz",
- "integrity": "sha512-VT/cxmx5yaoHSOTSyrCygIDFco+RsibY2NM0a4RdEeY/4KgqezwFtK1yr3U67xYhqJSlASm2pKhLVzPj2lr4bA==",
+ "version": "4.1.2",
+ "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.2.tgz",
+ "integrity": "sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==",
"dev": true,
"requires": {
+ "call-bind": "^1.0.0",
"define-properties": "^1.1.3",
- "es-abstract": "^1.18.0-next.0",
"has-symbols": "^1.0.1",
"object-keys": "^1.1.1"
}
},
"object.entries": {
- "version": "1.1.2",
- "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.2.tgz",
- "integrity": "sha512-BQdB9qKmb/HyNdMNWVr7O3+z5MUIx3aiegEIJqjMBbBf0YT9RRxTJSim4mzFqtyr7PDAHigq0N9dO0m0tRakQA==",
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.4.tgz",
+ "integrity": "sha512-h4LWKWE+wKQGhtMjZEBud7uLGhqyLwj8fpHOarZhD2uY3C9cRtk57VQ89ke3moByLXMedqs3XCHzyb4AmA2DjA==",
"dev": true,
"requires": {
+ "call-bind": "^1.0.2",
"define-properties": "^1.1.3",
- "es-abstract": "^1.17.5",
- "has": "^1.0.3"
- },
- "dependencies": {
- "es-abstract": {
- "version": "1.17.6",
- "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.6.tgz",
- "integrity": "sha512-Fr89bON3WFyUi5EvAeI48QTWX0AyekGgLA8H+c+7fbfCkJwRWRMLd8CQedNEyJuoYYhmtEqY92pgte1FAhBlhw==",
- "dev": true,
- "requires": {
- "es-to-primitive": "^1.2.1",
- "function-bind": "^1.1.1",
- "has": "^1.0.3",
- "has-symbols": "^1.0.1",
- "is-callable": "^1.2.0",
- "is-regex": "^1.1.0",
- "object-inspect": "^1.7.0",
- "object-keys": "^1.1.1",
- "object.assign": "^4.1.0",
- "string.prototype.trimend": "^1.0.1",
- "string.prototype.trimstart": "^1.0.1"
- }
- }
+ "es-abstract": "^1.18.2"
}
},
"object.values": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.1.tgz",
- "integrity": "sha512-WTa54g2K8iu0kmS/us18jEmdv1a4Wi//BZ/DTVYEcH0XhLM5NYdpDHja3gt57VrZLcNAO2WGA+KpWsDBaHt6eA==",
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.4.tgz",
+ "integrity": "sha512-TnGo7j4XSnKQoK3MfvkzqKCi0nVe/D9I9IjwTNYdb/fxYHpjrluHVOgw0AF6jrRFGMPHdfuidR09tIDiIvnaSg==",
"dev": true,
"requires": {
+ "call-bind": "^1.0.2",
"define-properties": "^1.1.3",
- "es-abstract": "^1.17.0-next.1",
- "function-bind": "^1.1.1",
- "has": "^1.0.3"
- },
- "dependencies": {
- "es-abstract": {
- "version": "1.17.6",
- "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.6.tgz",
- "integrity": "sha512-Fr89bON3WFyUi5EvAeI48QTWX0AyekGgLA8H+c+7fbfCkJwRWRMLd8CQedNEyJuoYYhmtEqY92pgte1FAhBlhw==",
- "dev": true,
- "requires": {
- "es-to-primitive": "^1.2.1",
- "function-bind": "^1.1.1",
- "has": "^1.0.3",
- "has-symbols": "^1.0.1",
- "is-callable": "^1.2.0",
- "is-regex": "^1.1.0",
- "object-inspect": "^1.7.0",
- "object-keys": "^1.1.1",
- "object.assign": "^4.1.0",
- "string.prototype.trimend": "^1.0.1",
- "string.prototype.trimstart": "^1.0.1"
- }
- }
+ "es-abstract": "^1.18.2"
}
},
"once": {
@@ -1307,12 +1309,13 @@
}
},
"parse-json": {
- "version": "2.2.0",
- "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz",
- "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=",
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz",
+ "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=",
"dev": true,
"requires": {
- "error-ex": "^1.2.0"
+ "error-ex": "^1.3.1",
+ "json-parse-better-errors": "^1.0.1"
}
},
"path-exists": {
@@ -1334,24 +1337,24 @@
"dev": true
},
"path-parse": {
- "version": "1.0.6",
- "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz",
- "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==",
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
+ "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==",
"dev": true
},
"path-type": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/path-type/-/path-type-2.0.0.tgz",
- "integrity": "sha1-8BLMuEFbcJb8LaoQVMPXI4lZTHM=",
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz",
+ "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==",
"dev": true,
"requires": {
- "pify": "^2.0.0"
+ "pify": "^3.0.0"
}
},
"pify": {
- "version": "2.3.0",
- "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
- "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=",
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz",
+ "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=",
"dev": true
},
"pkg-dir": {
@@ -1363,6 +1366,15 @@
"find-up": "^2.1.0"
}
},
+ "pkg-up": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/pkg-up/-/pkg-up-2.0.0.tgz",
+ "integrity": "sha1-yBmscoBZpGHKscOImivjxJoATX8=",
+ "dev": true,
+ "requires": {
+ "find-up": "^2.1.0"
+ }
+ },
"prelude-ls": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz",
@@ -1382,30 +1394,36 @@
"dev": true
},
"read-pkg": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-2.0.0.tgz",
- "integrity": "sha1-jvHAYjxqbbDcZxPEv6xGMysjaPg=",
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz",
+ "integrity": "sha1-nLxoaXj+5l0WwA4rGcI3/Pbjg4k=",
"dev": true,
"requires": {
- "load-json-file": "^2.0.0",
+ "load-json-file": "^4.0.0",
"normalize-package-data": "^2.3.2",
- "path-type": "^2.0.0"
+ "path-type": "^3.0.0"
}
},
"read-pkg-up": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-2.0.0.tgz",
- "integrity": "sha1-a3KoBImE4MQeeVEP1en6mbO1Sb4=",
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-3.0.0.tgz",
+ "integrity": "sha1-PtSWaF26D4/hGNBpHcUfSh/5bwc=",
"dev": true,
"requires": {
"find-up": "^2.0.0",
- "read-pkg": "^2.0.0"
+ "read-pkg": "^3.0.0"
}
},
"regexpp": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.1.0.tgz",
- "integrity": "sha512-ZOIzd8yVsQQA7j8GCSlPGXwg5PfmA1mrq0JP4nGhh54LaKN3xdai/vHUDu74pKwV8OxseMS65u2NImosQcSD0Q==",
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz",
+ "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==",
+ "dev": true
+ },
+ "require-from-string": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz",
+ "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==",
"dev": true
},
"requizzle": {
@@ -1418,11 +1436,12 @@
}
},
"resolve": {
- "version": "1.17.0",
- "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.17.0.tgz",
- "integrity": "sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w==",
+ "version": "1.20.0",
+ "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.20.0.tgz",
+ "integrity": "sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A==",
"dev": true,
"requires": {
+ "is-core-module": "^2.2.0",
"path-parse": "^1.0.6"
}
},
@@ -1433,19 +1452,22 @@
"dev": true
},
"rimraf": {
- "version": "2.6.3",
- "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz",
- "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==",
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz",
+ "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==",
"dev": true,
"requires": {
"glob": "^7.1.3"
}
},
"semver": {
- "version": "7.3.2",
- "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.2.tgz",
- "integrity": "sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ==",
- "dev": true
+ "version": "7.3.5",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz",
+ "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==",
+ "dev": true,
+ "requires": {
+ "lru-cache": "^6.0.0"
+ }
},
"shebang-command": {
"version": "2.0.0",
@@ -1463,14 +1485,40 @@
"dev": true
},
"slice-ansi": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-2.1.0.tgz",
- "integrity": "sha512-Qu+VC3EwYLldKa1fCxuuvULvSJOKEgk9pi8dZeCVK7TqBfUNTH4sFkk4joj8afVSfAYgJoSOetjx9QWOJ5mYoQ==",
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz",
+ "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==",
"dev": true,
"requires": {
- "ansi-styles": "^3.2.0",
- "astral-regex": "^1.0.0",
- "is-fullwidth-code-point": "^2.0.0"
+ "ansi-styles": "^4.0.0",
+ "astral-regex": "^2.0.0",
+ "is-fullwidth-code-point": "^3.0.0"
+ },
+ "dependencies": {
+ "ansi-styles": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+ "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+ "dev": true,
+ "requires": {
+ "color-convert": "^2.0.1"
+ }
+ },
+ "color-convert": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+ "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+ "dev": true,
+ "requires": {
+ "color-name": "~1.1.4"
+ }
+ },
+ "color-name": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+ "dev": true
+ }
}
},
"spdx-correct": {
@@ -1500,9 +1548,9 @@
}
},
"spdx-license-ids": {
- "version": "3.0.6",
- "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.6.tgz",
- "integrity": "sha512-+orQK83kyMva3WyPf59k1+Y525csj5JejicWut55zeTWANuN17qSiSLUXWtzHeNWORSvT7GLDJ/E/XiIWoXBTw==",
+ "version": "3.0.9",
+ "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.9.tgz",
+ "integrity": "sha512-Ki212dKK4ogX+xDo4CtOZBVIwhsKBEfsEEcwmJfLQzirgc2jIWdzg40Unxz/HzEUqM1WFzVlQSMF9kZZ2HboLQ==",
"dev": true
},
"sprintf-js": {
@@ -1512,93 +1560,34 @@
"dev": true
},
"string-width": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz",
- "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==",
+ "version": "4.2.2",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.2.tgz",
+ "integrity": "sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA==",
"dev": true,
"requires": {
- "emoji-regex": "^7.0.1",
- "is-fullwidth-code-point": "^2.0.0",
- "strip-ansi": "^5.1.0"
- },
- "dependencies": {
- "ansi-regex": {
- "version": "4.1.0",
- "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz",
- "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==",
- "dev": true
- },
- "strip-ansi": {
- "version": "5.2.0",
- "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz",
- "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==",
- "dev": true,
- "requires": {
- "ansi-regex": "^4.1.0"
- }
- }
+ "emoji-regex": "^8.0.0",
+ "is-fullwidth-code-point": "^3.0.0",
+ "strip-ansi": "^6.0.0"
}
},
"string.prototype.trimend": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.1.tgz",
- "integrity": "sha512-LRPxFUaTtpqYsTeNKaFOw3R4bxIzWOnbQ837QfBylo8jIxtcbK/A/sMV7Q+OAV/vWo+7s25pOE10KYSjaSO06g==",
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.4.tgz",
+ "integrity": "sha512-y9xCjw1P23Awk8EvTpcyL2NIr1j7wJ39f+k6lvRnSMz+mz9CGz9NYPelDk42kOz6+ql8xjfK8oYzy3jAP5QU5A==",
"dev": true,
"requires": {
- "define-properties": "^1.1.3",
- "es-abstract": "^1.17.5"
- },
- "dependencies": {
- "es-abstract": {
- "version": "1.17.6",
- "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.6.tgz",
- "integrity": "sha512-Fr89bON3WFyUi5EvAeI48QTWX0AyekGgLA8H+c+7fbfCkJwRWRMLd8CQedNEyJuoYYhmtEqY92pgte1FAhBlhw==",
- "dev": true,
- "requires": {
- "es-to-primitive": "^1.2.1",
- "function-bind": "^1.1.1",
- "has": "^1.0.3",
- "has-symbols": "^1.0.1",
- "is-callable": "^1.2.0",
- "is-regex": "^1.1.0",
- "object-inspect": "^1.7.0",
- "object-keys": "^1.1.1",
- "object.assign": "^4.1.0",
- "string.prototype.trimend": "^1.0.1",
- "string.prototype.trimstart": "^1.0.1"
- }
- }
+ "call-bind": "^1.0.2",
+ "define-properties": "^1.1.3"
}
},
"string.prototype.trimstart": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.1.tgz",
- "integrity": "sha512-XxZn+QpvrBI1FOcg6dIpxUPgWCPuNXvMD72aaRaUQv1eD4e/Qy8i/hFTe0BUmD60p/QA6bh1avmuPTfNjqVWRw==",
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.4.tgz",
+ "integrity": "sha512-jh6e984OBfvxS50tdY2nRZnoC5/mLFKOREQfw8t5yytkoUsJRNxvI/E39qu1sD0OtWI3OC0XgKSmcWwziwYuZw==",
"dev": true,
"requires": {
- "define-properties": "^1.1.3",
- "es-abstract": "^1.17.5"
- },
- "dependencies": {
- "es-abstract": {
- "version": "1.17.6",
- "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.6.tgz",
- "integrity": "sha512-Fr89bON3WFyUi5EvAeI48QTWX0AyekGgLA8H+c+7fbfCkJwRWRMLd8CQedNEyJuoYYhmtEqY92pgte1FAhBlhw==",
- "dev": true,
- "requires": {
- "es-to-primitive": "^1.2.1",
- "function-bind": "^1.1.1",
- "has": "^1.0.3",
- "has-symbols": "^1.0.1",
- "is-callable": "^1.2.0",
- "is-regex": "^1.1.0",
- "object-inspect": "^1.7.0",
- "object-keys": "^1.1.1",
- "object.assign": "^4.1.0",
- "string.prototype.trimend": "^1.0.1",
- "string.prototype.trimstart": "^1.0.1"
- }
- }
+ "call-bind": "^1.0.2",
+ "define-properties": "^1.1.3"
}
},
"strip-ansi": {
@@ -1632,15 +1621,37 @@
}
},
"table": {
- "version": "5.4.6",
- "resolved": "https://registry.npmjs.org/table/-/table-5.4.6.tgz",
- "integrity": "sha512-wmEc8m4fjnob4gt5riFRtTu/6+4rSe12TpAELNSqHMfF3IqnA+CH37USM6/YR3qRZv7e56kAEAtd6nKZaxe0Ug==",
+ "version": "6.7.1",
+ "resolved": "https://registry.npmjs.org/table/-/table-6.7.1.tgz",
+ "integrity": "sha512-ZGum47Yi6KOOFDE8m223td53ath2enHcYLgOCjGr5ngu8bdIARQk6mN/wRMv4yMRcHnCSnHbCEha4sobQx5yWg==",
"dev": true,
"requires": {
- "ajv": "^6.10.2",
- "lodash": "^4.17.14",
- "slice-ansi": "^2.1.0",
- "string-width": "^3.0.0"
+ "ajv": "^8.0.1",
+ "lodash.clonedeep": "^4.5.0",
+ "lodash.truncate": "^4.4.2",
+ "slice-ansi": "^4.0.0",
+ "string-width": "^4.2.0",
+ "strip-ansi": "^6.0.0"
+ },
+ "dependencies": {
+ "ajv": {
+ "version": "8.6.0",
+ "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.6.0.tgz",
+ "integrity": "sha512-cnUG4NSBiM4YFBxgZIj/In3/6KX+rQ2l2YPRVcvAMQGWEPKuXoPIhxzwqh31jA3IPbI4qEOp/5ILI4ynioXsGQ==",
+ "dev": true,
+ "requires": {
+ "fast-deep-equal": "^3.1.1",
+ "json-schema-traverse": "^1.0.0",
+ "require-from-string": "^2.0.2",
+ "uri-js": "^4.2.2"
+ }
+ },
+ "json-schema-traverse": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
+ "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==",
+ "dev": true
+ }
}
},
"taffydb": {
@@ -1677,9 +1688,9 @@
}
},
"type-fest": {
- "version": "0.8.1",
- "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz",
- "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==",
+ "version": "0.20.2",
+ "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz",
+ "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==",
"dev": true
},
"uc.micro": {
@@ -1688,25 +1699,37 @@
"integrity": "sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA==",
"dev": true
},
+ "unbox-primitive": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.1.tgz",
+ "integrity": "sha512-tZU/3NqK3dA5gpE1KtyiJUrEB0lxnGkMFHptJ7q6ewdZ8s12QrODwNbhIJStmJkd1QDXa1NRA8aF2A1zk/Ypyw==",
+ "dev": true,
+ "requires": {
+ "function-bind": "^1.1.1",
+ "has-bigints": "^1.0.1",
+ "has-symbols": "^1.0.2",
+ "which-boxed-primitive": "^1.0.2"
+ }
+ },
"underscore": {
- "version": "1.10.2",
- "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.10.2.tgz",
- "integrity": "sha512-N4P+Q/BuyuEKFJ43B9gYuOj4TQUHXX+j2FqguVOpjkssLUUrnJofCcBccJSCoeturDoZU6GorDTHSvUDlSQbTg==",
+ "version": "1.13.1",
+ "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.13.1.tgz",
+ "integrity": "sha512-hzSoAVtJF+3ZtiFX0VgfFPHEDRm7Y/QPjGyNo4TVdnDTdft3tr8hEkD25a1jC+TjTuE7tkHGKkhwCgs9dgBB2g==",
"dev": true
},
"uri-js": {
- "version": "4.4.0",
- "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.0.tgz",
- "integrity": "sha512-B0yRTzYdUCCn9n+F4+Gh4yIDtMQcaJsmYBDsTSG8g/OejKBodLQ2IHfN3bM7jUsRXndopT7OIXWdYqc1fjmV6g==",
+ "version": "4.4.1",
+ "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz",
+ "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==",
"dev": true,
"requires": {
"punycode": "^2.1.0"
}
},
"v8-compile-cache": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.1.1.tgz",
- "integrity": "sha512-8OQ9CL+VWyt3JStj7HX7/ciTL2V3Rl1Wf5OL+SNTm0yK1KvtReVulksyeRnCANHHuUxHlQig+JJDlUhBt1NQDQ==",
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz",
+ "integrity": "sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==",
"dev": true
},
"validate-npm-package-license": {
@@ -1728,6 +1751,19 @@
"isexe": "^2.0.0"
}
},
+ "which-boxed-primitive": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz",
+ "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==",
+ "dev": true,
+ "requires": {
+ "is-bigint": "^1.0.1",
+ "is-boolean-object": "^1.1.0",
+ "is-number-object": "^1.0.4",
+ "is-string": "^1.0.5",
+ "is-symbol": "^1.0.3"
+ }
+ },
"word-wrap": {
"version": "1.2.3",
"resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz",
@@ -1740,20 +1776,17 @@
"integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=",
"dev": true
},
- "write": {
- "version": "1.0.3",
- "resolved": "https://registry.npmjs.org/write/-/write-1.0.3.tgz",
- "integrity": "sha512-/lg70HAjtkUgWPVZhZcm+T4hkL8Zbtp1nFNOn3lRrxnlv50SRBv7cR7RqR+GMsd3hUXy9hWBo4CHTbFTcOYwig==",
- "dev": true,
- "requires": {
- "mkdirp": "^0.5.1"
- }
- },
"xmlcreate": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/xmlcreate/-/xmlcreate-2.0.3.tgz",
"integrity": "sha512-HgS+X6zAztGa9zIK3Y3LXuJes33Lz9x+YyTxgrkIdabu2vqcGOWwdfCpf1hWLRrd553wd4QCDf6BBO6FfdsRiQ==",
"dev": true
+ },
+ "yallist": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
+ "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
+ "dev": true
}
}
}
diff --git a/platform/javascript/package.json b/platform/javascript/package.json
index d9d272923e..9dafae30c5 100644
--- a/platform/javascript/package.json
+++ b/platform/javascript/package.json
@@ -20,9 +20,9 @@
"author": "Godot Engine contributors",
"license": "MIT",
"devDependencies": {
- "eslint": "^7.9.0",
- "eslint-config-airbnb-base": "^14.2.0",
- "eslint-plugin-import": "^2.22.0",
- "jsdoc": "^3.6.6"
+ "eslint": "^7.28.0",
+ "eslint-config-airbnb-base": "^14.2.1",
+ "eslint-plugin-import": "^2.23.4",
+ "jsdoc": "^3.6.7"
}
}
diff --git a/platform/linuxbsd/SCsub b/platform/linuxbsd/SCsub
index 46714e9502..8aebd57fd2 100644
--- a/platform/linuxbsd/SCsub
+++ b/platform/linuxbsd/SCsub
@@ -5,21 +5,28 @@ Import("env")
from platform_methods import run_in_subprocess
import platform_linuxbsd_builders
-common_x11 = [
+common_linuxbsd = [
"crash_handler_linuxbsd.cpp",
"os_linuxbsd.cpp",
"joypad_linux.cpp",
- "context_gl_x11.cpp",
- "detect_prime_x11.cpp",
- "display_server_x11.cpp",
- "vulkan_context_x11.cpp",
- "key_mapping_x11.cpp",
+ "freedesktop_screensaver.cpp",
]
+if "x11" in env and env["x11"]:
+ common_linuxbsd += [
+ "context_gl_x11.cpp",
+ "detect_prime_x11.cpp",
+ "display_server_x11.cpp",
+ "key_mapping_x11.cpp",
+ ]
+
+if "vulkan" in env and env["vulkan"]:
+ common_linuxbsd.append("vulkan_context_x11.cpp")
+
if "udev" in env and env["udev"]:
- common_x11.append("libudev-so_wrap.c")
+ common_linuxbsd.append("libudev-so_wrap.c")
-prog = env.add_program("#bin/godot", ["godot_linuxbsd.cpp"] + common_x11)
+prog = env.add_program("#bin/godot", ["godot_linuxbsd.cpp"] + common_linuxbsd)
if env["debug_symbols"] and env["separate_debug_symbols"]:
env.AddPostAction(prog, run_in_subprocess(platform_linuxbsd_builders.make_debug_linuxbsd))
diff --git a/platform/linuxbsd/detect.py b/platform/linuxbsd/detect.py
index 8f1afe0e66..eba672ddcb 100644
--- a/platform/linuxbsd/detect.py
+++ b/platform/linuxbsd/detect.py
@@ -67,11 +67,14 @@ def get_opts():
BoolVariable("use_static_cpp", "Link libgcc and libstdc++ statically for better portability", True),
BoolVariable("use_coverage", "Test Godot coverage", False),
BoolVariable("use_ubsan", "Use LLVM/GCC compiler undefined behavior sanitizer (UBSAN)", False),
- BoolVariable("use_asan", "Use LLVM/GCC compiler address sanitizer (ASAN))", False),
- BoolVariable("use_lsan", "Use LLVM/GCC compiler leak sanitizer (LSAN))", False),
- BoolVariable("use_tsan", "Use LLVM/GCC compiler thread sanitizer (TSAN))", False),
+ BoolVariable("use_asan", "Use LLVM/GCC compiler address sanitizer (ASAN)", False),
+ BoolVariable("use_lsan", "Use LLVM/GCC compiler leak sanitizer (LSAN)", False),
+ BoolVariable("use_tsan", "Use LLVM/GCC compiler thread sanitizer (TSAN)", False),
+ BoolVariable("use_msan", "Use LLVM compiler memory sanitizer (MSAN)", False),
BoolVariable("pulseaudio", "Detect and use PulseAudio", True),
+ BoolVariable("dbus", "Detect and use D-Bus to handle screensaver", True),
BoolVariable("udev", "Use udev for gamepad connection callbacks", True),
+ BoolVariable("x11", "Enable X11 display", True),
BoolVariable("debug_symbols", "Add debugging symbols to release/release_debug builds", True),
BoolVariable("separate_debug_symbols", "Create a separate file containing debugging symbols", False),
BoolVariable("touch", "Enable touch events", True),
@@ -89,7 +92,7 @@ def configure(env):
if env["target"] == "release":
if env["optimize"] == "speed": # optimize for speed (default)
env.Prepend(CCFLAGS=["-O3"])
- else: # optimize for size
+ elif env["optimize"] == "size": # optimize for size
env.Prepend(CCFLAGS=["-Os"])
if env["debug_symbols"]:
@@ -98,7 +101,7 @@ def configure(env):
elif env["target"] == "release_debug":
if env["optimize"] == "speed": # optimize for speed (default)
env.Prepend(CCFLAGS=["-O2"])
- else: # optimize for size
+ elif env["optimize"] == "size": # optimize for size
env.Prepend(CCFLAGS=["-Os"])
env.Prepend(CPPDEFINES=["DEBUG_ENABLED"])
@@ -142,15 +145,27 @@ def configure(env):
env.Append(CCFLAGS=["-ftest-coverage", "-fprofile-arcs"])
env.Append(LINKFLAGS=["-ftest-coverage", "-fprofile-arcs"])
- if env["use_ubsan"] or env["use_asan"] or env["use_lsan"] or env["use_tsan"]:
+ if env["use_ubsan"] or env["use_asan"] or env["use_lsan"] or env["use_tsan"] or env["use_msan"]:
env.extra_suffix += "s"
if env["use_ubsan"]:
- env.Append(CCFLAGS=["-fsanitize=undefined"])
+ env.Append(
+ CCFLAGS=[
+ "-fsanitize=undefined,shift,shift-exponent,integer-divide-by-zero,unreachable,vla-bound,null,return,signed-integer-overflow,bounds,float-divide-by-zero,float-cast-overflow,nonnull-attribute,returns-nonnull-attribute,bool,enum,vptr,pointer-overflow,builtin"
+ ]
+ )
env.Append(LINKFLAGS=["-fsanitize=undefined"])
+ if env["use_llvm"]:
+ env.Append(
+ CCFLAGS=[
+ "-fsanitize=nullability-return,nullability-arg,function,nullability-assign,implicit-integer-sign-change"
+ ]
+ )
+ else:
+ env.Append(CCFLAGS=["-fsanitize=bounds-strict"])
if env["use_asan"]:
- env.Append(CCFLAGS=["-fsanitize=address"])
+ env.Append(CCFLAGS=["-fsanitize=address,pointer-subtract,pointer-compare"])
env.Append(LINKFLAGS=["-fsanitize=address"])
if env["use_lsan"]:
@@ -161,6 +176,12 @@ def configure(env):
env.Append(CCFLAGS=["-fsanitize=thread"])
env.Append(LINKFLAGS=["-fsanitize=thread"])
+ if env["use_msan"] and env["use_llvm"]:
+ env.Append(CCFLAGS=["-fsanitize=memory"])
+ env.Append(CCFLAGS=["-fsanitize-memory-track-origins"])
+ env.Append(CCFLAGS=["-fsanitize-recover=memory"])
+ env.Append(LINKFLAGS=["-fsanitize=memory"])
+
if env["use_lto"]:
if not env["use_llvm"] and env.GetOption("num_jobs") > 1:
env.Append(CCFLAGS=["-flto"])
@@ -306,6 +327,10 @@ def configure(env):
if not env["builtin_pcre2"]:
env.ParseConfig("pkg-config libpcre2-32 --cflags --libs")
+ if not env["builtin_embree"]:
+ # No pkgconfig file so far, hardcode expected lib name.
+ env.Append(LIBS=["embree3"])
+
## Flags
if os.system("pkg-config --exists alsa") == 0: # 0 means found
@@ -323,6 +348,14 @@ def configure(env):
else:
print("PulseAudio development libraries not found, disabling driver")
+ if env["dbus"]:
+ if os.system("pkg-config --exists dbus-1") == 0: # 0 means found
+ print("Enabling D-Bus")
+ env.Append(CPPDEFINES=["DBUS_ENABLED"])
+ env.ParseConfig("pkg-config --cflags --libs dbus-1")
+ else:
+ print("D-Bus development libraries not found, disabling dependent features")
+
if platform.system() == "Linux":
env.Append(CPPDEFINES=["JOYDEV_ENABLED"])
if env["udev"]:
@@ -339,17 +372,26 @@ def configure(env):
env.ParseConfig("pkg-config zlib --cflags --libs")
env.Prepend(CPPPATH=["#platform/linuxbsd"])
- env.Append(CPPDEFINES=["X11_ENABLED", "UNIX_ENABLED"])
- env.Append(CPPDEFINES=["VULKAN_ENABLED"])
- if not env["builtin_vulkan"]:
- env.ParseConfig("pkg-config vulkan --cflags --libs")
- if not env["builtin_glslang"]:
- # No pkgconfig file for glslang so far
- env.Append(LIBS=["glslang", "SPIRV"])
+ if env["x11"]:
+ if not env["vulkan"]:
+ print("Error: X11 support requires vulkan=yes")
+ env.Exit(255)
+ env.Append(CPPDEFINES=["X11_ENABLED"])
+
+ env.Append(CPPDEFINES=["UNIX_ENABLED"])
+ env.Append(CPPDEFINES=[("_FILE_OFFSET_BITS", 64)])
+
+ if env["vulkan"]:
+ env.Append(CPPDEFINES=["VULKAN_ENABLED"])
+ if not env["builtin_vulkan"]:
+ env.ParseConfig("pkg-config vulkan --cflags --libs")
+ if not env["builtin_glslang"]:
+ # No pkgconfig file for glslang so far
+ env.Append(LIBS=["glslang", "SPIRV"])
- # env.Append(CPPDEFINES=['OPENGL_ENABLED'])
- env.Append(LIBS=["GL"])
+ # env.Append(CPPDEFINES=['OPENGL_ENABLED'])
+ env.Append(LIBS=["GL"])
env.Append(LIBS=["pthread"])
@@ -389,10 +431,7 @@ def configure(env):
# Link those statically for portability
if env["use_static_cpp"]:
- # Workaround for GH-31743, Ubuntu 18.04 i386 crashes when it's used.
- # That doesn't make any sense but it's likely a Ubuntu bug?
- if is64 or env["bits"] == "64":
- env.Append(LINKFLAGS=["-static-libgcc", "-static-libstdc++"])
+ env.Append(LINKFLAGS=["-static-libgcc", "-static-libstdc++"])
if env["use_llvm"]:
env["LINKCOM"] = env["LINKCOM"] + " -l:libatomic.a"
diff --git a/platform/linuxbsd/display_server_x11.cpp b/platform/linuxbsd/display_server_x11.cpp
index d7f7054acb..8f0742041c 100644
--- a/platform/linuxbsd/display_server_x11.cpp
+++ b/platform/linuxbsd/display_server_x11.cpp
@@ -121,6 +121,9 @@ bool DisplayServerX11::has_feature(Feature p_feature) const {
case FEATURE_ICON:
case FEATURE_NATIVE_ICON:
case FEATURE_SWAP_BUFFERS:
+#ifdef DBUS_ENABLED
+ case FEATURE_KEEP_SCREEN_ON:
+#endif
return true;
default: {
}
@@ -360,7 +363,7 @@ void DisplayServerX11::mouse_set_mode(MouseMode p_mode) {
return;
}
- if (mouse_mode == MOUSE_MODE_CAPTURED || mouse_mode == MOUSE_MODE_CONFINED) {
+ if (mouse_mode == MOUSE_MODE_CAPTURED || mouse_mode == MOUSE_MODE_CONFINED || mouse_mode == MOUSE_MODE_CONFINED_HIDDEN) {
XUngrabPointer(x11_display, CurrentTime);
}
@@ -376,7 +379,7 @@ void DisplayServerX11::mouse_set_mode(MouseMode p_mode) {
}
mouse_mode = p_mode;
- if (mouse_mode == MOUSE_MODE_CAPTURED || mouse_mode == MOUSE_MODE_CONFINED) {
+ if (mouse_mode == MOUSE_MODE_CAPTURED || mouse_mode == MOUSE_MODE_CONFINED || mouse_mode == MOUSE_MODE_CONFINED_HIDDEN) {
//flush pending motion events
_flush_mouse_motion();
WindowData &main_window = windows[MAIN_WINDOW_ID];
@@ -450,7 +453,7 @@ Point2i DisplayServerX11::mouse_get_absolute_position() const {
return Vector2i();
}
-int DisplayServerX11::mouse_get_button_state() const {
+MouseButton DisplayServerX11::mouse_get_button_state() const {
return last_button_state;
}
@@ -822,6 +825,26 @@ bool DisplayServerX11::screen_is_touchscreen(int p_screen) const {
return DisplayServer::screen_is_touchscreen(p_screen);
}
+#ifdef DBUS_ENABLED
+void DisplayServerX11::screen_set_keep_on(bool p_enable) {
+ if (screen_is_kept_on() == p_enable) {
+ return;
+ }
+
+ if (p_enable) {
+ screensaver->inhibit();
+ } else {
+ screensaver->uninhibit();
+ }
+
+ keep_screen_on = p_enable;
+}
+
+bool DisplayServerX11::screen_is_kept_on() const {
+ return keep_screen_on;
+}
+#endif
+
Vector<DisplayServer::WindowID> DisplayServerX11::get_window_list() const {
_THREAD_SAFE_METHOD_
@@ -832,10 +855,10 @@ Vector<DisplayServer::WindowID> DisplayServerX11::get_window_list() const {
return ret;
}
-DisplayServer::WindowID DisplayServerX11::create_sub_window(WindowMode p_mode, uint32_t p_flags, const Rect2i &p_rect) {
+DisplayServer::WindowID DisplayServerX11::create_sub_window(WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Rect2i &p_rect) {
_THREAD_SAFE_METHOD_
- WindowID id = _create_window(p_mode, p_flags, p_rect);
+ WindowID id = _create_window(p_mode, p_vsync_mode, p_flags, p_rect);
for (int i = 0; i < WINDOW_FLAG_MAX; i++) {
if (p_flags & (1 << i)) {
window_set_flag(WindowFlags(i), true, id);
@@ -1917,7 +1940,7 @@ void DisplayServerX11::cursor_set_custom_image(const RES &p_cursor, CursorShape
Rect2i atlas_rect;
if (texture.is_valid()) {
- image = texture->get_data();
+ image = texture->get_image();
}
if (!image.is_valid() && atlas_texture.is_valid()) {
@@ -1940,7 +1963,7 @@ void DisplayServerX11::cursor_set_custom_image(const RES &p_cursor, CursorShape
ERR_FAIL_COND(texture_size.width > 256 || texture_size.height > 256);
ERR_FAIL_COND(p_hotspot.x > texture_size.width || p_hotspot.y > texture_size.height);
- image = texture->get_data();
+ image = texture->get_image();
ERR_FAIL_COND(!image.is_valid());
@@ -2012,7 +2035,7 @@ int DisplayServerX11::keyboard_get_layout_count() const {
XkbGetNames(x11_display, XkbSymbolsNameMask, kbd);
const Atom *groups = kbd->names->groups;
- if (kbd->ctrls != NULL) {
+ if (kbd->ctrls != nullptr) {
_group_count = kbd->ctrls->num_groups;
} else {
while (_group_count < XkbNumKbdGroups && groups[_group_count] != None) {
@@ -2046,7 +2069,7 @@ String DisplayServerX11::keyboard_get_layout_language(int p_index) const {
int _group_count = 0;
const Atom *groups = kbd->names->groups;
- if (kbd->ctrls != NULL) {
+ if (kbd->ctrls != nullptr) {
_group_count = kbd->ctrls->num_groups;
} else {
while (_group_count < XkbNumKbdGroups && groups[_group_count] != None) {
@@ -2085,7 +2108,7 @@ String DisplayServerX11::keyboard_get_layout_name(int p_index) const {
int _group_count = 0;
const Atom *groups = kbd->names->groups;
- if (kbd->ctrls != NULL) {
+ if (kbd->ctrls != nullptr) {
_group_count = kbd->ctrls->num_groups;
} else {
while (_group_count < XkbNumKbdGroups && groups[_group_count] != None) {
@@ -2166,19 +2189,19 @@ static Atom pick_target_from_atoms(Display *p_disp, Atom p_t1, Atom p_t2, Atom p
}
void DisplayServerX11::_get_key_modifier_state(unsigned int p_x11_state, Ref<InputEventWithModifiers> state) {
- state->set_shift((p_x11_state & ShiftMask));
- state->set_control((p_x11_state & ControlMask));
- state->set_alt((p_x11_state & Mod1Mask /*|| p_x11_state&Mod5Mask*/)); //altgr should not count as alt
- state->set_metakey((p_x11_state & Mod4Mask));
+ state->set_shift_pressed((p_x11_state & ShiftMask));
+ state->set_ctrl_pressed((p_x11_state & ControlMask));
+ state->set_alt_pressed((p_x11_state & Mod1Mask /*|| p_x11_state&Mod5Mask*/)); //altgr should not count as alt
+ state->set_meta_pressed((p_x11_state & Mod4Mask));
}
-unsigned int DisplayServerX11::_get_mouse_button_state(unsigned int p_x11_button, int p_x11_type) {
- unsigned int mask = 1 << (p_x11_button - 1);
+MouseButton DisplayServerX11::_get_mouse_button_state(MouseButton p_x11_button, int p_x11_type) {
+ MouseButton mask = MouseButton(1 << (p_x11_button - 1));
if (p_x11_type == ButtonPress) {
last_button_state |= mask;
} else {
- last_button_state &= ~mask;
+ last_button_state &= MouseButton(~mask);
}
return last_button_state;
@@ -2255,7 +2278,7 @@ void DisplayServerX11::_handle_key_event(WindowID p_window, XKeyEvent *p_event,
tmp.parse_utf8(utf8string, utf8bytes);
for (int i = 0; i < tmp.length(); i++) {
Ref<InputEventKey> k;
- k.instance();
+ k.instantiate();
if (physical_keycode == 0 && keycode == 0 && tmp[i] == 0) {
continue;
}
@@ -2281,7 +2304,7 @@ void DisplayServerX11::_handle_key_event(WindowID p_window, XKeyEvent *p_event,
//make it consistent across platforms.
k->set_keycode(KEY_TAB);
k->set_physical_keycode(KEY_TAB);
- k->set_shift(true);
+ k->set_shift_pressed(true);
}
Input::get_singleton()->accumulate_input_event(k);
@@ -2346,7 +2369,7 @@ void DisplayServerX11::_handle_key_event(WindowID p_window, XKeyEvent *p_event,
//print_verbose("mod1: "+itos(xkeyevent->state&Mod1Mask)+" mod 5: "+itos(xkeyevent->state&Mod5Mask));
Ref<InputEventKey> k;
- k.instance();
+ k.instantiate();
k->set_window_id(p_window);
_get_key_modifier_state(xkeyevent->state, k);
@@ -2409,20 +2432,20 @@ void DisplayServerX11::_handle_key_event(WindowID p_window, XKeyEvent *p_event,
//make it consistent across platforms.
k->set_keycode(KEY_TAB);
k->set_physical_keycode(KEY_TAB);
- k->set_shift(true);
+ k->set_shift_pressed(true);
}
//don't set mod state if modifier keys are released by themselves
//else event.is_action() will not work correctly here
if (!k->is_pressed()) {
if (k->get_keycode() == KEY_SHIFT) {
- k->set_shift(false);
- } else if (k->get_keycode() == KEY_CONTROL) {
- k->set_control(false);
+ k->set_shift_pressed(false);
+ } else if (k->get_keycode() == KEY_CTRL) {
+ k->set_ctrl_pressed(false);
} else if (k->get_keycode() == KEY_ALT) {
- k->set_alt(false);
+ k->set_alt_pressed(false);
} else if (k->get_keycode() == KEY_META) {
- k->set_metakey(false);
+ k->set_meta_pressed(false);
}
}
@@ -2606,7 +2629,6 @@ void DisplayServerX11::_window_changed(XEvent *event) {
}
#endif
- print_line("DisplayServer::_window_changed: " + itos(window_id) + " rect: " + new_rect);
if (!wd.rect_changed_callback.is_null()) {
Rect2i r = new_rect;
@@ -2684,7 +2706,7 @@ bool DisplayServerX11::_wait_for_events() const {
tv.tv_sec = 1;
// Wait for next event or timeout.
- int num_ready_fds = select(x11_fd + 1, &in_fds, NULL, NULL, &tv);
+ int num_ready_fds = select(x11_fd + 1, &in_fds, nullptr, nullptr, &tv);
if (num_ready_fds > 0) {
// Event received.
@@ -2767,7 +2789,7 @@ void DisplayServerX11::process_events() {
do_mouse_warp = false;
// Is the current mouse mode one where it needs to be grabbed.
- bool mouse_mode_grab = mouse_mode == MOUSE_MODE_CAPTURED || mouse_mode == MOUSE_MODE_CONFINED;
+ bool mouse_mode_grab = mouse_mode == MOUSE_MODE_CAPTURED || mouse_mode == MOUSE_MODE_CONFINED || mouse_mode == MOUSE_MODE_CONFINED_HIDDEN;
xi.pressure = 0;
xi.tilt = Vector2();
@@ -2905,7 +2927,7 @@ void DisplayServerX11::process_events() {
bool is_begin = event_data->evtype == XI_TouchBegin;
Ref<InputEventScreenTouch> st;
- st.instance();
+ st.instantiate();
st->set_window_id(window_id);
st->set_index(index);
st->set_position(pos);
@@ -2939,7 +2961,7 @@ void DisplayServerX11::process_events() {
if (curr_pos_elem->value() != pos) {
Ref<InputEventScreenDrag> sd;
- sd.instance();
+ sd.instantiate();
sd->set_window_id(window_id);
sd->set_index(index);
sd->set_position(pos);
@@ -3031,7 +3053,7 @@ void DisplayServerX11::process_events() {
for (Map<WindowID, WindowData>::Element *E = windows.front(); E; E = E->next()) {
if (mouse_mode == MOUSE_MODE_CONFINED) {
XUndefineCursor(x11_display, E->get().x11_window);
- } else if (mouse_mode == MOUSE_MODE_CAPTURED) { // or re-hide it in captured mode
+ } else if (mouse_mode == MOUSE_MODE_CAPTURED || mouse_mode == MOUSE_MODE_CONFINED_HIDDEN) { // Or re-hide it.
XDefineCursor(x11_display, E->get().x11_window, null_cursor);
}
@@ -3092,7 +3114,7 @@ void DisplayServerX11::process_events() {
// Release every pointer to avoid sticky points
for (Map<int, Vector2>::Element *E = xi.state.front(); E; E = E->next()) {
Ref<InputEventScreenTouch> st;
- st.instance();
+ st.instantiate();
st->set_index(E->key());
st->set_window_id(window_id);
st->set_position(E->get());
@@ -3127,15 +3149,15 @@ void DisplayServerX11::process_events() {
}
Ref<InputEventMouseButton> mb;
- mb.instance();
+ mb.instantiate();
mb->set_window_id(window_id);
_get_key_modifier_state(event.xbutton.state, mb);
- mb->set_button_index(event.xbutton.button);
- if (mb->get_button_index() == 2) {
- mb->set_button_index(3);
- } else if (mb->get_button_index() == 3) {
- mb->set_button_index(2);
+ mb->set_button_index((MouseButton)event.xbutton.button);
+ if (mb->get_button_index() == MOUSE_BUTTON_RIGHT) {
+ mb->set_button_index(MOUSE_BUTTON_MIDDLE);
+ } else if (mb->get_button_index() == MOUSE_BUTTON_MIDDLE) {
+ mb->set_button_index(MOUSE_BUTTON_RIGHT);
}
mb->set_button_mask(_get_mouse_button_state(mb->get_button_index(), event.xbutton.type));
mb->set_position(Vector2(event.xbutton.x, event.xbutton.y));
@@ -3162,14 +3184,14 @@ void DisplayServerX11::process_events() {
last_click_ms = 0;
last_click_pos = Point2i(-100, -100);
last_click_button_index = -1;
- mb->set_doubleclick(true);
+ mb->set_double_click(true);
}
} else if (mb->get_button_index() < 4 || mb->get_button_index() > 7) {
last_click_button_index = mb->get_button_index();
}
- if (!mb->is_doubleclick()) {
+ if (!mb->is_double_click()) {
last_click_ms += diff;
last_click_pos = Point2i(event.xbutton.x, event.xbutton.y);
}
@@ -3292,13 +3314,13 @@ void DisplayServerX11::process_events() {
}
Ref<InputEventMouseMotion> mm;
- mm.instance();
+ mm.instantiate();
mm->set_window_id(window_id);
if (xi.pressure_supported) {
mm->set_pressure(xi.pressure);
} else {
- mm->set_pressure((mouse_get_button_state() & (1 << (BUTTON_LEFT - 1))) ? 1.0f : 0.0f);
+ mm->set_pressure((mouse_get_button_state() & MOUSE_BUTTON_MASK_LEFT) ? 1.0f : 0.0f);
}
mm->set_tilt(xi.tilt);
@@ -3619,6 +3641,22 @@ void DisplayServerX11::set_icon(const Ref<Image> &p_icon) {
XSetErrorHandler(oldHandler);
}
+void DisplayServerX11::window_set_vsync_mode(DisplayServer::VSyncMode p_vsync_mode, WindowID p_window) {
+ _THREAD_SAFE_METHOD_
+#if defined(VULKAN_ENABLED)
+ context_vulkan->set_vsync_mode(p_window, p_vsync_mode);
+#endif
+}
+
+DisplayServer::VSyncMode DisplayServerX11::window_get_vsync_mode(WindowID p_window) const {
+ _THREAD_SAFE_METHOD_
+#if defined(VULKAN_ENABLED)
+ return context_vulkan->get_vsync_mode(p_window);
+#else
+ return DisplayServer::VSYNC_ENABLED;
+#endif
+}
+
Vector<String> DisplayServerX11::get_rendering_drivers_func() {
Vector<String> drivers;
@@ -3632,8 +3670,8 @@ Vector<String> DisplayServerX11::get_rendering_drivers_func() {
return drivers;
}
-DisplayServer *DisplayServerX11::create_func(const String &p_rendering_driver, WindowMode p_mode, uint32_t p_flags, const Vector2i &p_resolution, Error &r_error) {
- DisplayServer *ds = memnew(DisplayServerX11(p_rendering_driver, p_mode, p_flags, p_resolution, r_error));
+DisplayServer *DisplayServerX11::create_func(const String &p_rendering_driver, WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i &p_resolution, Error &r_error) {
+ DisplayServer *ds = memnew(DisplayServerX11(p_rendering_driver, p_mode, p_vsync_mode, p_flags, p_resolution, r_error));
if (r_error != OK) {
ds->alert("Your video card driver does not support any of the supported Vulkan versions.\n"
"Please update your drivers or if you have a very old or integrated GPU upgrade it.",
@@ -3642,7 +3680,7 @@ DisplayServer *DisplayServerX11::create_func(const String &p_rendering_driver, W
return ds;
}
-DisplayServerX11::WindowID DisplayServerX11::_create_window(WindowMode p_mode, uint32_t p_flags, const Rect2i &p_rect) {
+DisplayServerX11::WindowID DisplayServerX11::_create_window(WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Rect2i &p_rect) {
//Create window
long visualMask = VisualScreenMask;
@@ -3806,7 +3844,7 @@ DisplayServerX11::WindowID DisplayServerX11::_create_window(WindowMode p_mode, u
#if defined(VULKAN_ENABLED)
if (context_vulkan) {
- Error err = context_vulkan->window_create(id, wd.x11_window, x11_display, p_rect.size.width, p_rect.size.height);
+ Error err = context_vulkan->window_create(id, p_vsync_mode, wd.x11_window, x11_display, p_rect.size.width, p_rect.size.height);
ERR_FAIL_COND_V_MSG(err != OK, INVALID_WINDOW_ID, "Can't create a Vulkan window");
}
#endif
@@ -3833,8 +3871,6 @@ DisplayServerX11::WindowID DisplayServerX11::_create_window(WindowMode p_mode, u
wd.position.y = xwa.y;
wd.size.width = xwa.width;
wd.size.height = xwa.height;
-
- print_line("DisplayServer::_create_window " + itos(id) + " want rect: " + p_rect + " got rect " + Rect2i(xwa.x, xwa.y, xwa.width, xwa.height));
}
//set cursor
@@ -3845,7 +3881,7 @@ DisplayServerX11::WindowID DisplayServerX11::_create_window(WindowMode p_mode, u
return id;
}
-DisplayServerX11::DisplayServerX11(const String &p_rendering_driver, WindowMode p_mode, uint32_t p_flags, const Vector2i &p_resolution, Error &r_error) {
+DisplayServerX11::DisplayServerX11(const String &p_rendering_driver, WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i &p_resolution, Error &r_error) {
Input::get_singleton()->set_event_dispatch_function(_dispatch_input_events);
r_error = OK;
@@ -3858,8 +3894,6 @@ DisplayServerX11::DisplayServerX11(const String &p_rendering_driver, WindowMode
img[i] = nullptr;
}
- last_button_state = 0;
-
xmbstring = nullptr;
last_click_ms = 0;
@@ -4030,7 +4064,10 @@ DisplayServerX11::DisplayServerX11(const String &p_rendering_driver, WindowMode
use_prime = 0;
}
- if (getenv("LD_LIBRARY_PATH")) {
+ // Some tools use fake libGL libraries and have them override the real one using
+ // LD_LIBRARY_PATH, so we skip them. *But* Steam also sets LD_LIBRARY_PATH for its
+ // runtime and includes system `/lib` and `/lib64`... so ignore Steam.
+ if (use_prime == -1 && getenv("LD_LIBRARY_PATH") && !getenv("STEAM_RUNTIME_LIBRARY_PATH")) {
String ld_library_path(getenv("LD_LIBRARY_PATH"));
Vector<String> libraries = ld_library_path.split(":");
@@ -4080,7 +4117,7 @@ DisplayServerX11::DisplayServerX11(const String &p_rendering_driver, WindowMode
Point2i window_position(
(screen_get_size(0).width - p_resolution.width) / 2,
(screen_get_size(0).height - p_resolution.height) / 2);
- WindowID main_window = _create_window(p_mode, p_flags, Rect2i(window_position, p_resolution));
+ WindowID main_window = _create_window(p_mode, p_vsync_mode, p_flags, Rect2i(window_position, p_resolution));
if (main_window == INVALID_WINDOW_ID) {
r_error = ERR_CANT_CREATE;
return;
@@ -4272,6 +4309,11 @@ DisplayServerX11::DisplayServerX11(const String &p_rendering_driver, WindowMode
_update_real_mouse_position(windows[MAIN_WINDOW_ID]);
+#ifdef DBUS_ENABLED
+ screensaver = memnew(FreeDesktopScreenSaver);
+ screen_set_keep_on(GLOBAL_DEF("display/window/energy_saving/keep_screen_on", true));
+#endif
+
r_error = OK;
}
@@ -4336,6 +4378,10 @@ DisplayServerX11::~DisplayServerX11() {
if (xmbstring) {
memfree(xmbstring);
}
+
+#ifdef DBUS_ENABLED
+ memdelete(screensaver);
+#endif
}
void DisplayServerX11::register_x11_driver() {
diff --git a/platform/linuxbsd/display_server_x11.h b/platform/linuxbsd/display_server_x11.h
index 10686d8424..c5cf5ee4cb 100644
--- a/platform/linuxbsd/display_server_x11.h
+++ b/platform/linuxbsd/display_server_x11.h
@@ -55,6 +55,10 @@
#include "platform/linuxbsd/vulkan_context_x11.h"
#endif
+#if defined(DBUS_ENABLED)
+#include "freedesktop_screensaver.h"
+#endif
+
#include <X11/Xcursor/Xcursor.h>
#include <X11/Xlib.h>
#include <X11/extensions/XInput2.h>
@@ -103,6 +107,11 @@ class DisplayServerX11 : public DisplayServer {
RenderingDeviceVulkan *rendering_device_vulkan;
#endif
+#if defined(DBUS_ENABLED)
+ FreeDesktopScreenSaver *screensaver;
+ bool keep_screen_on = false;
+#endif
+
struct WindowData {
Window x11_window;
::XIC xic;
@@ -143,7 +152,7 @@ class DisplayServerX11 : public DisplayServer {
Map<WindowID, WindowData> windows;
WindowID window_id_counter = MAIN_WINDOW_ID;
- WindowID _create_window(WindowMode p_mode, uint32_t p_flags, const Rect2i &p_rect);
+ WindowID _create_window(WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Rect2i &p_rect);
String internal_clipboard;
Window xdnd_source_window;
@@ -162,7 +171,7 @@ class DisplayServerX11 : public DisplayServer {
Point2i last_click_pos;
uint64_t last_click_ms;
int last_click_button_index;
- uint32_t last_button_state;
+ MouseButton last_button_state = MOUSE_BUTTON_NONE;
bool app_focused = false;
uint64_t time_since_no_focus = 0;
@@ -187,7 +196,7 @@ class DisplayServerX11 : public DisplayServer {
bool _refresh_device_info();
- unsigned int _get_mouse_button_state(unsigned int p_x11_button, int p_x11_type);
+ MouseButton _get_mouse_button_state(MouseButton p_x11_button, int p_x11_type);
void _get_key_modifier_state(unsigned int p_x11_state, Ref<InputEventWithModifiers> state);
void _flush_mouse_motion();
@@ -279,7 +288,7 @@ public:
virtual void mouse_warp_to_position(const Point2i &p_to);
virtual Point2i mouse_get_position() const;
virtual Point2i mouse_get_absolute_position() const;
- virtual int mouse_get_button_state() const;
+ virtual MouseButton mouse_get_button_state() const;
virtual void clipboard_set(const String &p_text);
virtual String clipboard_get() const;
@@ -291,9 +300,14 @@ public:
virtual int screen_get_dpi(int p_screen = SCREEN_OF_MAIN_WINDOW) const;
virtual bool screen_is_touchscreen(int p_screen = SCREEN_OF_MAIN_WINDOW) const;
+#if defined(DBUS_ENABLED)
+ virtual void screen_set_keep_on(bool p_enable);
+ virtual bool screen_is_kept_on() const;
+#endif
+
virtual Vector<DisplayServer::WindowID> get_window_list() const;
- virtual WindowID create_sub_window(WindowMode p_mode, uint32_t p_flags, const Rect2i &p_rect = Rect2i());
+ virtual WindowID create_sub_window(WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Rect2i &p_rect = Rect2i());
virtual void show_window(WindowID p_id);
virtual void delete_sub_window(WindowID p_id);
@@ -348,6 +362,9 @@ public:
virtual void window_set_ime_active(const bool p_active, WindowID p_window = MAIN_WINDOW_ID);
virtual void window_set_ime_position(const Point2i &p_pos, WindowID p_window = MAIN_WINDOW_ID);
+ virtual void window_set_vsync_mode(DisplayServer::VSyncMode p_vsync_mode, WindowID p_window = MAIN_WINDOW_ID) override;
+ virtual DisplayServer::VSyncMode window_get_vsync_mode(WindowID p_vsync_mode) const override;
+
virtual void cursor_set_shape(CursorShape p_shape);
virtual CursorShape cursor_get_shape() const;
virtual void cursor_set_custom_image(const RES &p_cursor, CursorShape p_shape, const Vector2 &p_hotspot);
@@ -369,12 +386,12 @@ public:
virtual void set_native_icon(const String &p_filename);
virtual void set_icon(const Ref<Image> &p_icon);
- static DisplayServer *create_func(const String &p_rendering_driver, WindowMode p_mode, uint32_t p_flags, const Vector2i &p_resolution, Error &r_error);
+ static DisplayServer *create_func(const String &p_rendering_driver, WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i &p_resolution, Error &r_error);
static Vector<String> get_rendering_drivers_func();
static void register_x11_driver();
- DisplayServerX11(const String &p_rendering_driver, WindowMode p_mode, uint32_t p_flags, const Vector2i &p_resolution, Error &r_error);
+ DisplayServerX11(const String &p_rendering_driver, WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i &p_resolution, Error &r_error);
~DisplayServerX11();
};
diff --git a/platform/linuxbsd/export/export.cpp b/platform/linuxbsd/export/export.cpp
index cb95068314..5c6be2d7d4 100644
--- a/platform/linuxbsd/export/export.cpp
+++ b/platform/linuxbsd/export/export.cpp
@@ -30,7 +30,7 @@
#include "export.h"
-#include "core/os/file_access.h"
+#include "core/io/file_access.h"
#include "editor/editor_export.h"
#include "platform/linuxbsd/logo.gen.h"
#include "scene/resources/texture.h"
@@ -39,11 +39,11 @@ static Error fixup_embedded_pck(const String &p_path, int64_t p_embedded_start,
void register_linuxbsd_exporter() {
Ref<EditorExportPlatformPC> platform;
- platform.instance();
+ platform.instantiate();
Ref<Image> img = memnew(Image(_linuxbsd_logo));
Ref<ImageTexture> logo;
- logo.instance();
+ logo.instantiate();
logo->create_from_image(img);
platform->set_logo(logo);
platform->set_name("Linux/X11");
diff --git a/platform/linuxbsd/freedesktop_screensaver.cpp b/platform/linuxbsd/freedesktop_screensaver.cpp
new file mode 100644
index 0000000000..a6a3b27d76
--- /dev/null
+++ b/platform/linuxbsd/freedesktop_screensaver.cpp
@@ -0,0 +1,125 @@
+/*************************************************************************/
+/* freedesktop_screensaver.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 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 "freedesktop_screensaver.h"
+
+#ifdef DBUS_ENABLED
+
+#include "core/config/project_settings.h"
+
+#include <dbus/dbus.h>
+
+#define BUS_OBJECT_NAME "org.freedesktop.ScreenSaver"
+#define BUS_OBJECT_PATH "/org/freedesktop/ScreenSaver"
+#define BUS_INTERFACE "org.freedesktop.ScreenSaver"
+
+void FreeDesktopScreenSaver::inhibit() {
+ if (unsupported) {
+ return;
+ }
+
+ DBusError error;
+ dbus_error_init(&error);
+
+ DBusConnection *bus = dbus_bus_get(DBUS_BUS_SESSION, &error);
+ if (dbus_error_is_set(&error)) {
+ unsupported = true;
+ return;
+ }
+
+ String app_name_string = ProjectSettings::get_singleton()->get("application/config/name");
+ CharString app_name_utf8 = app_name_string.utf8();
+ const char *app_name = app_name_string.is_empty() ? "Godot Engine" : app_name_utf8.get_data();
+
+ const char *reason = "Running Godot Engine project";
+
+ DBusMessage *message = dbus_message_new_method_call(
+ BUS_OBJECT_NAME, BUS_OBJECT_PATH, BUS_INTERFACE,
+ "Inhibit");
+ dbus_message_append_args(
+ message,
+ DBUS_TYPE_STRING, &app_name,
+ DBUS_TYPE_STRING, &reason,
+ DBUS_TYPE_INVALID);
+
+ DBusMessage *reply = dbus_connection_send_with_reply_and_block(bus, message, 50, &error);
+ dbus_message_unref(message);
+ if (dbus_error_is_set(&error)) {
+ dbus_connection_unref(bus);
+ unsupported = false;
+ return;
+ }
+
+ DBusMessageIter reply_iter;
+ dbus_message_iter_init(reply, &reply_iter);
+ dbus_message_iter_get_basic(&reply_iter, &cookie);
+ print_verbose("FreeDesktopScreenSaver: Acquired screensaver inhibition cookie: " + uitos(cookie));
+
+ dbus_message_unref(reply);
+ dbus_connection_unref(bus);
+}
+
+void FreeDesktopScreenSaver::uninhibit() {
+ if (unsupported) {
+ return;
+ }
+
+ DBusError error;
+ dbus_error_init(&error);
+
+ DBusConnection *bus = dbus_bus_get(DBUS_BUS_SESSION, &error);
+ if (dbus_error_is_set(&error)) {
+ unsupported = true;
+ return;
+ }
+
+ DBusMessage *message = dbus_message_new_method_call(
+ BUS_OBJECT_NAME, BUS_OBJECT_PATH, BUS_INTERFACE,
+ "UnInhibit");
+ dbus_message_append_args(
+ message,
+ DBUS_TYPE_UINT32, &cookie,
+ DBUS_TYPE_INVALID);
+
+ DBusMessage *reply = dbus_connection_send_with_reply_and_block(bus, message, 50, &error);
+ if (dbus_error_is_set(&error)) {
+ dbus_connection_unref(bus);
+ unsupported = true;
+ return;
+ }
+
+ print_verbose("FreeDesktopScreenSaver: Released screensaver inhibition cookie: " + uitos(cookie));
+
+ dbus_message_unref(message);
+ dbus_message_unref(reply);
+ dbus_connection_unref(bus);
+}
+
+#endif // DBUS_ENABLED
diff --git a/platform/iphone/native_video_view.h b/platform/linuxbsd/freedesktop_screensaver.h
index 2df5e27fa4..f27e60fce4 100644
--- a/platform/iphone/native_video_view.h
+++ b/platform/linuxbsd/freedesktop_screensaver.h
@@ -1,5 +1,5 @@
/*************************************************************************/
-/* native_video_view.h */
+/* freedesktop_screensaver.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
@@ -28,14 +28,20 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#import <UIKit/UIKit.h>
+#ifdef DBUS_ENABLED
-@interface GodotNativeVideoView : UIView
+#include <dbus/dbus.h>
+#include <stdint.h>
-- (BOOL)playVideoAtPath:(NSString *)filePath volume:(float)videoVolume audio:(NSString *)audioTrack subtitle:(NSString *)subtitleTrack;
-- (BOOL)isVideoPlaying;
-- (void)pauseVideo;
-- (void)unpauseVideo;
-- (void)stopVideo;
+class FreeDesktopScreenSaver {
+private:
+ uint32_t cookie = 0;
+ bool unsupported = false;
-@end
+public:
+ FreeDesktopScreenSaver() {}
+ void inhibit();
+ void uninhibit();
+};
+
+#endif // DBUS_ENABLED
diff --git a/platform/linuxbsd/joypad_linux.cpp b/platform/linuxbsd/joypad_linux.cpp
index 471259e50f..8b6dbc4c20 100644
--- a/platform/linuxbsd/joypad_linux.cpp
+++ b/platform/linuxbsd/joypad_linux.cpp
@@ -62,7 +62,7 @@ void JoypadLinux::Joypad::reset() {
dpad = 0;
fd = -1;
- Input::JoyAxis jx;
+ Input::JoyAxisValue jx;
jx.min = -1;
jx.value = 0.0f;
for (int i = 0; i < MAX_ABS; i++) {
@@ -178,17 +178,18 @@ void JoypadLinux::monitor_joypads(udev *p_udev) {
select() ensured that this will not block. */
dev = udev_monitor_receive_device(mon);
- if (dev && udev_device_get_devnode(dev) != 0) {
+ if (dev && udev_device_get_devnode(dev) != nullptr) {
MutexLock lock(joy_mutex);
String action = udev_device_get_action(dev);
const char *devnode = udev_device_get_devnode(dev);
if (devnode) {
String devnode_str = devnode;
if (devnode_str.find(ignore_str) == -1) {
- if (action == "add")
+ if (action == "add") {
open_joypad(devnode);
- else if (String(action) == "remove")
+ } else if (String(action) == "remove") {
close_joypad(get_joy_from_path(devnode));
+ }
}
}
@@ -212,7 +213,7 @@ void JoypadLinux::monitor_joypads() {
struct dirent *current;
char fname[64];
- while ((current = readdir(input_directory)) != NULL) {
+ while ((current = readdir(input_directory)) != nullptr) {
if (strncmp(current->d_name, "event", 5) != 0) {
continue;
}
@@ -428,10 +429,10 @@ void JoypadLinux::joypad_vibration_stop(int p_id, uint64_t p_timestamp) {
joy.ff_effect_timestamp = p_timestamp;
}
-Input::JoyAxis JoypadLinux::axis_correct(const input_absinfo *p_abs, int p_value) const {
+Input::JoyAxisValue JoypadLinux::axis_correct(const input_absinfo *p_abs, int p_value) const {
int min = p_abs->minimum;
int max = p_abs->maximum;
- Input::JoyAxis jx;
+ Input::JoyAxisValue jx;
if (min < 0) {
jx.min = -1;
@@ -474,7 +475,7 @@ void JoypadLinux::process_joypads() {
switch (ev.type) {
case EV_KEY:
- input->joy_button(i, joy->key_map[ev.code], ev.value);
+ input->joy_button(i, (JoyButton)joy->key_map[ev.code], ev.value);
break;
case EV_ABS:
@@ -483,29 +484,29 @@ void JoypadLinux::process_joypads() {
case ABS_HAT0X:
if (ev.value != 0) {
if (ev.value < 0) {
- joy->dpad = (joy->dpad | Input::HAT_MASK_LEFT) & ~Input::HAT_MASK_RIGHT;
+ joy->dpad = (HatMask)((joy->dpad | HatMask::HAT_MASK_LEFT) & ~HatMask::HAT_MASK_RIGHT);
} else {
- joy->dpad = (joy->dpad | Input::HAT_MASK_RIGHT) & ~Input::HAT_MASK_LEFT;
+ joy->dpad = (HatMask)((joy->dpad | HatMask::HAT_MASK_RIGHT) & ~HatMask::HAT_MASK_LEFT);
}
} else {
- joy->dpad &= ~(Input::HAT_MASK_LEFT | Input::HAT_MASK_RIGHT);
+ joy->dpad &= ~(HatMask::HAT_MASK_LEFT | HatMask::HAT_MASK_RIGHT);
}
- input->joy_hat(i, joy->dpad);
+ input->joy_hat(i, (HatMask)joy->dpad);
break;
case ABS_HAT0Y:
if (ev.value != 0) {
if (ev.value < 0) {
- joy->dpad = (joy->dpad | Input::HAT_MASK_UP) & ~Input::HAT_MASK_DOWN;
+ joy->dpad = (HatMask)((joy->dpad | HatMask::HAT_MASK_UP) & ~HatMask::HAT_MASK_DOWN);
} else {
- joy->dpad = (joy->dpad | Input::HAT_MASK_DOWN) & ~Input::HAT_MASK_UP;
+ joy->dpad = (HatMask)((joy->dpad | HatMask::HAT_MASK_DOWN) & ~HatMask::HAT_MASK_UP);
}
} else {
- joy->dpad &= ~(Input::HAT_MASK_UP | Input::HAT_MASK_DOWN);
+ joy->dpad &= ~(HatMask::HAT_MASK_UP | HatMask::HAT_MASK_DOWN);
}
- input->joy_hat(i, joy->dpad);
+ input->joy_hat(i, (HatMask)joy->dpad);
break;
default:
@@ -513,7 +514,7 @@ void JoypadLinux::process_joypads() {
return;
}
if (joy->abs_map[ev.code] != -1 && joy->abs_info[ev.code]) {
- Input::JoyAxis value = axis_correct(joy->abs_info[ev.code], ev.value);
+ Input::JoyAxisValue value = axis_correct(joy->abs_info[ev.code], ev.value);
joy->curr_axis[joy->abs_map[ev.code]] = value;
}
break;
@@ -525,7 +526,7 @@ void JoypadLinux::process_joypads() {
for (int j = 0; j < MAX_ABS; j++) {
int index = joy->abs_map[j];
if (index != -1) {
- input->joy_axis(i, index, joy->curr_axis[index]);
+ input->joy_axis(i, (JoyAxis)index, joy->curr_axis[index]);
}
}
if (len == 0 || (len < 0 && errno != EAGAIN)) {
diff --git a/platform/linuxbsd/joypad_linux.h b/platform/linuxbsd/joypad_linux.h
index b0d0db047b..177d7a51ce 100644
--- a/platform/linuxbsd/joypad_linux.h
+++ b/platform/linuxbsd/joypad_linux.h
@@ -53,7 +53,7 @@ private:
};
struct Joypad {
- Input::JoyAxis curr_axis[MAX_ABS];
+ Input::JoyAxisValue curr_axis[MAX_ABS];
int key_map[MAX_KEY];
int abs_map[MAX_ABS];
int dpad = 0;
@@ -97,7 +97,7 @@ private:
void joypad_vibration_start(int p_id, float p_weak_magnitude, float p_strong_magnitude, float p_duration, uint64_t p_timestamp);
void joypad_vibration_stop(int p_id, uint64_t p_timestamp);
- Input::JoyAxis axis_correct(const input_absinfo *p_abs, int p_value) const;
+ Input::JoyAxisValue axis_correct(const input_absinfo *p_abs, int p_value) const;
};
#endif
diff --git a/platform/linuxbsd/key_mapping_x11.cpp b/platform/linuxbsd/key_mapping_x11.cpp
index f9f612fa74..74257a7e61 100644
--- a/platform/linuxbsd/key_mapping_x11.cpp
+++ b/platform/linuxbsd/key_mapping_x11.cpp
@@ -61,8 +61,8 @@ static _XTranslatePair _xkeysym_to_keycode[] = {
{ XK_Shift_L, KEY_SHIFT },
{ XK_Shift_R, KEY_SHIFT },
{ XK_Shift_Lock, KEY_SHIFT },
- { XK_Control_L, KEY_CONTROL },
- { XK_Control_R, KEY_CONTROL },
+ { XK_Control_L, KEY_CTRL },
+ { XK_Control_R, KEY_CTRL },
{ XK_Meta_L, KEY_META },
{ XK_Meta_R, KEY_META },
{ XK_Alt_L, KEY_ALT },
@@ -213,7 +213,7 @@ static _TranslatePair _scancode_to_keycode[] = {
{ KEY_BRACELEFT, 0x22 },
{ KEY_BRACERIGHT, 0x23 },
{ KEY_ENTER, 0x24 },
- { KEY_CONTROL, 0x25 },
+ { KEY_CTRL, 0x25 },
{ KEY_A, 0x26 },
{ KEY_S, 0x27 },
{ KEY_D, 0x28 },
@@ -272,7 +272,7 @@ static _TranslatePair _scancode_to_keycode[] = {
{ KEY_F11, 0x5F },
{ KEY_F12, 0x60 },
{ KEY_KP_ENTER, 0x68 },
- { KEY_CONTROL, 0x69 },
+ { KEY_CTRL, 0x69 },
{ KEY_KP_DIVIDE, 0x6A },
{ KEY_PRINT, 0x6B },
{ KEY_ALT, 0x6C },
diff --git a/platform/linuxbsd/os_linuxbsd.cpp b/platform/linuxbsd/os_linuxbsd.cpp
index 09e1f9461c..8cd6ec43c6 100644
--- a/platform/linuxbsd/os_linuxbsd.cpp
+++ b/platform/linuxbsd/os_linuxbsd.cpp
@@ -30,7 +30,7 @@
#include "os_linuxbsd.h"
-#include "core/os/dir_access.h"
+#include "core/io/dir_access.h"
#include "main/main.h"
#ifdef X11_ENABLED
@@ -116,6 +116,8 @@ String OS_LinuxBSD::get_name() const {
return "FreeBSD";
#elif defined(__NetBSD__)
return "NetBSD";
+#elif defined(__OpenBSD__)
+ return "OpenBSD";
#else
return "BSD";
#endif
@@ -164,7 +166,12 @@ bool OS_LinuxBSD::_check_internal_feature_support(const String &p_feature) {
String OS_LinuxBSD::get_config_path() const {
if (has_environment("XDG_CONFIG_HOME")) {
- return get_environment("XDG_CONFIG_HOME");
+ if (get_environment("XDG_CONFIG_HOME").is_absolute_path()) {
+ return get_environment("XDG_CONFIG_HOME");
+ } else {
+ WARN_PRINT_ONCE("`XDG_CONFIG_HOME` is a relative path. Ignoring its value and falling back to `$HOME/.config` or `.` per the XDG Base Directory specification.");
+ return has_environment("HOME") ? get_environment("HOME").plus_file(".config") : ".";
+ }
} else if (has_environment("HOME")) {
return get_environment("HOME").plus_file(".config");
} else {
@@ -174,7 +181,12 @@ String OS_LinuxBSD::get_config_path() const {
String OS_LinuxBSD::get_data_path() const {
if (has_environment("XDG_DATA_HOME")) {
- return get_environment("XDG_DATA_HOME");
+ if (get_environment("XDG_DATA_HOME").is_absolute_path()) {
+ return get_environment("XDG_DATA_HOME");
+ } else {
+ WARN_PRINT_ONCE("`XDG_DATA_HOME` is a relative path. Ignoring its value and falling back to `$HOME/.local/share` or `get_config_path()` per the XDG Base Directory specification.");
+ return has_environment("HOME") ? get_environment("HOME").plus_file(".local/share") : get_config_path();
+ }
} else if (has_environment("HOME")) {
return get_environment("HOME").plus_file(".local/share");
} else {
@@ -184,7 +196,12 @@ String OS_LinuxBSD::get_data_path() const {
String OS_LinuxBSD::get_cache_path() const {
if (has_environment("XDG_CACHE_HOME")) {
- return get_environment("XDG_CACHE_HOME");
+ if (get_environment("XDG_CACHE_HOME").is_absolute_path()) {
+ return get_environment("XDG_CACHE_HOME");
+ } else {
+ WARN_PRINT_ONCE("`XDG_CACHE_HOME` is a relative path. Ignoring its value and falling back to `$HOME/.cache` or `get_config_path()` per the XDG Base Directory specification.");
+ return has_environment("HOME") ? get_environment("HOME").plus_file(".cache") : get_config_path();
+ }
} else if (has_environment("HOME")) {
return get_environment("HOME").plus_file(".cache");
} else {
@@ -408,8 +425,8 @@ Error OS_LinuxBSD::move_to_trash(const String &p_path) {
// Generates the .trashinfo file
OS::Date date = OS::get_singleton()->get_date(false);
OS::Time time = OS::get_singleton()->get_time(false);
- String timestamp = vformat("%04d-%02d-%02dT%02d:%02d:", date.year, date.month, date.day, time.hour, time.min);
- timestamp = vformat("%s%02d", timestamp, time.sec); // vformat only supports up to 6 arguments.
+ String timestamp = vformat("%04d-%02d-%02dT%02d:%02d:", date.year, (int)date.month, date.day, time.hour, time.minute);
+ timestamp = vformat("%s%02d", timestamp, time.second); // vformat only supports up to 6 arguments.
String trash_info = "[Trash Info]\nPath=" + p_path.uri_encode() + "\nDeletionDate=" + timestamp + "\n";
{
Error err;
diff --git a/platform/linuxbsd/vulkan_context_x11.cpp b/platform/linuxbsd/vulkan_context_x11.cpp
index 021db630e0..88cb00a8a1 100644
--- a/platform/linuxbsd/vulkan_context_x11.cpp
+++ b/platform/linuxbsd/vulkan_context_x11.cpp
@@ -35,7 +35,7 @@ const char *VulkanContextX11::_get_platform_surface_extension() const {
return VK_KHR_XLIB_SURFACE_EXTENSION_NAME;
}
-Error VulkanContextX11::window_create(DisplayServer::WindowID p_window_id, ::Window p_window, Display *p_display, int p_width, int p_height) {
+Error VulkanContextX11::window_create(DisplayServer::WindowID p_window_id, DisplayServer::VSyncMode p_vsync_mode, ::Window p_window, Display *p_display, int p_width, int p_height) {
VkXlibSurfaceCreateInfoKHR createInfo;
createInfo.sType = VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR;
createInfo.pNext = nullptr;
@@ -46,7 +46,7 @@ Error VulkanContextX11::window_create(DisplayServer::WindowID p_window_id, ::Win
VkSurfaceKHR surface;
VkResult err = vkCreateXlibSurfaceKHR(_get_instance(), &createInfo, nullptr, &surface);
ERR_FAIL_COND_V(err, ERR_CANT_CREATE);
- return _window_create(p_window_id, surface, p_width, p_height);
+ return _window_create(p_window_id, p_vsync_mode, surface, p_width, p_height);
}
VulkanContextX11::VulkanContextX11() {
diff --git a/platform/linuxbsd/vulkan_context_x11.h b/platform/linuxbsd/vulkan_context_x11.h
index 26472444ad..de4a9c7b90 100644
--- a/platform/linuxbsd/vulkan_context_x11.h
+++ b/platform/linuxbsd/vulkan_context_x11.h
@@ -38,7 +38,7 @@ class VulkanContextX11 : public VulkanContext {
virtual const char *_get_platform_surface_extension() const;
public:
- Error window_create(DisplayServer::WindowID p_window_id, ::Window p_window, Display *p_display, int p_width, int p_height);
+ Error window_create(DisplayServer::WindowID p_window_id, DisplayServer::VSyncMode p_vsync_mode, ::Window p_window, Display *p_display, int p_width, int p_height);
VulkanContextX11();
~VulkanContextX11();
diff --git a/platform/osx/crash_handler_osx.mm b/platform/osx/crash_handler_osx.mm
index 147ce26779..0f128d504f 100644
--- a/platform/osx/crash_handler_osx.mm
+++ b/platform/osx/crash_handler_osx.mm
@@ -70,7 +70,7 @@ static uint64_t load_address() {
}
static void handle_crash(int sig) {
- if (OS::get_singleton() == NULL) {
+ if (OS::get_singleton() == nullptr) {
abort();
}
@@ -105,7 +105,7 @@ static void handle_crash(int sig) {
if (dladdr(bt_buffer[i], &info) && info.dli_sname) {
if (info.dli_sname[0] == '_') {
int status;
- char *demangled = abi::__cxa_demangle(info.dli_sname, NULL, 0, &status);
+ char *demangled = abi::__cxa_demangle(info.dli_sname, nullptr, 0, &status);
if (status == 0 && demangled) {
snprintf(fname, 1024, "%s", demangled);
@@ -167,9 +167,9 @@ void CrashHandler::disable() {
return;
#ifdef CRASH_HANDLER_ENABLED
- signal(SIGSEGV, NULL);
- signal(SIGFPE, NULL);
- signal(SIGILL, NULL);
+ signal(SIGSEGV, nullptr);
+ signal(SIGFPE, nullptr);
+ signal(SIGILL, nullptr);
#endif
disabled = true;
diff --git a/platform/osx/detect.py b/platform/osx/detect.py
index acea00c5ac..317e79d0ea 100644
--- a/platform/osx/detect.py
+++ b/platform/osx/detect.py
@@ -34,8 +34,8 @@ def get_opts():
BoolVariable("debug_symbols", "Add debugging symbols to release/release_debug builds", True),
BoolVariable("separate_debug_symbols", "Create a separate file containing debugging symbols", False),
BoolVariable("use_ubsan", "Use LLVM/GCC compiler undefined behavior sanitizer (UBSAN)", False),
- BoolVariable("use_asan", "Use LLVM/GCC compiler address sanitizer (ASAN))", False),
- BoolVariable("use_tsan", "Use LLVM/GCC compiler thread sanitizer (TSAN))", False),
+ BoolVariable("use_asan", "Use LLVM/GCC compiler address sanitizer (ASAN)", False),
+ BoolVariable("use_tsan", "Use LLVM/GCC compiler thread sanitizer (TSAN)", False),
]
@@ -49,7 +49,7 @@ def configure(env):
if env["target"] == "release":
if env["optimize"] == "speed": # optimize for speed (default)
env.Prepend(CCFLAGS=["-O3", "-fomit-frame-pointer", "-ftree-vectorize"])
- else: # optimize for size
+ elif env["optimize"] == "size": # optimize for size
env.Prepend(CCFLAGS=["-Os", "-ftree-vectorize"])
if env["arch"] != "arm64":
env.Prepend(CCFLAGS=["-msse2"])
@@ -60,7 +60,7 @@ def configure(env):
elif env["target"] == "release_debug":
if env["optimize"] == "speed": # optimize for speed (default)
env.Prepend(CCFLAGS=["-O2"])
- else: # optimize for size
+ elif env["optimize"] == "size": # optimize for size
env.Prepend(CCFLAGS=["-Os"])
env.Prepend(CPPDEFINES=["DEBUG_ENABLED"])
if env["debug_symbols"]:
@@ -135,11 +135,16 @@ def configure(env):
env.extra_suffix += "s"
if env["use_ubsan"]:
- env.Append(CCFLAGS=["-fsanitize=undefined"])
+ env.Append(
+ CCFLAGS=[
+ "-fsanitize=undefined,shift,shift-exponent,integer-divide-by-zero,unreachable,vla-bound,null,return,signed-integer-overflow,bounds,float-divide-by-zero,float-cast-overflow,nonnull-attribute,returns-nonnull-attribute,bool,enum,vptr,pointer-overflow,builtin"
+ ]
+ )
env.Append(LINKFLAGS=["-fsanitize=undefined"])
+ env.Append(CCFLAGS=["-fsanitize=nullability-return,nullability-arg,function,nullability-assign"])
if env["use_asan"]:
- env.Append(CCFLAGS=["-fsanitize=address"])
+ env.Append(CCFLAGS=["-fsanitize=address,pointer-subtract,pointer-compare"])
env.Append(LINKFLAGS=["-fsanitize=address"])
if env["use_tsan"]:
diff --git a/platform/osx/dir_access_osx.h b/platform/osx/dir_access_osx.h
index f61581979f..a894723e64 100644
--- a/platform/osx/dir_access_osx.h
+++ b/platform/osx/dir_access_osx.h
@@ -38,7 +38,7 @@
#include <sys/types.h>
#include <unistd.h>
-#include "core/os/dir_access.h"
+#include "core/io/dir_access.h"
#include "drivers/unix/dir_access_unix.h"
class DirAccessOSX : public DirAccessUnix {
diff --git a/platform/osx/display_server_osx.h b/platform/osx/display_server_osx.h
index 9fac99810b..c7b9e411b8 100644
--- a/platform/osx/display_server_osx.h
+++ b/platform/osx/display_server_osx.h
@@ -145,7 +145,7 @@ public:
WindowID window_id_counter = MAIN_WINDOW_ID;
- WindowID _create_window(WindowMode p_mode, const Rect2i &p_rect);
+ WindowID _create_window(WindowMode p_mode, VSyncMode p_vsync_mode, const Rect2i &p_rect);
void _update_window(WindowData p_wd);
void _send_window_event(const WindowData &wd, WindowEvent p_event);
static void _dispatch_input_events(const Ref<InputEvent> &p_event);
@@ -173,7 +173,7 @@ public:
MouseMode mouse_mode;
Point2i last_mouse_pos;
- uint32_t last_button_state;
+ MouseButton last_button_state = MOUSE_BUTTON_NONE;
bool window_focused;
bool drop_events;
@@ -217,7 +217,7 @@ public:
virtual void mouse_warp_to_position(const Point2i &p_to) override;
virtual Point2i mouse_get_position() const override;
virtual Point2i mouse_get_absolute_position() const override;
- virtual int mouse_get_button_state() const override;
+ virtual MouseButton mouse_get_button_state() const override;
virtual void clipboard_set(const String &p_text) override;
virtual String clipboard_get() const override;
@@ -232,7 +232,7 @@ public:
virtual Vector<int> get_window_list() const override;
- virtual WindowID create_sub_window(WindowMode p_mode, uint32_t p_flags, const Rect2i &p_rect = Rect2i()) override;
+ virtual WindowID create_sub_window(WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Rect2i &p_rect = Rect2i()) override;
virtual void show_window(WindowID p_id) override;
virtual void delete_sub_window(WindowID p_id) override;
@@ -286,6 +286,9 @@ public:
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_vsync_mode(DisplayServer::VSyncMode p_vsync_mode, WindowID p_window = MAIN_WINDOW_ID) override;
+ virtual DisplayServer::VSyncMode window_get_vsync_mode(WindowID p_vsync_mode) const override;
+
virtual Point2i ime_get_selection() const override;
virtual String ime_get_text() const override;
@@ -314,12 +317,12 @@ public:
virtual void console_set_visible(bool p_enabled) override;
virtual bool is_console_visible() const override;
- static DisplayServer *create_func(const String &p_rendering_driver, WindowMode p_mode, uint32_t p_flags, const Vector2i &p_resolution, Error &r_error);
+ static DisplayServer *create_func(const String &p_rendering_driver, WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i &p_resolution, Error &r_error);
static Vector<String> get_rendering_drivers_func();
static void register_osx_driver();
- DisplayServerOSX(const String &p_rendering_driver, WindowMode p_mode, uint32_t p_flags, const Vector2i &p_resolution, Error &r_error);
+ DisplayServerOSX(const String &p_rendering_driver, WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i &p_resolution, Error &r_error);
~DisplayServerOSX();
};
diff --git a/platform/osx/display_server_osx.mm b/platform/osx/display_server_osx.mm
index ed7d89009f..dec6da42fe 100644
--- a/platform/osx/display_server_osx.mm
+++ b/platform/osx/display_server_osx.mm
@@ -66,10 +66,10 @@
static bool ignore_momentum_scroll = false;
static void _get_key_modifier_state(unsigned int p_osx_state, Ref<InputEventWithModifiers> r_state) {
- r_state->set_shift((p_osx_state & NSEventModifierFlagShift));
- r_state->set_control((p_osx_state & NSEventModifierFlagControl));
- r_state->set_alt((p_osx_state & NSEventModifierFlagOption));
- r_state->set_metakey((p_osx_state & NSEventModifierFlagCommand));
+ r_state->set_shift_pressed((p_osx_state & NSEventModifierFlagShift));
+ r_state->set_ctrl_pressed((p_osx_state & NSEventModifierFlagControl));
+ r_state->set_alt_pressed((p_osx_state & NSEventModifierFlagOption));
+ r_state->set_meta_pressed((p_osx_state & NSEventModifierFlagCommand));
}
static Vector2i _get_mouse_pos(DisplayServerOSX::WindowData &p_wd, NSPoint p_locationInWindow) {
@@ -119,7 +119,7 @@ static NSCursor *_cursorFromSelector(SEL selector, SEL fallback = nil) {
if ([event type] == NSEventTypeKeyDown) {
if (([event modifierFlags] & NSEventModifierFlagCommand) && [event keyCode] == 0x2f) {
Ref<InputEventKey> k;
- k.instance();
+ k.instantiate();
_get_key_modifier_state([event modifierFlags], k);
k->set_window_id(DisplayServerOSX::INVALID_WINDOW_ID);
@@ -368,6 +368,10 @@ static NSCursor *_cursorFromSelector(SEL selector, SEL fallback = nil) {
if (wd.resize_disabled) {
[wd.window_object setStyleMask:[wd.window_object styleMask] & ~NSWindowStyleMaskResizable];
}
+
+ if (wd.on_top) {
+ [wd.window_object setLevel:NSFloatingWindowLevel];
+ }
}
- (void)windowDidChangeBackingProperties:(NSNotification *)notification {
@@ -809,18 +813,18 @@ static const NSRange kEmptyRange = { NSNotFound, 0 };
DS_OSX->cursor_set_shape(p_shape);
}
-static void _mouseDownEvent(DisplayServer::WindowID window_id, NSEvent *event, int index, int mask, bool pressed) {
+static void _mouseDownEvent(DisplayServer::WindowID window_id, NSEvent *event, MouseButton index, MouseButton mask, bool pressed) {
ERR_FAIL_COND(!DS_OSX->windows.has(window_id));
DisplayServerOSX::WindowData &wd = DS_OSX->windows[window_id];
if (pressed) {
DS_OSX->last_button_state |= mask;
} else {
- DS_OSX->last_button_state &= ~mask;
+ DS_OSX->last_button_state &= (MouseButton)~mask;
}
Ref<InputEventMouseButton> mb;
- mb.instance();
+ mb.instantiate();
mb->set_window_id(window_id);
const Vector2 pos = _get_mouse_pos(wd, [event locationInWindow]);
_get_key_modifier_state([event modifierFlags], mb);
@@ -829,8 +833,8 @@ static void _mouseDownEvent(DisplayServer::WindowID window_id, NSEvent *event, i
mb->set_position(pos);
mb->set_global_position(pos);
mb->set_button_mask(DS_OSX->last_button_state);
- if (index == BUTTON_LEFT && pressed) {
- mb->set_doubleclick([event clickCount] == 2);
+ if (index == MOUSE_BUTTON_LEFT && pressed) {
+ mb->set_double_click([event clickCount] == 2);
}
Input::get_singleton()->accumulate_input_event(mb);
@@ -842,10 +846,10 @@ static void _mouseDownEvent(DisplayServer::WindowID window_id, NSEvent *event, i
if (([event modifierFlags] & NSEventModifierFlagControl)) {
wd.mouse_down_control = true;
- _mouseDownEvent(window_id, event, BUTTON_RIGHT, BUTTON_MASK_RIGHT, true);
+ _mouseDownEvent(window_id, event, MOUSE_BUTTON_RIGHT, MOUSE_BUTTON_MASK_RIGHT, true);
} else {
wd.mouse_down_control = false;
- _mouseDownEvent(window_id, event, BUTTON_LEFT, BUTTON_MASK_LEFT, true);
+ _mouseDownEvent(window_id, event, MOUSE_BUTTON_LEFT, MOUSE_BUTTON_MASK_LEFT, true);
}
}
@@ -858,9 +862,9 @@ static void _mouseDownEvent(DisplayServer::WindowID window_id, NSEvent *event, i
DisplayServerOSX::WindowData &wd = DS_OSX->windows[window_id];
if (wd.mouse_down_control) {
- _mouseDownEvent(window_id, event, BUTTON_RIGHT, BUTTON_MASK_RIGHT, false);
+ _mouseDownEvent(window_id, event, MOUSE_BUTTON_RIGHT, MOUSE_BUTTON_MASK_RIGHT, false);
} else {
- _mouseDownEvent(window_id, event, BUTTON_LEFT, BUTTON_MASK_LEFT, false);
+ _mouseDownEvent(window_id, event, MOUSE_BUTTON_LEFT, MOUSE_BUTTON_MASK_LEFT, false);
}
}
@@ -880,7 +884,7 @@ static void _mouseDownEvent(DisplayServer::WindowID window_id, NSEvent *event, i
return;
}
- if (DS_OSX->mouse_mode == DisplayServer::MOUSE_MODE_CONFINED) {
+ if (DS_OSX->mouse_mode == DisplayServer::MOUSE_MODE_CONFINED || DS_OSX->mouse_mode == DisplayServer::MOUSE_MODE_CONFINED_HIDDEN) {
// Discard late events
if (([event timestamp]) < DS_OSX->last_warp) {
return;
@@ -924,7 +928,7 @@ static void _mouseDownEvent(DisplayServer::WindowID window_id, NSEvent *event, i
}
Ref<InputEventMouseMotion> mm;
- mm.instance();
+ mm.instantiate();
mm->set_window_id(window_id);
mm->set_button_mask(DS_OSX->last_button_state);
@@ -946,7 +950,7 @@ static void _mouseDownEvent(DisplayServer::WindowID window_id, NSEvent *event, i
}
- (void)rightMouseDown:(NSEvent *)event {
- _mouseDownEvent(window_id, event, BUTTON_RIGHT, BUTTON_MASK_RIGHT, true);
+ _mouseDownEvent(window_id, event, MOUSE_BUTTON_RIGHT, MOUSE_BUTTON_MASK_RIGHT, true);
}
- (void)rightMouseDragged:(NSEvent *)event {
@@ -954,16 +958,16 @@ static void _mouseDownEvent(DisplayServer::WindowID window_id, NSEvent *event, i
}
- (void)rightMouseUp:(NSEvent *)event {
- _mouseDownEvent(window_id, event, BUTTON_RIGHT, BUTTON_MASK_RIGHT, false);
+ _mouseDownEvent(window_id, event, MOUSE_BUTTON_RIGHT, MOUSE_BUTTON_MASK_RIGHT, false);
}
- (void)otherMouseDown:(NSEvent *)event {
if ((int)[event buttonNumber] == 2) {
- _mouseDownEvent(window_id, event, BUTTON_MIDDLE, BUTTON_MASK_MIDDLE, true);
+ _mouseDownEvent(window_id, event, MOUSE_BUTTON_MIDDLE, MOUSE_BUTTON_MASK_MIDDLE, true);
} else if ((int)[event buttonNumber] == 3) {
- _mouseDownEvent(window_id, event, BUTTON_XBUTTON1, BUTTON_MASK_XBUTTON1, true);
+ _mouseDownEvent(window_id, event, MOUSE_BUTTON_XBUTTON1, MOUSE_BUTTON_MASK_XBUTTON1, true);
} else if ((int)[event buttonNumber] == 4) {
- _mouseDownEvent(window_id, event, BUTTON_XBUTTON2, BUTTON_MASK_XBUTTON2, true);
+ _mouseDownEvent(window_id, event, MOUSE_BUTTON_XBUTTON2, MOUSE_BUTTON_MASK_XBUTTON2, true);
} else {
return;
}
@@ -975,11 +979,11 @@ static void _mouseDownEvent(DisplayServer::WindowID window_id, NSEvent *event, i
- (void)otherMouseUp:(NSEvent *)event {
if ((int)[event buttonNumber] == 2) {
- _mouseDownEvent(window_id, event, BUTTON_MIDDLE, BUTTON_MASK_MIDDLE, false);
+ _mouseDownEvent(window_id, event, MOUSE_BUTTON_MIDDLE, MOUSE_BUTTON_MASK_MIDDLE, false);
} else if ((int)[event buttonNumber] == 3) {
- _mouseDownEvent(window_id, event, BUTTON_XBUTTON1, BUTTON_MASK_XBUTTON1, false);
+ _mouseDownEvent(window_id, event, MOUSE_BUTTON_XBUTTON1, MOUSE_BUTTON_MASK_XBUTTON1, false);
} else if ((int)[event buttonNumber] == 4) {
- _mouseDownEvent(window_id, event, BUTTON_XBUTTON2, BUTTON_MASK_XBUTTON2, false);
+ _mouseDownEvent(window_id, event, MOUSE_BUTTON_XBUTTON2, MOUSE_BUTTON_MASK_XBUTTON2, false);
} else {
return;
}
@@ -1012,7 +1016,7 @@ static void _mouseDownEvent(DisplayServer::WindowID window_id, NSEvent *event, i
DisplayServerOSX::WindowData &wd = DS_OSX->windows[window_id];
Ref<InputEventMagnifyGesture> ev;
- ev.instance();
+ ev.instantiate();
ev->set_window_id(window_id);
_get_key_modifier_state([event modifierFlags], ev);
ev->set_position(_get_mouse_pos(wd, [event locationInWindow]));
@@ -1133,10 +1137,10 @@ static int translateKey(unsigned int key) {
/* 38 */ KEY_SHIFT,
/* 39 */ KEY_CAPSLOCK,
/* 3a */ KEY_ALT,
- /* 3b */ KEY_CONTROL,
+ /* 3b */ KEY_CTRL,
/* 3c */ KEY_SHIFT,
/* 3d */ KEY_ALT,
- /* 3e */ KEY_CONTROL,
+ /* 3e */ KEY_CTRL,
/* 3f */ KEY_UNKNOWN, /* Function */
/* 40 */ KEY_UNKNOWN, /* F17 */
/* 41 */ KEY_KP_PERIOD,
@@ -1481,14 +1485,14 @@ static int remapKey(unsigned int key, unsigned int state) {
}
}
-inline void sendScrollEvent(DisplayServer::WindowID window_id, int button, double factor, int modifierFlags) {
+inline void sendScrollEvent(DisplayServer::WindowID window_id, MouseButton button, double factor, int modifierFlags) {
ERR_FAIL_COND(!DS_OSX->windows.has(window_id));
DisplayServerOSX::WindowData &wd = DS_OSX->windows[window_id];
- unsigned int mask = 1 << (button - 1);
+ MouseButton mask = MouseButton(1 << (button - 1));
Ref<InputEventMouseButton> sc;
- sc.instance();
+ sc.instantiate();
sc->set_window_id(window_id);
_get_key_modifier_state(modifierFlags, sc);
@@ -1497,19 +1501,19 @@ inline void sendScrollEvent(DisplayServer::WindowID window_id, int button, doubl
sc->set_pressed(true);
sc->set_position(wd.mouse_pos);
sc->set_global_position(wd.mouse_pos);
- DS_OSX->last_button_state |= mask;
+ DS_OSX->last_button_state |= (MouseButton)mask;
sc->set_button_mask(DS_OSX->last_button_state);
Input::get_singleton()->accumulate_input_event(sc);
- sc.instance();
+ sc.instantiate();
sc->set_window_id(window_id);
sc->set_button_index(button);
sc->set_factor(factor);
sc->set_pressed(false);
sc->set_position(wd.mouse_pos);
sc->set_global_position(wd.mouse_pos);
- DS_OSX->last_button_state &= ~mask;
+ DS_OSX->last_button_state &= (MouseButton)~mask;
sc->set_button_mask(DS_OSX->last_button_state);
Input::get_singleton()->accumulate_input_event(sc);
@@ -1520,7 +1524,7 @@ inline void sendPanEvent(DisplayServer::WindowID window_id, double dx, double dy
DisplayServerOSX::WindowData &wd = DS_OSX->windows[window_id];
Ref<InputEventPanGesture> pg;
- pg.instance();
+ pg.instantiate();
pg->set_window_id(window_id);
_get_key_modifier_state(modifierFlags, pg);
@@ -1558,10 +1562,10 @@ inline void sendPanEvent(DisplayServer::WindowID window_id, double dx, double dy
sendPanEvent(window_id, deltaX, deltaY, [event modifierFlags]);
} else {
if (fabs(deltaX)) {
- sendScrollEvent(window_id, 0 > deltaX ? BUTTON_WHEEL_RIGHT : BUTTON_WHEEL_LEFT, fabs(deltaX * 0.3), [event modifierFlags]);
+ sendScrollEvent(window_id, 0 > deltaX ? MOUSE_BUTTON_WHEEL_RIGHT : MOUSE_BUTTON_WHEEL_LEFT, fabs(deltaX * 0.3), [event modifierFlags]);
}
if (fabs(deltaY)) {
- sendScrollEvent(window_id, 0 < deltaY ? BUTTON_WHEEL_UP : BUTTON_WHEEL_DOWN, fabs(deltaY * 0.3), [event modifierFlags]);
+ sendScrollEvent(window_id, 0 < deltaY ? MOUSE_BUTTON_WHEEL_UP : MOUSE_BUTTON_WHEEL_DOWN, fabs(deltaY * 0.3), [event modifierFlags]);
}
}
}
@@ -1640,7 +1644,7 @@ String DisplayServerOSX::get_name() const {
}
const NSMenu *DisplayServerOSX::_get_menu_root(const String &p_menu_root) const {
- const NSMenu *menu = NULL;
+ const NSMenu *menu = nullptr;
if (p_menu_root == "") {
// Main menu.x
menu = [NSApp mainMenu];
@@ -1655,13 +1659,13 @@ const NSMenu *DisplayServerOSX::_get_menu_root(const String &p_menu_root) const
}
if (menu == apple_menu) {
// Do not allow to change Apple menu.
- return NULL;
+ return nullptr;
}
return menu;
}
NSMenu *DisplayServerOSX::_get_menu_root(const String &p_menu_root) {
- NSMenu *menu = NULL;
+ NSMenu *menu = nullptr;
if (p_menu_root == "") {
// Main menu.
menu = [NSApp mainMenu];
@@ -1678,7 +1682,7 @@ NSMenu *DisplayServerOSX::_get_menu_root(const String &p_menu_root) {
}
if (menu == apple_menu) {
// Do not allow to change Apple menu.
- return NULL;
+ return nullptr;
}
return menu;
}
@@ -2102,7 +2106,12 @@ void DisplayServerOSX::mouse_set_mode(MouseMode p_mode) {
} else if (p_mode == MOUSE_MODE_CONFINED) {
CGDisplayShowCursor(kCGDirectMainDisplay);
CGAssociateMouseAndMouseCursorPosition(false);
- } else {
+ } else if (p_mode == MOUSE_MODE_CONFINED_HIDDEN) {
+ if (mouse_mode == MOUSE_MODE_VISIBLE || mouse_mode == MOUSE_MODE_CONFINED) {
+ CGDisplayHideCursor(kCGDirectMainDisplay);
+ }
+ CGAssociateMouseAndMouseCursorPosition(false);
+ } else { // MOUSE_MODE_VISIBLE
CGDisplayShowCursor(kCGDirectMainDisplay);
CGAssociateMouseAndMouseCursorPosition(true);
}
@@ -2111,6 +2120,12 @@ void DisplayServerOSX::mouse_set_mode(MouseMode p_mode) {
ignore_warp = true;
warp_events.clear();
mouse_mode = p_mode;
+
+ if (mouse_mode == MOUSE_MODE_VISIBLE || mouse_mode == MOUSE_MODE_CONFINED) {
+ CursorShape p_shape = cursor_shape;
+ cursor_shape = DisplayServer::CURSOR_MAX;
+ cursor_set_shape(p_shape);
+ }
}
DisplayServer::MouseMode DisplayServerOSX::mouse_get_mode() const {
@@ -2139,7 +2154,7 @@ void DisplayServerOSX::mouse_warp_to_position(const Point2i &p_to) {
CGEventSourceSetLocalEventsSuppressionInterval(lEventRef, 0.0);
CGAssociateMouseAndMouseCursorPosition(false);
CGWarpMouseCursorPosition(lMouseWarpPos);
- if (mouse_mode != MOUSE_MODE_CONFINED) {
+ if (mouse_mode != MOUSE_MODE_CONFINED && mouse_mode != MOUSE_MODE_CONFINED_HIDDEN) {
CGAssociateMouseAndMouseCursorPosition(true);
}
}
@@ -2164,7 +2179,7 @@ Point2i DisplayServerOSX::mouse_get_absolute_position() const {
return Vector2i();
}
-int DisplayServerOSX::mouse_get_button_state() const {
+MouseButton DisplayServerOSX::mouse_get_button_state() const {
return last_button_state;
}
@@ -2373,10 +2388,10 @@ Vector<DisplayServer::WindowID> DisplayServerOSX::get_window_list() const {
return ret;
}
-DisplayServer::WindowID DisplayServerOSX::create_sub_window(WindowMode p_mode, uint32_t p_flags, const Rect2i &p_rect) {
+DisplayServer::WindowID DisplayServerOSX::create_sub_window(WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Rect2i &p_rect) {
_THREAD_SAFE_METHOD_
- WindowID id = _create_window(p_mode, p_rect);
+ WindowID id = _create_window(p_mode, p_vsync_mode, p_rect);
for (int i = 0; i < WINDOW_FLAG_MAX; i++) {
if (p_flags & (1 << i)) {
window_set_flag(WindowFlags(i), true, id);
@@ -2437,7 +2452,7 @@ void DisplayServerOSX::_update_window(WindowData p_wd) {
[p_wd.window_object setHidesOnDeactivate:YES];
} else {
// Reset these when our window is not a borderless window that covers up the screen
- if (p_wd.on_top) {
+ if (p_wd.on_top && !p_wd.fullscreen) {
[p_wd.window_object setLevel:NSFloatingWindowLevel];
} else {
[p_wd.window_object setLevel:NSNormalWindowLevel];
@@ -2786,6 +2801,7 @@ void DisplayServerOSX::window_set_mode(WindowMode p_mode, WindowID p_window) {
[wd.window_object deminiaturize:nil];
} break;
case WINDOW_MODE_FULLSCREEN: {
+ [wd.window_object setLevel:NSNormalWindowLevel];
if (wd.layered_window) {
_set_window_per_pixel_transparency_enabled(true, p_window);
}
@@ -2903,6 +2919,9 @@ void DisplayServerOSX::window_set_flag(WindowFlags p_flag, bool p_enabled, Windo
} break;
case WINDOW_FLAG_ALWAYS_ON_TOP: {
wd.on_top = p_enabled;
+ if (wd.fullscreen) {
+ return;
+ }
if (p_enabled) {
[wd.window_object setLevel:NSFloatingWindowLevel];
} else {
@@ -2940,7 +2959,11 @@ bool DisplayServerOSX::window_get_flag(WindowFlags p_flag, WindowID p_window) co
return [wd.window_object styleMask] == NSWindowStyleMaskBorderless;
} break;
case WINDOW_FLAG_ALWAYS_ON_TOP: {
- return [wd.window_object level] == NSFloatingWindowLevel;
+ if (wd.fullscreen) {
+ return wd.on_top;
+ } else {
+ return [wd.window_object level] == NSFloatingWindowLevel;
+ }
} break;
case WINDOW_FLAG_TRANSPARENT: {
return wd.layered_window;
@@ -3029,7 +3052,7 @@ void DisplayServerOSX::cursor_set_shape(CursorShape p_shape) {
return;
}
- if (cursors[p_shape] != NULL) {
+ if (cursors[p_shape] != nullptr) {
[cursors[p_shape] set];
} else {
switch (p_shape) {
@@ -3117,7 +3140,7 @@ void DisplayServerOSX::cursor_set_custom_image(const RES &p_cursor, CursorShape
Rect2 atlas_rect;
if (texture.is_valid()) {
- image = texture->get_data();
+ image = texture->get_image();
}
if (!image.is_valid() && atlas_texture.is_valid()) {
@@ -3140,7 +3163,7 @@ void DisplayServerOSX::cursor_set_custom_image(const RES &p_cursor, CursorShape
ERR_FAIL_COND(texture_size.width > 256 || texture_size.height > 256);
ERR_FAIL_COND(p_hotspot.x > texture_size.width || p_hotspot.y > texture_size.height);
- image = texture->get_data();
+ image = texture->get_image();
ERR_FAIL_COND(!image.is_valid());
@@ -3202,9 +3225,9 @@ void DisplayServerOSX::cursor_set_custom_image(const RES &p_cursor, CursorShape
[nsimage release];
} else {
// Reset to default system cursor
- if (cursors[p_shape] != NULL) {
+ if (cursors[p_shape] != nullptr) {
[cursors[p_shape] release];
- cursors[p_shape] = NULL;
+ cursors[p_shape] = nullptr;
}
CursorShape c = cursor_shape;
@@ -3358,7 +3381,7 @@ void DisplayServerOSX::_process_key_events() {
const KeyEvent &ke = key_event_buffer[i];
if (ke.raw) {
// Non IME input - no composite characters, pass events as is
- k.instance();
+ k.instantiate();
k->set_window_id(ke.window_id);
_get_key_modifier_state(ke.osx_state, k);
@@ -3372,7 +3395,7 @@ void DisplayServerOSX::_process_key_events() {
} else {
// IME input
if ((i == 0 && ke.keycode == 0) || (i > 0 && key_event_buffer[i - 1].keycode == 0)) {
- k.instance();
+ k.instantiate();
k->set_window_id(ke.window_id);
_get_key_modifier_state(ke.osx_state, k);
@@ -3385,7 +3408,7 @@ void DisplayServerOSX::_process_key_events() {
_push_input(k);
}
if (ke.keycode != 0) {
- k.instance();
+ k.instantiate();
k->set_window_id(ke.window_id);
_get_key_modifier_state(ke.osx_state, k);
@@ -3467,7 +3490,7 @@ void DisplayServerOSX::set_native_icon(const String &p_filename) {
ERR_FAIL_COND(!f);
Vector<uint8_t> data;
- uint32_t len = f->get_len();
+ uint64_t len = f->get_length();
data.resize(len);
f->get_buffer((uint8_t *)&data.write[0], len);
memdelete(f);
@@ -3523,6 +3546,22 @@ void DisplayServerOSX::set_icon(const Ref<Image> &p_icon) {
[nsimg release];
}
+void DisplayServerOSX::window_set_vsync_mode(DisplayServer::VSyncMode p_vsync_mode, WindowID p_window) {
+ _THREAD_SAFE_METHOD_
+#if defined(VULKAN_ENABLED)
+ context_vulkan->set_vsync_mode(p_window, p_vsync_mode);
+#endif
+}
+
+DisplayServer::VSyncMode DisplayServerOSX::window_get_vsync_mode(WindowID p_window) const {
+ _THREAD_SAFE_METHOD_
+#if defined(VULKAN_ENABLED)
+ return context_vulkan->get_vsync_mode(p_window);
+#else
+ return DisplayServer::VSYNC_ENABLED;
+#endif
+}
+
Vector<String> DisplayServerOSX::get_rendering_drivers_func() {
Vector<String> drivers;
@@ -3573,15 +3612,15 @@ ObjectID DisplayServerOSX::window_get_attached_instance_id(WindowID p_window) co
return windows[p_window].instance_id;
}
-DisplayServer *DisplayServerOSX::create_func(const String &p_rendering_driver, WindowMode p_mode, uint32_t p_flags, const Vector2i &p_resolution, Error &r_error) {
- DisplayServer *ds = memnew(DisplayServerOSX(p_rendering_driver, p_mode, p_flags, p_resolution, r_error));
+DisplayServer *DisplayServerOSX::create_func(const String &p_rendering_driver, WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i &p_resolution, Error &r_error) {
+ DisplayServer *ds = memnew(DisplayServerOSX(p_rendering_driver, p_mode, p_vsync_mode, p_flags, p_resolution, r_error));
if (r_error != OK) {
ds->alert("Your video card driver does not support any of the supported Metal versions.", "Unable to initialize Video driver");
}
return ds;
}
-DisplayServerOSX::WindowID DisplayServerOSX::_create_window(WindowMode p_mode, const Rect2i &p_rect) {
+DisplayServerOSX::WindowID DisplayServerOSX::_create_window(WindowMode p_mode, VSyncMode p_vsync_mode, const Rect2i &p_rect) {
WindowID id;
const float scale = screen_get_max_scale();
{
@@ -3628,7 +3667,7 @@ DisplayServerOSX::WindowID DisplayServerOSX::_create_window(WindowMode p_mode, c
#if defined(VULKAN_ENABLED)
if (rendering_driver == "vulkan") {
if (context_vulkan) {
- Error err = context_vulkan->window_create(window_id_counter, wd.window_view, p_rect.size.width, p_rect.size.height);
+ Error err = context_vulkan->window_create(window_id_counter, p_vsync_mode, wd.window_view, p_rect.size.width, p_rect.size.height);
ERR_FAIL_COND_V_MSG(err != OK, INVALID_WINDOW_ID, "Can't create a Vulkan context");
}
}
@@ -3727,7 +3766,7 @@ bool DisplayServerOSX::is_console_visible() const {
return isatty(STDIN_FILENO);
}
-DisplayServerOSX::DisplayServerOSX(const String &p_rendering_driver, WindowMode p_mode, uint32_t p_flags, const Vector2i &p_resolution, Error &r_error) {
+DisplayServerOSX::DisplayServerOSX(const String &p_rendering_driver, WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i &p_resolution, Error &r_error) {
Input::get_singleton()->set_event_dispatch_function(_dispatch_input_events);
r_error = OK;
@@ -3738,7 +3777,6 @@ DisplayServerOSX::DisplayServerOSX(const String &p_rendering_driver, WindowMode
key_event_pos = 0;
mouse_mode = MOUSE_MODE_VISIBLE;
- last_button_state = 0;
autoreleasePool = [[NSAutoreleasePool alloc] init];
@@ -3759,12 +3797,12 @@ DisplayServerOSX::DisplayServerOSX(const String &p_rendering_driver, WindowMode
// Register to be notified on keyboard layout changes
CFNotificationCenterAddObserver(CFNotificationCenterGetDistributedCenter(),
- NULL, keyboard_layout_changed,
- kTISNotifySelectedKeyboardInputSourceChanged, NULL,
+ nullptr, keyboard_layout_changed,
+ kTISNotifySelectedKeyboardInputSourceChanged, nullptr,
CFNotificationSuspensionBehaviorDeliverImmediately);
// Register to be notified on displays arrangement changes
- CGDisplayRegisterReconfigurationCallback(displays_arrangement_changed, NULL);
+ CGDisplayRegisterReconfigurationCallback(displays_arrangement_changed, nullptr);
// Menu bar setup must go between sharedApplication above and
// finishLaunching below, in order to properly emulate the behavior
@@ -3854,7 +3892,7 @@ DisplayServerOSX::DisplayServerOSX(const String &p_rendering_driver, WindowMode
context_vulkan = memnew(VulkanContextOSX);
if (context_vulkan->initialize() != OK) {
memdelete(context_vulkan);
- context_vulkan = NULL;
+ context_vulkan = nullptr;
r_error = ERR_CANT_CREATE;
ERR_FAIL_MSG("Could not initialize Vulkan");
}
@@ -3864,7 +3902,7 @@ DisplayServerOSX::DisplayServerOSX(const String &p_rendering_driver, WindowMode
Point2i window_position(
screen_get_position(0).x + (screen_get_size(0).width - p_resolution.width) / 2,
screen_get_position(0).y + (screen_get_size(0).height - p_resolution.height) / 2);
- WindowID main_window = _create_window(p_mode, Rect2i(window_position, p_resolution));
+ WindowID main_window = _create_window(p_mode, p_vsync_mode, Rect2i(window_position, p_resolution));
ERR_FAIL_COND(main_window == INVALID_WINDOW_ID);
for (int i = 0; i < WINDOW_FLAG_MAX; i++) {
if (p_flags & (1 << i)) {
@@ -3926,8 +3964,8 @@ DisplayServerOSX::~DisplayServerOSX() {
}
#endif
- CFNotificationCenterRemoveObserver(CFNotificationCenterGetDistributedCenter(), NULL, kTISNotifySelectedKeyboardInputSourceChanged, NULL);
- CGDisplayRemoveReconfigurationCallback(displays_arrangement_changed, NULL);
+ CFNotificationCenterRemoveObserver(CFNotificationCenterGetDistributedCenter(), nullptr, kTISNotifySelectedKeyboardInputSourceChanged, nullptr);
+ CGDisplayRemoveReconfigurationCallback(displays_arrangement_changed, nullptr);
cursors_cache.clear();
}
diff --git a/platform/osx/export/export.cpp b/platform/osx/export/export.cpp
index 337cfd6808..ea34b8a24e 100644
--- a/platform/osx/export/export.cpp
+++ b/platform/osx/export/export.cpp
@@ -31,11 +31,11 @@
#include "export.h"
#include "core/config/project_settings.h"
+#include "core/io/dir_access.h"
+#include "core/io/file_access.h"
#include "core/io/marshalls.h"
#include "core/io/resource_saver.h"
#include "core/io/zip_io.h"
-#include "core/os/dir_access.h"
-#include "core/os/file_access.h"
#include "core/os/os.h"
#include "core/version.h"
#include "editor/editor_export.h"
@@ -56,7 +56,7 @@ class EditorExportPlatformOSX : public EditorExportPlatform {
void _make_icon(const Ref<Image> &p_icon, Vector<uint8_t> &p_data);
Error _notarize(const Ref<EditorExportPreset> &p_preset, const String &p_path);
- Error _code_sign(const Ref<EditorExportPreset> &p_preset, const String &p_path);
+ Error _code_sign(const Ref<EditorExportPreset> &p_preset, const String &p_path, const String &p_ent_path);
Error _create_dmg(const String &p_dmg_path, const String &p_pkg_name, const String &p_app_path_name);
void _zip_folder_recursive(zipFile &p_zip, const String &p_root_path, const String &p_folder, const String &p_pkg_name);
@@ -114,7 +114,7 @@ public:
virtual void get_platform_features(List<String> *r_features) override {
r_features->push_back("pc");
r_features->push_back("s3tc");
- r_features->push_back("OSX");
+ r_features->push_back("macOS");
}
virtual void resolve_platform_feature_priorities(const Ref<EditorExportPreset> &p_preset, Set<String> &p_features) override {
@@ -147,6 +147,7 @@ void EditorExportPlatformOSX::get_export_options(List<ExportOption> *r_options)
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/icon", PROPERTY_HINT_FILE, "*.png,*.icns"), ""));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/bundle_identifier", PROPERTY_HINT_PLACEHOLDER_TEXT, "com.example.game"), ""));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/signature"), ""));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/app_category", PROPERTY_HINT_ENUM, "Business,Developer-tools,Education,Entertainment,Finance,Games,Action-games,Adventure-games,Arcade-games,Board-games,Card-games,Casino-games,Dice-games,Educational-games,Family-games,Kids-games,Music-games,Puzzle-games,Racing-games,Role-playing-games,Simulation-games,Sports-games,Strategy-games,Trivia-games,Word-games,Graphics-design,Healthcare-fitness,Lifestyle,Medical,Music,News,Photography,Productivity,Reference,Social-networking,Sports,Travel,Utilities,Video,Weather"), "Games"));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/short_version"), "1.0"));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/version"), "1.0"));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/copyright"), ""));
@@ -155,11 +156,39 @@ void EditorExportPlatformOSX::get_export_options(List<ExportOption> *r_options)
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "privacy/microphone_usage_description", PROPERTY_HINT_PLACEHOLDER_TEXT, "Provide a message if you need to use the microphone"), ""));
#ifdef OSX_ENABLED
- r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/enable"), false));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/enable"), true));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "codesign/identity", PROPERTY_HINT_PLACEHOLDER_TEXT, "Type: Name (ID)"), ""));
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/timestamp"), true));
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/hardened_runtime"), true));
- r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "codesign/entitlements", PROPERTY_HINT_GLOBAL_FILE, "*.plist"), ""));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/replace_existing_signature"), true));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "codesign/entitlements/custom_file", PROPERTY_HINT_GLOBAL_FILE, "*.plist"), ""));
+
+ if (!Engine::get_singleton()->has_singleton("GodotSharp")) {
+ // These entitlements are required to run managed code, and are always enabled in Mono builds.
+ r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/entitlements/allow_jit_code_execution"), false));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/entitlements/allow_unsigned_executable_memory"), false));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/entitlements/allow_dyld_environment_variables"), false));
+ }
+
+ r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/entitlements/disable_library_validation"), false));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/entitlements/audio_input"), false));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/entitlements/camera"), false));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/entitlements/location"), false));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/entitlements/address_book"), false));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/entitlements/calendars"), false));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/entitlements/photos_library"), false));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/entitlements/apple_events"), false));
+
+ r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/entitlements/app_sandbox/enabled"), false));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/entitlements/app_sandbox/network_server"), false));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/entitlements/app_sandbox/network_client"), false));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/entitlements/app_sandbox/device_usb"), false));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/entitlements/app_sandbox/device_bluetooth"), false));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "codesign/entitlements/app_sandbox/files_downloads", PROPERTY_HINT_ENUM, "No,Read-only,Read-write"), 0));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "codesign/entitlements/app_sandbox/files_pictures", PROPERTY_HINT_ENUM, "No,Read-only,Read-write"), 0));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "codesign/entitlements/app_sandbox/files_music", PROPERTY_HINT_ENUM, "No,Read-only,Read-write"), 0));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "codesign/entitlements/app_sandbox/files_movies", PROPERTY_HINT_ENUM, "No,Read-only,Read-write"), 0));
+
r_options->push_back(ExportOption(PropertyInfo(Variant::PACKED_STRING_ARRAY, "codesign/custom_options"), PackedStringArray()));
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "notarization/enable"), false));
@@ -191,7 +220,7 @@ void _rgba8_to_packbits_encode(int p_ch, int p_size, Vector<uint8_t> &p_source,
if ((p_source.ptr()[(i + 1) * 4 + p_ch] == cur) && (p_source.ptr()[(i + 2) * 4 + p_ch] == cur)) {
if (buf_size > 0) {
result.write[res_size++] = (uint8_t)(buf_size - 1);
- copymem(&result.write[res_size], &buf, buf_size);
+ memcpy(&result.write[res_size], &buf, buf_size);
res_size += buf_size;
buf_size = 0;
}
@@ -217,7 +246,7 @@ void _rgba8_to_packbits_encode(int p_ch, int p_size, Vector<uint8_t> &p_source,
buf[buf_size++] = cur;
if (buf_size == 128) {
result.write[res_size++] = (uint8_t)(buf_size - 1);
- copymem(&result.write[res_size], &buf, buf_size);
+ memcpy(&result.write[res_size], &buf, buf_size);
res_size += buf_size;
buf_size = 0;
}
@@ -225,7 +254,7 @@ void _rgba8_to_packbits_encode(int p_ch, int p_size, Vector<uint8_t> &p_source,
} else {
buf[buf_size++] = cur;
result.write[res_size++] = (uint8_t)(buf_size - 1);
- copymem(&result.write[res_size], &buf, buf_size);
+ memcpy(&result.write[res_size], &buf, buf_size);
res_size += buf_size;
buf_size = 0;
}
@@ -235,7 +264,7 @@ void _rgba8_to_packbits_encode(int p_ch, int p_size, Vector<uint8_t> &p_source,
int ofs = p_dest.size();
p_dest.resize(p_dest.size() + res_size);
- copymem(&p_dest.write[ofs], result.ptr(), res_size);
+ memcpy(&p_dest.write[ofs], result.ptr(), res_size);
}
void EditorExportPlatformOSX::_make_icon(const Ref<Image> &p_icon, Vector<uint8_t> &p_data) {
@@ -277,7 +306,7 @@ void EditorExportPlatformOSX::_make_icon(const Ref<Image> &p_icon, Vector<uint8_
if (icon_infos[i].is_png) {
// Encode PNG icon.
it->create_from_image(copy);
- String path = EditorSettings::get_singleton()->get_cache_dir().plus_file("icon.png");
+ String path = EditorPaths::get_singleton()->get_cache_dir().plus_file("icon.png");
ResourceSaver::save(path, it);
FileAccess *f = FileAccess::open(path, FileAccess::READ);
@@ -288,13 +317,13 @@ void EditorExportPlatformOSX::_make_icon(const Ref<Image> &p_icon, Vector<uint8_
}
int ofs = data.size();
- uint32_t len = f->get_len();
+ uint64_t len = f->get_length();
data.resize(data.size() + len + 8);
f->get_buffer(&data.write[ofs + 8], len);
memdelete(f);
len += 8;
len = BSWAP32(len);
- copymem(&data.write[ofs], icon_infos[i].name, 4);
+ memcpy(&data.write[ofs], icon_infos[i].name, 4);
encode_uint32(len, &data.write[ofs + 4]);
// Clean up generated file.
@@ -314,7 +343,7 @@ void EditorExportPlatformOSX::_make_icon(const Ref<Image> &p_icon, Vector<uint8_
int len = data.size() - ofs;
len = BSWAP32(len);
- copymem(&data.write[ofs], icon_infos[i].name, 4);
+ memcpy(&data.write[ofs], icon_infos[i].name, 4);
encode_uint32(len, &data.write[ofs + 4]);
}
@@ -329,7 +358,7 @@ void EditorExportPlatformOSX::_make_icon(const Ref<Image> &p_icon, Vector<uint8_
}
len += 8;
len = BSWAP32(len);
- copymem(&data.write[ofs], icon_infos[i].mask_name, 4);
+ memcpy(&data.write[ofs], icon_infos[i].mask_name, 4);
encode_uint32(len, &data.write[ofs + 4]);
}
}
@@ -362,6 +391,9 @@ void EditorExportPlatformOSX::_fix_plist(const Ref<EditorExportPreset> &p_preset
strnew += lines[i].replace("$version", p_preset->get("application/version")) + "\n";
} else if (lines[i].find("$signature") != -1) {
strnew += lines[i].replace("$signature", p_preset->get("application/signature")) + "\n";
+ } else if (lines[i].find("$app_category") != -1) {
+ String cat = p_preset->get("application/app_category");
+ strnew += lines[i].replace("$app_category", cat.to_lower()) + "\n";
} else if (lines[i].find("$copyright") != -1) {
strnew += lines[i].replace("$copyright", p_preset->get("application/copyright")) + "\n";
} else if (lines[i].find("$highres") != -1) {
@@ -437,7 +469,7 @@ Error EditorExportPlatformOSX::_notarize(const Ref<EditorExportPreset> &p_preset
return OK;
}
-Error EditorExportPlatformOSX::_code_sign(const Ref<EditorExportPreset> &p_preset, const String &p_path) {
+Error EditorExportPlatformOSX::_code_sign(const Ref<EditorExportPreset> &p_preset, const String &p_path, const String &p_ent_path) {
#ifdef OSX_ENABLED
List<String> args;
@@ -449,9 +481,9 @@ Error EditorExportPlatformOSX::_code_sign(const Ref<EditorExportPreset> &p_prese
args.push_back("runtime");
}
- if ((p_preset->get("codesign/entitlements") != "") && (p_path.get_extension() != "dmg")) {
+ if (p_path.get_extension() != "dmg") {
args.push_back("--entitlements");
- args.push_back(p_preset->get("codesign/entitlements"));
+ args.push_back(p_ent_path);
}
PackedStringArray user_args = p_preset->get("codesign/custom_options");
@@ -463,10 +495,18 @@ Error EditorExportPlatformOSX::_code_sign(const Ref<EditorExportPreset> &p_prese
}
args.push_back("-s");
- args.push_back(p_preset->get("codesign/identity"));
+ if (p_preset->get("codesign/identity") == "") {
+ args.push_back("-");
+ } else {
+ args.push_back(p_preset->get("codesign/identity"));
+ }
args.push_back("-v"); /* provide some more feedback */
+ if (p_preset->get("codesign/replace_existing_signature")) {
+ args.push_back("-f");
+ }
+
args.push_back(p_path);
String str;
@@ -578,7 +618,7 @@ Error EditorExportPlatformOSX::export_project(const Ref<EditorExportPreset> &p_p
// Create our application bundle.
String tmp_app_dir_name = pkg_name + ".app";
- String tmp_app_path_name = EditorSettings::get_singleton()->get_cache_dir().plus_file(tmp_app_dir_name);
+ String tmp_app_path_name = EditorPaths::get_singleton()->get_cache_dir().plus_file(tmp_app_dir_name);
print_line("Exporting to " + tmp_app_path_name);
Error err = OK;
@@ -607,6 +647,7 @@ Error EditorExportPlatformOSX::export_project(const Ref<EditorExportPreset> &p_p
// Now process our template.
bool found_binary = false;
int total_size = 0;
+ Vector<String> dylibs_found;
while (ret == UNZ_OK && err == OK) {
bool is_execute = false;
@@ -656,14 +697,14 @@ Error EditorExportPlatformOSX::export_project(const Ref<EditorExportPreset> &p_p
if (iconpath.get_extension() == "icns") {
FileAccess *icon = FileAccess::open(iconpath, FileAccess::READ);
if (icon) {
- data.resize(icon->get_len());
- icon->get_buffer(&data.write[0], icon->get_len());
+ data.resize(icon->get_length());
+ icon->get_buffer(&data.write[0], icon->get_length());
icon->close();
memdelete(icon);
}
} else {
Ref<Image> icon;
- icon.instance();
+ icon.instantiate();
icon->load(iconpath);
if (!icon->is_empty()) {
_make_icon(icon, data);
@@ -678,14 +719,18 @@ Error EditorExportPlatformOSX::export_project(const Ref<EditorExportPreset> &p_p
ret = unzGoToNextFile(src_pkg_zip);
continue; // skip
}
- file = file.replace("/data.mono.osx.64.release_debug/", "/data_" + pkg_name + "/");
+ file = file.replace("/data.mono.osx.64.release_debug/", "/GodotSharp/");
}
if (file.find("/data.mono.osx.64.release/") != -1) {
if (p_debug) {
ret = unzGoToNextFile(src_pkg_zip);
continue; // skip
}
- file = file.replace("/data.mono.osx.64.release/", "/data_" + pkg_name + "/");
+ file = file.replace("/data.mono.osx.64.release/", "/GodotSharp/");
+ }
+
+ if (file.ends_with(".dylib")) {
+ dylibs_found.push_back(file);
}
print_line("ADDING: " + file + " size: " + itos(data.size()));
@@ -735,22 +780,173 @@ Error EditorExportPlatformOSX::export_project(const Ref<EditorExportPreset> &p_p
// See if we can code sign our new package.
bool sign_enabled = p_preset->get("codesign/enable");
+ String ent_path = p_preset->get("codesign/entitlements/custom_file");
+ if (sign_enabled && (ent_path == "")) {
+ ent_path = EditorPaths::get_singleton()->get_cache_dir().plus_file(pkg_name + ".entitlements");
+
+ FileAccess *ent_f = FileAccess::open(ent_path, FileAccess::WRITE);
+ if (ent_f) {
+ ent_f->store_line("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
+ ent_f->store_line("<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">");
+ ent_f->store_line("<plist version=\"1.0\">");
+ ent_f->store_line("<dict>");
+ if (Engine::get_singleton()->has_singleton("GodotSharp")) {
+ // These entitlements are required to run managed code, and are always enabled in Mono builds.
+ ent_f->store_line("<key>com.apple.security.cs.allow-jit</key>");
+ ent_f->store_line("<true/>");
+ ent_f->store_line("<key>com.apple.security.cs.allow-unsigned-executable-memory</key>");
+ ent_f->store_line("<true/>");
+ ent_f->store_line("<key>com.apple.security.cs.allow-dyld-environment-variables</key>");
+ ent_f->store_line("<true/>");
+ } else {
+ if ((bool)p_preset->get("codesign/entitlements/allow_jit_code_execution")) {
+ ent_f->store_line("<key>com.apple.security.cs.allow-jit</key>");
+ ent_f->store_line("<true/>");
+ }
+ if ((bool)p_preset->get("codesign/entitlements/allow_unsigned_executable_memory")) {
+ ent_f->store_line("<key>com.apple.security.cs.allow-unsigned-executable-memory</key>");
+ ent_f->store_line("<true/>");
+ }
+ if ((bool)p_preset->get("codesign/entitlements/allow_dyld_environment_variables")) {
+ ent_f->store_line("<key>com.apple.security.cs.allow-dyld-environment-variables</key>");
+ ent_f->store_line("<true/>");
+ }
+ }
+
+ if ((bool)p_preset->get("codesign/entitlements/disable_library_validation")) {
+ ent_f->store_line("<key>com.apple.security.cs.disable-library-validation</key>");
+ ent_f->store_line("<true/>");
+ }
+ if ((bool)p_preset->get("codesign/entitlements/audio_input")) {
+ ent_f->store_line("<key>com.apple.security.device.audio-input</key>");
+ ent_f->store_line("<true/>");
+ }
+ if ((bool)p_preset->get("codesign/entitlements/camera")) {
+ ent_f->store_line("<key>com.apple.security.device.camera</key>");
+ ent_f->store_line("<true/>");
+ }
+ if ((bool)p_preset->get("codesign/entitlements/location")) {
+ ent_f->store_line("<key>com.apple.security.personal-information.location</key>");
+ ent_f->store_line("<true/>");
+ }
+ if ((bool)p_preset->get("codesign/entitlements/address_book")) {
+ ent_f->store_line("<key>com.apple.security.personal-information.addressbook</key>");
+ ent_f->store_line("<true/>");
+ }
+ if ((bool)p_preset->get("codesign/entitlements/calendars")) {
+ ent_f->store_line("<key>com.apple.security.personal-information.calendars</key>");
+ ent_f->store_line("<true/>");
+ }
+ if ((bool)p_preset->get("codesign/entitlements/photos_library")) {
+ ent_f->store_line("<key>com.apple.security.personal-information.photos-library</key>");
+ ent_f->store_line("<true/>");
+ }
+ if ((bool)p_preset->get("codesign/entitlements/apple_events")) {
+ ent_f->store_line("<key>com.apple.security.automation.apple-events</key>");
+ ent_f->store_line("<true/>");
+ }
+
+ if ((bool)p_preset->get("codesign/entitlements/app_sandbox/enabled")) {
+ ent_f->store_line("<key>com.apple.security.app-sandbox</key>");
+ ent_f->store_line("<true/>");
+
+ if ((bool)p_preset->get("codesign/entitlements/app_sandbox/network_server")) {
+ ent_f->store_line("<key>com.apple.security.network.server</key>");
+ ent_f->store_line("<true/>");
+ }
+ if ((bool)p_preset->get("codesign/entitlements/app_sandbox/network_client")) {
+ ent_f->store_line("<key>com.apple.security.network.client</key>");
+ ent_f->store_line("<true/>");
+ }
+ if ((bool)p_preset->get("codesign/entitlements/app_sandbox/device_usb")) {
+ ent_f->store_line("<key>com.apple.security.device.usb</key>");
+ ent_f->store_line("<true/>");
+ }
+ if ((bool)p_preset->get("codesign/entitlements/app_sandbox/device_bluetooth")) {
+ ent_f->store_line("<key>com.apple.security.device.bluetooth</key>");
+ ent_f->store_line("<true/>");
+ }
+ if ((int)p_preset->get("codesign/entitlements/app_sandbox/files_downloads") == 1) {
+ ent_f->store_line("<key>com.apple.security.files.downloads.read-only</key>");
+ ent_f->store_line("<true/>");
+ }
+ if ((int)p_preset->get("codesign/entitlements/app_sandbox/files_downloads") == 2) {
+ ent_f->store_line("<key>com.apple.security.files.downloads.read-write</key>");
+ ent_f->store_line("<true/>");
+ }
+ if ((int)p_preset->get("codesign/entitlements/app_sandbox/files_pictures") == 1) {
+ ent_f->store_line("<key>com.apple.security.files.pictures.read-only</key>");
+ ent_f->store_line("<true/>");
+ }
+ if ((int)p_preset->get("codesign/entitlements/app_sandbox/files_pictures") == 2) {
+ ent_f->store_line("<key>com.apple.security.files.pictures.read-write</key>");
+ ent_f->store_line("<true/>");
+ }
+ if ((int)p_preset->get("codesign/entitlements/app_sandbox/files_music") == 1) {
+ ent_f->store_line("<key>com.apple.security.files.music.read-only</key>");
+ ent_f->store_line("<true/>");
+ }
+ if ((int)p_preset->get("codesign/entitlements/app_sandbox/files_music") == 2) {
+ ent_f->store_line("<key>com.apple.security.files.music.read-write</key>");
+ ent_f->store_line("<true/>");
+ }
+ if ((int)p_preset->get("codesign/entitlements/app_sandbox/files_movies") == 1) {
+ ent_f->store_line("<key>com.apple.security.files.movies.read-only</key>");
+ ent_f->store_line("<true/>");
+ }
+ if ((int)p_preset->get("codesign/entitlements/app_sandbox/files_movies") == 2) {
+ ent_f->store_line("<key>com.apple.security.files.movies.read-write</key>");
+ ent_f->store_line("<true/>");
+ }
+ }
+
+ ent_f->store_line("</dict>");
+ ent_f->store_line("</plist>");
+
+ ent_f->close();
+ memdelete(ent_f);
+ } else {
+ err = ERR_CANT_CREATE;
+ }
+ }
+
if (err == OK) {
DirAccess *da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
for (int i = 0; i < shared_objects.size(); i++) {
- err = da->copy(shared_objects[i].path, tmp_app_path_name + "/Contents/Frameworks/" + shared_objects[i].path.get_file());
+ String src_path = ProjectSettings::get_singleton()->globalize_path(shared_objects[i].path);
+ if (da->dir_exists(src_path)) {
+#ifndef UNIX_ENABLED
+ WARN_PRINT("Relative symlinks are not supported, exported " + src_path.get_file() + " might be broken!");
+#endif
+ print_verbose("export framework: " + src_path + " -> " + tmp_app_path_name + "/Contents/Frameworks/" + src_path.get_file());
+ err = da->make_dir_recursive(tmp_app_path_name + "/Contents/Frameworks/" + src_path.get_file());
+ if (err == OK) {
+ err = da->copy_dir(src_path, tmp_app_path_name + "/Contents/Frameworks/" + src_path.get_file(), -1, true);
+ }
+ } else {
+ print_verbose("export dylib: " + src_path + " -> " + tmp_app_path_name + "/Contents/Frameworks/" + src_path.get_file());
+ err = da->copy(src_path, tmp_app_path_name + "/Contents/Frameworks/" + src_path.get_file());
+ }
if (err == OK && sign_enabled) {
- err = _code_sign(p_preset, tmp_app_path_name + "/Contents/Frameworks/" + shared_objects[i].path.get_file());
+ err = _code_sign(p_preset, tmp_app_path_name + "/Contents/Frameworks/" + src_path.get_file(), ent_path);
}
}
memdelete(da);
}
+ if (sign_enabled) {
+ for (int i = 0; i < dylibs_found.size(); i++) {
+ if (err == OK) {
+ err = _code_sign(p_preset, tmp_app_path_name + "/" + dylibs_found[i], ent_path);
+ }
+ }
+ }
+
if (err == OK && sign_enabled) {
if (ep.step("Code signing bundle", 2)) {
return ERR_SKIP;
}
- err = _code_sign(p_preset, tmp_app_path_name + "/Contents/MacOS/" + pkg_name);
+ err = _code_sign(p_preset, tmp_app_path_name + "/Contents/MacOS/" + pkg_name, ent_path);
}
if (export_format == "dmg") {
@@ -766,7 +962,7 @@ Error EditorExportPlatformOSX::export_project(const Ref<EditorExportPreset> &p_p
if (ep.step("Code signing DMG", 3)) {
return ERR_SKIP;
}
- err = _code_sign(p_preset, p_path);
+ err = _code_sign(p_preset, p_path, ent_path);
}
} else {
// Create ZIP.
@@ -782,7 +978,7 @@ Error EditorExportPlatformOSX::export_project(const Ref<EditorExportPreset> &p_p
zlib_filefunc_def io_dst = zipio_create_io_from_file(&dst_f);
zipFile zip = zipOpen2(p_path.utf8().get_data(), APPEND_STATUS_CREATE, nullptr, &io_dst);
- _zip_folder_recursive(zip, EditorSettings::get_singleton()->get_cache_dir(), pkg_name + ".app", pkg_name);
+ _zip_folder_recursive(zip, EditorPaths::get_singleton()->get_cache_dir(), pkg_name + ".app", pkg_name);
zipClose(zip, nullptr);
}
@@ -816,7 +1012,48 @@ void EditorExportPlatformOSX::_zip_folder_recursive(zipFile &p_zip, const String
if (f == "." || f == "..") {
continue;
}
- if (da->current_is_dir()) {
+ if (da->is_link(f)) {
+ OS::Time time = OS::get_singleton()->get_time();
+ OS::Date date = OS::get_singleton()->get_date();
+
+ zip_fileinfo zipfi;
+ zipfi.tmz_date.tm_hour = time.hour;
+ zipfi.tmz_date.tm_mday = date.day;
+ zipfi.tmz_date.tm_min = time.minute;
+ zipfi.tmz_date.tm_mon = date.month - 1; // Note: "tm" month range - 0..11, Godot month range - 1..12, http://www.cplusplus.com/reference/ctime/tm/
+ zipfi.tmz_date.tm_sec = time.second;
+ zipfi.tmz_date.tm_year = date.year;
+ zipfi.dosDate = 0;
+ // 0120000: symbolic link type
+ // 0000755: permissions rwxr-xr-x
+ // 0000644: permissions rw-r--r--
+ uint32_t _mode = 0120644;
+ zipfi.external_fa = (_mode << 16L) | !(_mode & 0200);
+ zipfi.internal_fa = 0;
+
+ zipOpenNewFileInZip4(p_zip,
+ p_folder.plus_file(f).utf8().get_data(),
+ &zipfi,
+ nullptr,
+ 0,
+ nullptr,
+ 0,
+ nullptr,
+ Z_DEFLATED,
+ Z_DEFAULT_COMPRESSION,
+ 0,
+ -MAX_WBITS,
+ DEF_MEM_LEVEL,
+ Z_DEFAULT_STRATEGY,
+ nullptr,
+ 0,
+ 0x0314, // "version made by", 0x03 - Unix, 0x14 - ZIP specification version 2.0, required to store Unix file permissions
+ 0);
+
+ String target = da->read_link(f);
+ zipWriteInFileInZip(p_zip, target.utf8().get_data(), target.utf8().size());
+ zipCloseFileInZip(p_zip);
+ } else if (da->current_is_dir()) {
_zip_folder_recursive(p_zip, p_root_path, p_folder.plus_file(f), p_pkg_name);
} else {
bool is_executable = (p_folder.ends_with("MacOS") && (f == p_pkg_name));
@@ -827,9 +1064,9 @@ void EditorExportPlatformOSX::_zip_folder_recursive(zipFile &p_zip, const String
zip_fileinfo zipfi;
zipfi.tmz_date.tm_hour = time.hour;
zipfi.tmz_date.tm_mday = date.day;
- zipfi.tmz_date.tm_min = time.min;
+ zipfi.tmz_date.tm_min = time.minute;
zipfi.tmz_date.tm_mon = date.month - 1; // Note: "tm" month range - 0..11, Godot month range - 1..12, http://www.cplusplus.com/reference/ctime/tm/
- zipfi.tmz_date.tm_sec = time.sec;
+ zipfi.tmz_date.tm_sec = time.second;
zipfi.tmz_date.tm_year = date.year;
zipfi.dosDate = 0;
// 0100000: regular file type
@@ -900,12 +1137,6 @@ bool EditorExportPlatformOSX::can_export(const Ref<EditorExportPreset> &p_preset
}
bool sign_enabled = p_preset->get("codesign/enable");
- if (sign_enabled) {
- if (p_preset->get("codesign/identity") == "") {
- err += TTR("Codesign: identity not specified.") + "\n";
- valid = false;
- }
- }
bool noto_enabled = p_preset->get("notarization/enable");
if (noto_enabled) {
if (!sign_enabled) {
@@ -935,7 +1166,7 @@ bool EditorExportPlatformOSX::can_export(const Ref<EditorExportPreset> &p_preset
EditorExportPlatformOSX::EditorExportPlatformOSX() {
Ref<Image> img = memnew(Image(_osx_logo));
- logo.instance();
+ logo.instantiate();
logo->create_from_image(img);
}
@@ -944,7 +1175,7 @@ EditorExportPlatformOSX::~EditorExportPlatformOSX() {
void register_osx_exporter() {
Ref<EditorExportPlatformOSX> platform;
- platform.instance();
+ platform.instantiate();
EditorExport::get_singleton()->add_export_platform(platform);
}
diff --git a/platform/osx/joypad_osx.cpp b/platform/osx/joypad_osx.cpp
index 0b6a0e20a6..126ebc1908 100644
--- a/platform/osx/joypad_osx.cpp
+++ b/platform/osx/joypad_osx.cpp
@@ -390,38 +390,38 @@ bool joypad::check_ff_features() {
static int process_hat_value(int p_min, int p_max, int p_value) {
int range = (p_max - p_min + 1);
int value = p_value - p_min;
- int hat_value = Input::HAT_MASK_CENTER;
+ int hat_value = HatMask::HAT_MASK_CENTER;
if (range == 4) {
value *= 2;
}
switch (value) {
case 0:
- hat_value = Input::HAT_MASK_UP;
+ hat_value = (HatMask)HatMask::HAT_MASK_UP;
break;
case 1:
- hat_value = Input::HAT_MASK_UP | Input::HAT_MASK_RIGHT;
+ hat_value = (HatMask)(HatMask::HAT_MASK_UP | HatMask::HAT_MASK_RIGHT);
break;
case 2:
- hat_value = Input::HAT_MASK_RIGHT;
+ hat_value = (HatMask)HatMask::HAT_MASK_RIGHT;
break;
case 3:
- hat_value = Input::HAT_MASK_DOWN | Input::HAT_MASK_RIGHT;
+ hat_value = (HatMask)(HatMask::HAT_MASK_DOWN | HatMask::HAT_MASK_RIGHT);
break;
case 4:
- hat_value = Input::HAT_MASK_DOWN;
+ hat_value = (HatMask)HatMask::HAT_MASK_DOWN;
break;
case 5:
- hat_value = Input::HAT_MASK_DOWN | Input::HAT_MASK_LEFT;
+ hat_value = (HatMask)(HatMask::HAT_MASK_DOWN | HatMask::HAT_MASK_LEFT);
break;
case 6:
- hat_value = Input::HAT_MASK_LEFT;
+ hat_value = (HatMask)HatMask::HAT_MASK_LEFT;
break;
case 7:
- hat_value = Input::HAT_MASK_UP | Input::HAT_MASK_LEFT;
+ hat_value = (HatMask)(HatMask::HAT_MASK_UP | HatMask::HAT_MASK_LEFT);
break;
default:
- hat_value = Input::HAT_MASK_CENTER;
+ hat_value = (HatMask)HatMask::HAT_MASK_CENTER;
break;
}
return hat_value;
@@ -433,8 +433,8 @@ void JoypadOSX::poll_joypads() const {
}
}
-static const Input::JoyAxis axis_correct(int p_value, int p_min, int p_max) {
- Input::JoyAxis jx;
+static const Input::JoyAxisValue axis_correct(int p_value, int p_min, int p_max) {
+ Input::JoyAxisValue jx;
if (p_min < 0) {
jx.min = -1;
if (p_value < 0) {
@@ -458,17 +458,17 @@ void JoypadOSX::process_joypads() {
for (int j = 0; j < joy.axis_elements.size(); j++) {
rec_element &elem = joy.axis_elements.write[j];
int value = joy.get_hid_element_state(&elem);
- input->joy_axis(joy.id, j, axis_correct(value, elem.min, elem.max));
+ input->joy_axis(joy.id, (JoyAxis)j, axis_correct(value, elem.min, elem.max));
}
for (int j = 0; j < joy.button_elements.size(); j++) {
int value = joy.get_hid_element_state(&joy.button_elements.write[j]);
- input->joy_button(joy.id, j, (value >= 1));
+ input->joy_button(joy.id, (JoyButton)j, (value >= 1));
}
for (int j = 0; j < joy.hat_elements.size(); j++) {
rec_element &elem = joy.hat_elements.write[j];
int value = joy.get_hid_element_state(&elem);
int hat_value = process_hat_value(elem.min, elem.max, value);
- input->joy_hat(joy.id, hat_value);
+ input->joy_hat(joy.id, (HatMask)hat_value);
}
if (joy.ffservice) {
diff --git a/platform/osx/os_osx.mm b/platform/osx/os_osx.mm
index 7b5daf5bfb..b65d84d900 100644
--- a/platform/osx/os_osx.mm
+++ b/platform/osx/os_osx.mm
@@ -101,7 +101,7 @@ String OS_OSX::get_unique_id() const {
if (serial_number.is_empty()) {
io_service_t platformExpert = IOServiceGetMatchingService(kIOMasterPortDefault, IOServiceMatching("IOPlatformExpertDevice"));
- CFStringRef serialNumberAsCFString = NULL;
+ CFStringRef serialNumberAsCFString = nullptr;
if (platformExpert) {
serialNumberAsCFString = (CFStringRef)IORegistryEntryCreateCFProperty(platformExpert, CFSTR(kIOPlatformSerialNumberKey), kCFAllocatorDefault, 0);
IOObjectRelease(platformExpert);
@@ -158,7 +158,7 @@ void OS_OSX::delete_main_loop() {
if (!main_loop)
return;
memdelete(main_loop);
- main_loop = NULL;
+ main_loop = nullptr;
}
String OS_OSX::get_name() const {
@@ -188,31 +188,45 @@ MainLoop *OS_OSX::get_main_loop() const {
}
String OS_OSX::get_config_path() const {
+ // The XDG Base Directory specification technically only applies on Linux/*BSD, but it doesn't hurt to support it on macOS as well.
if (has_environment("XDG_CONFIG_HOME")) {
- return get_environment("XDG_CONFIG_HOME");
- } else if (has_environment("HOME")) {
+ if (get_environment("XDG_CONFIG_HOME").is_absolute_path()) {
+ return get_environment("XDG_CONFIG_HOME");
+ } else {
+ WARN_PRINT_ONCE("`XDG_CONFIG_HOME` is a relative path. Ignoring its value and falling back to `$HOME/Library/Application Support` or `.` per the XDG Base Directory specification.");
+ }
+ }
+ if (has_environment("HOME")) {
return get_environment("HOME").plus_file("Library/Application Support");
- } else {
- return ".";
}
+ return ".";
}
String OS_OSX::get_data_path() const {
+ // The XDG Base Directory specification technically only applies on Linux/*BSD, but it doesn't hurt to support it on macOS as well.
if (has_environment("XDG_DATA_HOME")) {
- return get_environment("XDG_DATA_HOME");
- } else {
- return get_config_path();
+ if (get_environment("XDG_DATA_HOME").is_absolute_path()) {
+ return get_environment("XDG_DATA_HOME");
+ } else {
+ WARN_PRINT_ONCE("`XDG_DATA_HOME` is a relative path. Ignoring its value and falling back to `get_config_path()` per the XDG Base Directory specification.");
+ }
}
+ return get_config_path();
}
String OS_OSX::get_cache_path() const {
+ // The XDG Base Directory specification technically only applies on Linux/*BSD, but it doesn't hurt to support it on macOS as well.
if (has_environment("XDG_CACHE_HOME")) {
- return get_environment("XDG_CACHE_HOME");
- } else if (has_environment("HOME")) {
+ if (get_environment("XDG_CACHE_HOME").is_absolute_path()) {
+ return get_environment("XDG_CACHE_HOME");
+ } else {
+ WARN_PRINT_ONCE("`XDG_CACHE_HOME` is a relative path. Ignoring its value and falling back to `$HOME/Libary/Caches` or `get_config_path()` per the XDG Base Directory specification.");
+ }
+ }
+ if (has_environment("HOME")) {
return get_environment("HOME").plus_file("Library/Caches");
- } else {
- return get_config_path();
}
+ return get_config_path();
}
String OS_OSX::get_bundle_resource_dir() const {
@@ -346,7 +360,7 @@ Error OS_OSX::move_to_trash(const String &p_path) {
}
OS_OSX::OS_OSX() {
- main_loop = NULL;
+ main_loop = nullptr;
force_quit = false;
Vector<Logger *> loggers;
diff --git a/platform/osx/vulkan_context_osx.h b/platform/osx/vulkan_context_osx.h
index 8b6a75adfb..22d43688a3 100644
--- a/platform/osx/vulkan_context_osx.h
+++ b/platform/osx/vulkan_context_osx.h
@@ -38,7 +38,7 @@ class VulkanContextOSX : public VulkanContext {
virtual const char *_get_platform_surface_extension() const;
public:
- Error window_create(DisplayServer::WindowID p_window_id, id p_window, int p_width, int p_height);
+ Error window_create(DisplayServer::WindowID p_window_id, DisplayServer::VSyncMode p_vsync_mode, id p_window, int p_width, int p_height);
VulkanContextOSX();
~VulkanContextOSX();
diff --git a/platform/osx/vulkan_context_osx.mm b/platform/osx/vulkan_context_osx.mm
index 75a4fc990f..7e18e177c1 100644
--- a/platform/osx/vulkan_context_osx.mm
+++ b/platform/osx/vulkan_context_osx.mm
@@ -35,17 +35,17 @@ const char *VulkanContextOSX::_get_platform_surface_extension() const {
return VK_MVK_MACOS_SURFACE_EXTENSION_NAME;
}
-Error VulkanContextOSX::window_create(DisplayServer::WindowID p_window_id, id p_window, int p_width, int p_height) {
+Error VulkanContextOSX::window_create(DisplayServer::WindowID p_window_id, DisplayServer::VSyncMode p_vsync_mode, id p_window, int p_width, int p_height) {
VkMacOSSurfaceCreateInfoMVK createInfo;
createInfo.sType = VK_STRUCTURE_TYPE_MACOS_SURFACE_CREATE_INFO_MVK;
- createInfo.pNext = NULL;
+ createInfo.pNext = nullptr;
createInfo.flags = 0;
createInfo.pView = p_window;
VkSurfaceKHR surface;
- VkResult err = vkCreateMacOSSurfaceMVK(_get_instance(), &createInfo, NULL, &surface);
+ VkResult err = vkCreateMacOSSurfaceMVK(_get_instance(), &createInfo, nullptr, &surface);
ERR_FAIL_COND_V(err, ERR_CANT_CREATE);
- return _window_create(p_window_id, surface, p_width, p_height);
+ return _window_create(p_window_id, p_vsync_mode, surface, p_width, p_height);
}
VulkanContextOSX::VulkanContextOSX() {
diff --git a/platform/server/SCsub b/platform/server/SCsub
deleted file mode 100644
index 15b9af4d25..0000000000
--- a/platform/server/SCsub
+++ /dev/null
@@ -1,16 +0,0 @@
-#!/usr/bin/env python
-
-import sys
-
-Import("env")
-
-common_server = [
- "os_server.cpp",
-]
-
-if sys.platform == "darwin":
- common_server.append("#platform/osx/crash_handler_osx.mm")
-else:
- common_server.append("#platform/x11/crash_handler_x11.cpp")
-
-prog = env.add_program("#bin/godot_server", ["godot_server.cpp"] + common_server)
diff --git a/platform/server/detect.py b/platform/server/detect.py
deleted file mode 100644
index 5be7e81e7a..0000000000
--- a/platform/server/detect.py
+++ /dev/null
@@ -1,277 +0,0 @@
-import os
-import platform
-import sys
-
-# This file is mostly based on platform/x11/detect.py.
-# If editing this file, make sure to apply relevant changes here too.
-
-
-def is_active():
- return True
-
-
-def get_name():
- return "Server"
-
-
-def get_program_suffix():
- if sys.platform == "darwin":
- return "osx"
- return "linuxbsd"
-
-
-def can_build():
- if os.name != "posix":
- return False
-
- return True
-
-
-def get_opts():
- from SCons.Variables import BoolVariable, EnumVariable
-
- return [
- BoolVariable("use_llvm", "Use the LLVM compiler", False),
- BoolVariable("use_static_cpp", "Link libgcc and libstdc++ statically for better portability", True),
- BoolVariable("use_coverage", "Test Godot coverage", False),
- BoolVariable("use_ubsan", "Use LLVM/GCC compiler undefined behavior sanitizer (UBSAN)", False),
- BoolVariable("use_asan", "Use LLVM/GCC compiler address sanitizer (ASAN))", False),
- BoolVariable("use_lsan", "Use LLVM/GCC compiler leak sanitizer (LSAN))", False),
- BoolVariable("use_tsan", "Use LLVM/GCC compiler thread sanitizer (TSAN))", False),
- BoolVariable("debug_symbols", "Add debugging symbols to release/release_debug builds", True),
- BoolVariable("separate_debug_symbols", "Create a separate file containing debugging symbols", False),
- BoolVariable("execinfo", "Use libexecinfo on systems where glibc is not available", False),
- ]
-
-
-def get_flags():
- return []
-
-
-def configure(env):
-
- ## Build type
-
- if env["target"] == "release":
- if env["optimize"] == "speed": # optimize for speed (default)
- env.Prepend(CCFLAGS=["-O3"])
- else: # optimize for size
- env.Prepend(CCFLAGS=["-Os"])
-
- if env["debug_symbols"]:
- env.Prepend(CCFLAGS=["-g2"])
-
- elif env["target"] == "release_debug":
- if env["optimize"] == "speed": # optimize for speed (default)
- env.Prepend(CCFLAGS=["-O2"])
- else: # optimize for size
- env.Prepend(CCFLAGS=["-Os"])
- env.Prepend(CPPDEFINES=["DEBUG_ENABLED"])
-
- if env["debug_symbols"]:
- env.Prepend(CCFLAGS=["-g2"])
-
- elif env["target"] == "debug":
- env.Prepend(CCFLAGS=["-g3"])
- env.Prepend(CPPDEFINES=["DEBUG_ENABLED"])
- env.Append(LINKFLAGS=["-rdynamic"])
-
- ## Architecture
-
- is64 = sys.maxsize > 2 ** 32
- if env["bits"] == "default":
- env["bits"] = "64" if is64 else "32"
-
- ## Compiler configuration
-
- if "CXX" in env and "clang" in os.path.basename(env["CXX"]):
- # Convenience check to enforce the use_llvm overrides when CXX is clang(++)
- env["use_llvm"] = True
-
- if env["use_llvm"]:
- if "clang++" not in os.path.basename(env["CXX"]):
- env["CC"] = "clang"
- env["CXX"] = "clang++"
- env.extra_suffix = ".llvm" + env.extra_suffix
- env.Append(LIBS=["atomic"])
-
- if env["use_coverage"]:
- env.Append(CCFLAGS=["-ftest-coverage", "-fprofile-arcs"])
- env.Append(LINKFLAGS=["-ftest-coverage", "-fprofile-arcs"])
-
- if env["use_ubsan"] or env["use_asan"] or env["use_lsan"] or env["use_tsan"]:
- env.extra_suffix += "s"
-
- if env["use_ubsan"]:
- env.Append(CCFLAGS=["-fsanitize=undefined"])
- env.Append(LINKFLAGS=["-fsanitize=undefined"])
-
- if env["use_asan"]:
- env.Append(CCFLAGS=["-fsanitize=address"])
- env.Append(LINKFLAGS=["-fsanitize=address"])
-
- if env["use_lsan"]:
- env.Append(CCFLAGS=["-fsanitize=leak"])
- env.Append(LINKFLAGS=["-fsanitize=leak"])
-
- if env["use_tsan"]:
- env.Append(CCFLAGS=["-fsanitize=thread"])
- env.Append(LINKFLAGS=["-fsanitize=thread"])
-
- if env["use_lto"]:
- env.Append(CCFLAGS=["-flto"])
- if not env["use_llvm"] and env.GetOption("num_jobs") > 1:
- env.Append(LINKFLAGS=["-flto=" + str(env.GetOption("num_jobs"))])
- else:
- env.Append(LINKFLAGS=["-flto"])
- if not env["use_llvm"]:
- env["RANLIB"] = "gcc-ranlib"
- env["AR"] = "gcc-ar"
-
- env.Append(CCFLAGS=["-pipe"])
- env.Append(LINKFLAGS=["-pipe"])
-
- ## Dependencies
-
- # FIXME: Check for existence of the libs before parsing their flags with pkg-config
-
- # freetype depends on libpng and zlib, so bundling one of them while keeping others
- # as shared libraries leads to weird issues
- if (
- env["builtin_freetype"]
- or env["builtin_libpng"]
- or env["builtin_zlib"]
- or env["builtin_graphite"]
- or env["builtin_harfbuzz"]
- ):
- env["builtin_freetype"] = True
- env["builtin_libpng"] = True
- env["builtin_zlib"] = True
- env["builtin_graphite"] = True
- env["builtin_harfbuzz"] = True
-
- if not env["builtin_freetype"]:
- env.ParseConfig("pkg-config freetype2 --cflags --libs")
-
- if not env["builtin_graphite"]:
- env.ParseConfig("pkg-config graphite2 --cflags --libs")
-
- if not env["builtin_icu"]:
- env.ParseConfig("pkg-config icu-uc --cflags --libs")
-
- if not env["builtin_harfbuzz"]:
- env.ParseConfig("pkg-config harfbuzz harfbuzz-icu --cflags --libs")
-
- if not env["builtin_libpng"]:
- env.ParseConfig("pkg-config libpng16 --cflags --libs")
-
- if not env["builtin_bullet"]:
- # We need at least version 2.89
- import subprocess
-
- bullet_version = subprocess.check_output(["pkg-config", "bullet", "--modversion"]).strip()
- if str(bullet_version) < "2.89":
- # Abort as system bullet was requested but too old
- print(
- "Bullet: System version {0} does not match minimal requirements ({1}). Aborting.".format(
- bullet_version, "2.89"
- )
- )
- sys.exit(255)
- env.ParseConfig("pkg-config bullet --cflags --libs")
-
- if False: # not env['builtin_assimp']:
- # FIXME: Add min version check
- env.ParseConfig("pkg-config assimp --cflags --libs")
-
- if not env["builtin_enet"]:
- env.ParseConfig("pkg-config libenet --cflags --libs")
-
- if not env["builtin_squish"]:
- env.ParseConfig("pkg-config libsquish --cflags --libs")
-
- if not env["builtin_zstd"]:
- env.ParseConfig("pkg-config libzstd --cflags --libs")
-
- # Sound and video libraries
- # Keep the order as it triggers chained dependencies (ogg needed by others, etc.)
-
- if not env["builtin_libtheora"]:
- env["builtin_libogg"] = False # Needed to link against system libtheora
- env["builtin_libvorbis"] = False # Needed to link against system libtheora
- env.ParseConfig("pkg-config theora theoradec --cflags --libs")
- else:
- list_of_x86 = ["x86_64", "x86", "i386", "i586"]
- if any(platform.machine() in s for s in list_of_x86):
- env["x86_libtheora_opt_gcc"] = True
-
- if not env["builtin_libvpx"]:
- env.ParseConfig("pkg-config vpx --cflags --libs")
-
- if not env["builtin_libvorbis"]:
- env["builtin_libogg"] = False # Needed to link against system libvorbis
- env.ParseConfig("pkg-config vorbis vorbisfile --cflags --libs")
-
- if not env["builtin_opus"]:
- env["builtin_libogg"] = False # Needed to link against system opus
- env.ParseConfig("pkg-config opus opusfile --cflags --libs")
-
- if not env["builtin_libogg"]:
- env.ParseConfig("pkg-config ogg --cflags --libs")
-
- if not env["builtin_libwebp"]:
- env.ParseConfig("pkg-config libwebp --cflags --libs")
-
- if not env["builtin_mbedtls"]:
- # mbedTLS does not provide a pkgconfig config yet. See https://github.com/ARMmbed/mbedtls/issues/228
- env.Append(LIBS=["mbedtls", "mbedcrypto", "mbedx509"])
-
- if not env["builtin_wslay"]:
- env.ParseConfig("pkg-config libwslay --cflags --libs")
-
- if not env["builtin_miniupnpc"]:
- # No pkgconfig file so far, hardcode default paths.
- env.Prepend(CPPPATH=["/usr/include/miniupnpc"])
- env.Append(LIBS=["miniupnpc"])
-
- # On Linux wchar_t should be 32-bits
- # 16-bit library shouldn't be required due to compiler optimisations
- if not env["builtin_pcre2"]:
- env.ParseConfig("pkg-config libpcre2-32 --cflags --libs")
-
- ## Flags
-
- # Linkflags below this line should typically stay the last ones
- if not env["builtin_zlib"]:
- env.ParseConfig("pkg-config zlib --cflags --libs")
-
- env.Prepend(CPPPATH=["#platform/server"])
- env.Append(CPPDEFINES=["SERVER_ENABLED", "UNIX_ENABLED"])
-
- if platform.system() == "Darwin":
- env.Append(
- LINKFLAGS=[
- "-framework",
- "Cocoa",
- "-framework",
- "Carbon",
- "-lz",
- "-framework",
- "IOKit",
- ]
- )
-
- env.Append(LIBS=["pthread"])
-
- if platform.system() == "Linux":
- env.Append(LIBS=["dl"])
-
- if platform.system().find("BSD") >= 0:
- env["execinfo"] = True
-
- if env["execinfo"]:
- env.Append(LIBS=["execinfo"])
-
- # Link those statically for portability
- if env["use_static_cpp"]:
- env.Append(LINKFLAGS=["-static-libgcc", "-static-libstdc++"])
diff --git a/platform/server/logo.png b/platform/server/logo.png
deleted file mode 100644
index 8666ada9ca..0000000000
--- a/platform/server/logo.png
+++ /dev/null
Binary files differ
diff --git a/platform/server/os_server.cpp b/platform/server/os_server.cpp
deleted file mode 100644
index 852ec7c4ef..0000000000
--- a/platform/server/os_server.cpp
+++ /dev/null
@@ -1,267 +0,0 @@
-/*************************************************************************/
-/* os_server.cpp */
-/*************************************************************************/
-/* This file is part of: */
-/* GODOT ENGINE */
-/* https://godotengine.org */
-/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 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 "os_server.h"
-
-#include "core/string/print_string.h"
-#include "drivers/dummy/rasterizer_dummy.h"
-#include "drivers/dummy/texture_loader_dummy.h"
-#include "servers/rendering/rendering_server_default.h"
-
-#include "main/main.h"
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <unistd.h>
-
-int OS_Server::get_video_driver_count() const {
- return 1;
-}
-
-const char *OS_Server::get_video_driver_name(int p_driver) const {
- return "Dummy";
-}
-
-int OS_Server::get_current_video_driver() const {
- return video_driver_index;
-}
-
-void OS_Server::initialize_core() {
- crash_handler.initialize();
-
- OS_Unix::initialize_core();
-}
-
-Error OS_Server::initialize(const VideoMode &p_desired, int p_video_driver, int p_audio_driver) {
- args = OS::get_singleton()->get_cmdline_args();
- current_videomode = p_desired;
- main_loop = nullptr;
-
- RasterizerDummy::make_current();
-
- video_driver_index = p_video_driver; // unused in server platform, but should still be initialized
-
- rendering_server = memnew(RenderingServerDefault);
- rendering_server->init();
-
- AudioDriverManager::initialize(p_audio_driver);
-
- input = memnew(InputDefault);
-
- _ensure_user_data_dir();
-
- resource_loader_dummy.instance();
- ResourceLoader::add_resource_format_loader(resource_loader_dummy);
-
- return OK;
-}
-
-void OS_Server::finalize() {
- if (main_loop)
- memdelete(main_loop);
- main_loop = nullptr;
-
- rendering_server->finish();
- memdelete(rendering_server);
-
- memdelete(input);
-
- ResourceLoader::remove_resource_format_loader(resource_loader_dummy);
- resource_loader_dummy.unref();
-
- args.clear();
-}
-
-void OS_Server::set_mouse_show(bool p_show) {
-}
-
-void OS_Server::set_mouse_grab(bool p_grab) {
- grab = p_grab;
-}
-
-bool OS_Server::is_mouse_grab_enabled() const {
- return grab;
-}
-
-int OS_Server::get_mouse_button_state() const {
- return 0;
-}
-
-Point2 OS_Server::get_mouse_position() const {
- return Point2();
-}
-
-void OS_Server::set_window_title(const String &p_title) {
-}
-
-void OS_Server::set_video_mode(const VideoMode &p_video_mode, int p_screen) {
-}
-
-OS::VideoMode OS_Server::get_video_mode(int p_screen) const {
- return current_videomode;
-}
-
-Size2 OS_Server::get_window_size() const {
- return Vector2(current_videomode.width, current_videomode.height);
-}
-
-void OS_Server::get_fullscreen_mode_list(List<VideoMode> *p_list, int p_screen) const {
-}
-
-MainLoop *OS_Server::get_main_loop() const {
- return main_loop;
-}
-
-void OS_Server::delete_main_loop() {
- if (main_loop)
- memdelete(main_loop);
- main_loop = nullptr;
-}
-
-void OS_Server::set_main_loop(MainLoop *p_main_loop) {
- main_loop = p_main_loop;
- input->set_main_loop(p_main_loop);
-}
-
-String OS_Server::get_name() const {
- return "Server";
-}
-
-void OS_Server::move_window_to_foreground() {
-}
-
-bool OS_Server::_check_internal_feature_support(const String &p_feature) {
- return p_feature == "pc";
-}
-
-void OS_Server::run() {
- force_quit = false;
-
- if (!main_loop)
- return;
-
- main_loop->init();
-
- while (!force_quit) {
- if (Main::iteration())
- break;
- };
-
- main_loop->finish();
-}
-
-String OS_Server::get_config_path() const {
- if (has_environment("XDG_CONFIG_HOME")) {
- return get_environment("XDG_CONFIG_HOME");
- } else if (has_environment("HOME")) {
- return get_environment("HOME").plus_file(".config");
- } else {
- return ".";
- }
-}
-
-String OS_Server::get_data_path() const {
- if (has_environment("XDG_DATA_HOME")) {
- return get_environment("XDG_DATA_HOME");
- } else if (has_environment("HOME")) {
- return get_environment("HOME").plus_file(".local/share");
- } else {
- return get_config_path();
- }
-}
-
-String OS_Server::get_cache_path() const {
- if (has_environment("XDG_CACHE_HOME")) {
- return get_environment("XDG_CACHE_HOME");
- } else if (has_environment("HOME")) {
- return get_environment("HOME").plus_file(".cache");
- } else {
- return get_config_path();
- }
-}
-
-String OS_Server::get_system_dir(SystemDir p_dir) const {
- String xdgparam;
-
- switch (p_dir) {
- case SYSTEM_DIR_DESKTOP: {
- xdgparam = "DESKTOP";
- } break;
- case SYSTEM_DIR_DCIM: {
- xdgparam = "PICTURES";
-
- } break;
- case SYSTEM_DIR_DOCUMENTS: {
- xdgparam = "DOCUMENTS";
-
- } break;
- case SYSTEM_DIR_DOWNLOADS: {
- xdgparam = "DOWNLOAD";
-
- } break;
- case SYSTEM_DIR_MOVIES: {
- xdgparam = "VIDEOS";
-
- } break;
- case SYSTEM_DIR_MUSIC: {
- xdgparam = "MUSIC";
-
- } break;
- case SYSTEM_DIR_PICTURES: {
- xdgparam = "PICTURES";
-
- } break;
- case SYSTEM_DIR_RINGTONES: {
- xdgparam = "MUSIC";
-
- } break;
- }
-
- String pipe;
- List<String> arg;
- arg.push_back(xdgparam);
- Error err = const_cast<OS_Server *>(this)->execute("xdg-user-dir", arg, true, nullptr, &pipe);
- if (err != OK)
- return ".";
- return pipe.strip_edges();
-}
-
-void OS_Server::disable_crash_handler() {
- crash_handler.disable();
-}
-
-bool OS_Server::is_disable_crash_handler() const {
- return crash_handler.is_disabled();
-}
-
-OS_Server::OS_Server() {
- //adriver here
- grab = false;
-};
diff --git a/platform/server/os_server.h b/platform/server/os_server.h
deleted file mode 100644
index 61025fa14b..0000000000
--- a/platform/server/os_server.h
+++ /dev/null
@@ -1,116 +0,0 @@
-/*************************************************************************/
-/* os_server.h */
-/*************************************************************************/
-/* This file is part of: */
-/* GODOT ENGINE */
-/* https://godotengine.org */
-/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 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 OS_SERVER_H
-#define OS_SERVER_H
-
-#include "core/input/input.h"
-#include "drivers/dummy/texture_loader_dummy.h"
-#include "drivers/unix/os_unix.h"
-#ifdef __APPLE__
-#include "platform/osx/crash_handler_osx.h"
-#include "platform/osx/semaphore_osx.h"
-#else
-#include "platform/x11/crash_handler_linuxbsd.h"
-#endif
-#include "servers/audio_server.h"
-#include "servers/rendering/renderer_compositor.h"
-#include "servers/rendering_server.h"
-
-#undef CursorShape
-
-class OS_Server : public OS_Unix {
- RenderingServer *rendering_server = nullptr;
- VideoMode current_videomode;
- List<String> args;
- MainLoop *main_loop = nullptr;
-
- bool grab = false;
-
- virtual void delete_main_loop();
-
- bool force_quit = false;
-
- InputDefault *input = nullptr;
-
- CrashHandler crash_handler;
-
- int video_driver_index = 0;
-
- Ref<ResourceFormatDummyTexture> resource_loader_dummy;
-
-protected:
- virtual int get_video_driver_count() const;
- virtual const char *get_video_driver_name(int p_driver) const;
- 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 finalize();
-
- virtual void set_main_loop(MainLoop *p_main_loop);
-
-public:
- virtual String get_name() const;
-
- 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 MainLoop *get_main_loop() const;
-
- 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;
-
- virtual Size2 get_window_size() const;
-
- virtual void move_window_to_foreground();
-
- void run();
-
- virtual bool _check_internal_feature_support(const String &p_feature);
-
- virtual String get_config_path() const;
- virtual String get_data_path() const;
- virtual String get_cache_path() const;
-
- virtual String get_system_dir(SystemDir p_dir) const;
-
- void disable_crash_handler();
- bool is_disable_crash_handler() const;
-
- OS_Server();
-};
-
-#endif
diff --git a/platform/server/platform_config.h b/platform/server/platform_config.h
deleted file mode 100644
index 32a19d811b..0000000000
--- a/platform/server/platform_config.h
+++ /dev/null
@@ -1,49 +0,0 @@
-/*************************************************************************/
-/* platform_config.h */
-/*************************************************************************/
-/* This file is part of: */
-/* GODOT ENGINE */
-/* https://godotengine.org */
-/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 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. */
-/*************************************************************************/
-
-#if defined(__linux__) || defined(__APPLE__)
-#include <alloca.h>
-#endif
-
-#if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__)
-#include <stdlib.h> // alloca
-// FreeBSD and OpenBSD use pthread_set_name_np, while other platforms,
-// include NetBSD, use pthread_setname_np. NetBSD's version however requires
-// a different format, we handle this directly in thread_posix.
-#ifdef __NetBSD__
-#define PTHREAD_NETBSD_SET_NAME
-#else
-#define PTHREAD_BSD_SET_NAME
-#endif
-#endif
-
-#ifdef __APPLE__
-#define PTHREAD_RENAME_SELF
-#endif
diff --git a/platform/uwp/SCsub b/platform/uwp/SCsub
index 4358b0eead..71c402358f 100644
--- a/platform/uwp/SCsub
+++ b/platform/uwp/SCsub
@@ -3,7 +3,6 @@
Import("env")
files = [
- "thread_uwp.cpp",
"#platform/windows/key_mapping_windows.cpp",
"#platform/windows/windows_terminal_logger.cpp",
"joypad_uwp.cpp",
diff --git a/platform/uwp/app.cpp b/platform/uwp/app.cpp
index dc4238bdd4..1da17ffc5d 100644
--- a/platform/uwp/app.cpp
+++ b/platform/uwp/app.cpp
@@ -34,8 +34,8 @@
#include "app.h"
-#include "core/os/dir_access.h"
-#include "core/os/file_access.h"
+#include "core/io/dir_access.h"
+#include "core/io/file_access.h"
#include "core/os/keyboard.h"
#include "main/main.h"
@@ -145,39 +145,39 @@ void App::SetWindow(CoreWindow ^ p_window) {
Main::setup2();
}
-static int _get_button(Windows::UI::Input::PointerPoint ^ pt) {
+static MouseButton _get_button(Windows::UI::Input::PointerPoint ^ pt) {
using namespace Windows::UI::Input;
#if WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP
- return BUTTON_LEFT;
+ return MOUSE_BUTTON_LEFT;
#else
switch (pt->Properties->PointerUpdateKind) {
case PointerUpdateKind::LeftButtonPressed:
case PointerUpdateKind::LeftButtonReleased:
- return BUTTON_LEFT;
+ return MOUSE_BUTTON_LEFT;
case PointerUpdateKind::RightButtonPressed:
case PointerUpdateKind::RightButtonReleased:
- return BUTTON_RIGHT;
+ return MOUSE_BUTTON_RIGHT;
case PointerUpdateKind::MiddleButtonPressed:
case PointerUpdateKind::MiddleButtonReleased:
- return BUTTON_MIDDLE;
+ return MOUSE_BUTTON_MIDDLE;
case PointerUpdateKind::XButton1Pressed:
case PointerUpdateKind::XButton1Released:
- return BUTTON_WHEEL_UP;
+ return MOUSE_BUTTON_WHEEL_UP;
case PointerUpdateKind::XButton2Pressed:
case PointerUpdateKind::XButton2Released:
- return BUTTON_WHEEL_DOWN;
+ return MOUSE_BUTTON_WHEEL_DOWN;
default:
break;
}
#endif
- return 0;
+ return MOUSE_BUTTON_NONE;
};
static bool _is_touch(Windows::UI::Input::PointerPoint ^ pointerPoint) {
@@ -241,10 +241,10 @@ static int _get_finger(uint32_t p_touch_id) {
void App::pointer_event(Windows::UI::Core::CoreWindow ^ sender, Windows::UI::Core::PointerEventArgs ^ args, bool p_pressed, bool p_is_wheel) {
Windows::UI::Input::PointerPoint ^ point = args->CurrentPoint;
Windows::Foundation::Point pos = _get_pixel_position(window, point->Position, os);
- int but = _get_button(point);
+ MouseButton but = _get_button(point);
if (_is_touch(point)) {
Ref<InputEventScreenTouch> screen_touch;
- screen_touch.instance();
+ screen_touch.instantiate();
screen_touch->set_device(0);
screen_touch->set_pressed(p_pressed);
screen_touch->set_position(Vector2(pos.X, pos.Y));
@@ -256,7 +256,7 @@ void App::pointer_event(Windows::UI::Core::CoreWindow ^ sender, Windows::UI::Cor
os->input_event(screen_touch);
} else {
Ref<InputEventMouseButton> mouse_button;
- mouse_button.instance();
+ mouse_button.instantiate();
mouse_button->set_device(0);
mouse_button->set_pressed(p_pressed);
mouse_button->set_button_index(but);
@@ -265,9 +265,9 @@ void App::pointer_event(Windows::UI::Core::CoreWindow ^ sender, Windows::UI::Cor
if (p_is_wheel) {
if (point->Properties->MouseWheelDelta > 0) {
- mouse_button->set_button_index(point->Properties->IsHorizontalMouseWheel ? BUTTON_WHEEL_RIGHT : BUTTON_WHEEL_UP);
+ mouse_button->set_button_index(point->Properties->IsHorizontalMouseWheel ? MOUSE_BUTTON_WHEEL_RIGHT : MOUSE_BUTTON_WHEEL_UP);
} else if (point->Properties->MouseWheelDelta < 0) {
- mouse_button->set_button_index(point->Properties->IsHorizontalMouseWheel ? BUTTON_WHEEL_LEFT : BUTTON_WHEEL_DOWN);
+ mouse_button->set_button_index(point->Properties->IsHorizontalMouseWheel ? MOUSE_BUTTON_WHEEL_LEFT : MOUSE_BUTTON_WHEEL_DOWN);
}
}
@@ -324,7 +324,7 @@ void App::OnPointerMoved(Windows::UI::Core::CoreWindow ^ sender, Windows::UI::Co
if (_is_touch(point)) {
Ref<InputEventScreenDrag> screen_drag;
- screen_drag.instance();
+ screen_drag.instantiate();
screen_drag->set_device(0);
screen_drag->set_position(Vector2(pos.X, pos.Y));
screen_drag->set_index(_get_finger(point->PointerId));
@@ -333,11 +333,12 @@ void App::OnPointerMoved(Windows::UI::Core::CoreWindow ^ sender, Windows::UI::Co
os->input_event(screen_drag);
} else {
// In case the mouse grabbed, MouseMoved will handle this
- if (os->get_mouse_mode() == OS::MouseMode::MOUSE_MODE_CAPTURED)
+ if (os->get_mouse_mode() == OS::MouseMode::MOUSE_MODE_CAPTURED) {
return;
+ }
Ref<InputEventMouseMotion> mouse_motion;
- mouse_motion.instance();
+ mouse_motion.instantiate();
mouse_motion->set_device(0);
mouse_motion->set_position(Vector2(pos.X, pos.Y));
mouse_motion->set_global_position(Vector2(pos.X, pos.Y));
@@ -351,15 +352,16 @@ void App::OnPointerMoved(Windows::UI::Core::CoreWindow ^ sender, Windows::UI::Co
void App::OnMouseMoved(MouseDevice ^ mouse_device, MouseEventArgs ^ args) {
// In case the mouse isn't grabbed, PointerMoved will handle this
- if (os->get_mouse_mode() != OS::MouseMode::MOUSE_MODE_CAPTURED)
+ if (os->get_mouse_mode() != OS::MouseMode::MOUSE_MODE_CAPTURED) {
return;
+ }
Windows::Foundation::Point pos;
pos.X = last_mouse_pos.X + args->MouseDelta.X;
pos.Y = last_mouse_pos.Y + args->MouseDelta.Y;
Ref<InputEventMouseMotion> mouse_motion;
- mouse_motion.instance();
+ mouse_motion.instantiate();
mouse_motion->set_device(0);
mouse_motion->set_position(Vector2(pos.X, pos.Y));
mouse_motion->set_global_position(Vector2(pos.X, pos.Y));
@@ -383,7 +385,7 @@ void App::key_event(Windows::UI::Core::CoreWindow ^ sender, bool p_pressed, Wind
ke.type = OS_UWP::KeyEvent::MessageType::KEY_EVENT_MESSAGE;
ke.unicode = 0;
ke.keycode = KeyMappingWindows::get_keysym((unsigned int)key_args->VirtualKey);
- ke.physical_keycode = KeyMappingWindows::get_scansym((unsigned int)key_args->KeyStatus.ScanCode);
+ ke.physical_keycode = KeyMappingWindows::get_scansym((unsigned int)key_args->KeyStatus.ScanCode, key_args->KeyStatus.IsExtendedKey);
ke.echo = (!p_pressed && !key_args->KeyStatus.IsKeyReleased) || (p_pressed && key_args->KeyStatus.WasKeyDown);
} else {
diff --git a/platform/uwp/detect.py b/platform/uwp/detect.py
index fda8fdec66..28922a4f59 100644
--- a/platform/uwp/detect.py
+++ b/platform/uwp/detect.py
@@ -54,16 +54,19 @@ def configure(env):
## Build type
if env["target"] == "release":
- env.Append(CCFLAGS=["/O2", "/GL"])
env.Append(CCFLAGS=["/MD"])
- env.Append(LINKFLAGS=["/SUBSYSTEM:WINDOWS", "/LTCG"])
+ env.Append(LINKFLAGS=["/SUBSYSTEM:WINDOWS"])
+ if env["optimize"] != "none":
+ env.Append(CCFLAGS=["/O2", "/GL"])
+ env.Append(LINKFLAGS=["/LTCG"])
elif env["target"] == "release_debug":
- env.Append(CCFLAGS=["/O2", "/Zi"])
env.Append(CCFLAGS=["/MD"])
- env.Append(CPPDEFINES=["DEBUG_ENABLED"])
env.Append(LINKFLAGS=["/SUBSYSTEM:CONSOLE"])
env.AppendUnique(CPPDEFINES=["WINDOWS_SUBSYSTEM_CONSOLE"])
+ env.Append(CPPDEFINES=["DEBUG_ENABLED"])
+ if env["optimize"] != "none":
+ env.Append(CCFLAGS=["/O2", "/Zi"])
elif env["target"] == "debug":
env.Append(CCFLAGS=["/Zi"])
diff --git a/platform/uwp/export/export.cpp b/platform/uwp/export/export.cpp
index 1aad2bfa1a..98925fd7fa 100644
--- a/platform/uwp/export/export.cpp
+++ b/platform/uwp/export/export.cpp
@@ -31,13 +31,12 @@
#include "export.h"
#include "core/config/project_settings.h"
-#include "core/core_bind.h"
#include "core/crypto/crypto_core.h"
+#include "core/io/dir_access.h"
+#include "core/io/file_access.h"
#include "core/io/marshalls.h"
#include "core/io/zip_io.h"
#include "core/object/class_db.h"
-#include "core/os/dir_access.h"
-#include "core/os/file_access.h"
#include "core/version.h"
#include "editor/editor_export.h"
#include "editor/editor_node.h"
@@ -567,12 +566,12 @@ void AppxPackager::finish() {
// Create and add block map file
EditorNode::progress_task_step("export", "Creating block map...", 4);
- const String &tmp_blockmap_file_path = EditorSettings::get_singleton()->get_cache_dir().plus_file("tmpblockmap.xml");
+ const String &tmp_blockmap_file_path = EditorPaths::get_singleton()->get_cache_dir().plus_file("tmpblockmap.xml");
make_block_map(tmp_blockmap_file_path);
FileAccess *blockmap_file = FileAccess::open(tmp_blockmap_file_path, FileAccess::READ);
Vector<uint8_t> blockmap_buffer;
- blockmap_buffer.resize(blockmap_file->get_len());
+ blockmap_buffer.resize(blockmap_file->get_length());
blockmap_file->get_buffer(blockmap_buffer.ptrw(), blockmap_buffer.size());
@@ -585,12 +584,12 @@ void AppxPackager::finish() {
EditorNode::progress_task_step("export", "Setting content types...", 5);
- const String &tmp_content_types_file_path = EditorSettings::get_singleton()->get_cache_dir().plus_file("tmpcontenttypes.xml");
+ const String &tmp_content_types_file_path = EditorPaths::get_singleton()->get_cache_dir().plus_file("tmpcontenttypes.xml");
make_content_types(tmp_content_types_file_path);
FileAccess *types_file = FileAccess::open(tmp_content_types_file_path, FileAccess::READ);
Vector<uint8_t> types_buffer;
- types_buffer.resize(types_file->get_len());
+ types_buffer.resize(types_file->get_length());
types_file->get_buffer(types_buffer.ptrw(), types_buffer.size());
@@ -855,33 +854,33 @@ class EditorExportPlatformUWP : public EditorExportPlatform {
Vector<uint8_t> _get_image_data(const Ref<EditorExportPreset> &p_preset, const String &p_path) {
Vector<uint8_t> data;
- StreamTexture2D *image = nullptr;
+ StreamTexture2D *texture = nullptr;
if (p_path.find("StoreLogo") != -1) {
- image = p_preset->get("images/store_logo").is_zero() ? nullptr : Object::cast_to<StreamTexture2D>(((Object *)p_preset->get("images/store_logo")));
+ texture = p_preset->get("images/store_logo").is_zero() ? nullptr : Object::cast_to<StreamTexture2D>(((Object *)p_preset->get("images/store_logo")));
} else if (p_path.find("Square44x44Logo") != -1) {
- image = p_preset->get("images/square44x44_logo").is_zero() ? nullptr : Object::cast_to<StreamTexture2D>(((Object *)p_preset->get("images/square44x44_logo")));
+ texture = p_preset->get("images/square44x44_logo").is_zero() ? nullptr : Object::cast_to<StreamTexture2D>(((Object *)p_preset->get("images/square44x44_logo")));
} else if (p_path.find("Square71x71Logo") != -1) {
- image = p_preset->get("images/square71x71_logo").is_zero() ? nullptr : Object::cast_to<StreamTexture2D>(((Object *)p_preset->get("images/square71x71_logo")));
+ texture = p_preset->get("images/square71x71_logo").is_zero() ? nullptr : Object::cast_to<StreamTexture2D>(((Object *)p_preset->get("images/square71x71_logo")));
} else if (p_path.find("Square150x150Logo") != -1) {
- image = p_preset->get("images/square150x150_logo").is_zero() ? nullptr : Object::cast_to<StreamTexture2D>(((Object *)p_preset->get("images/square150x150_logo")));
+ texture = p_preset->get("images/square150x150_logo").is_zero() ? nullptr : Object::cast_to<StreamTexture2D>(((Object *)p_preset->get("images/square150x150_logo")));
} else if (p_path.find("Square310x310Logo") != -1) {
- image = p_preset->get("images/square310x310_logo").is_zero() ? nullptr : Object::cast_to<StreamTexture2D>(((Object *)p_preset->get("images/square310x310_logo")));
+ texture = p_preset->get("images/square310x310_logo").is_zero() ? nullptr : Object::cast_to<StreamTexture2D>(((Object *)p_preset->get("images/square310x310_logo")));
} else if (p_path.find("Wide310x150Logo") != -1) {
- image = p_preset->get("images/wide310x150_logo").is_zero() ? nullptr : Object::cast_to<StreamTexture2D>(((Object *)p_preset->get("images/wide310x150_logo")));
+ texture = p_preset->get("images/wide310x150_logo").is_zero() ? nullptr : Object::cast_to<StreamTexture2D>(((Object *)p_preset->get("images/wide310x150_logo")));
} else if (p_path.find("SplashScreen") != -1) {
- image = p_preset->get("images/splash_screen").is_zero() ? nullptr : Object::cast_to<StreamTexture2D>(((Object *)p_preset->get("images/splash_screen")));
+ texture = p_preset->get("images/splash_screen").is_zero() ? nullptr : Object::cast_to<StreamTexture2D>(((Object *)p_preset->get("images/splash_screen")));
} else {
ERR_PRINT("Unable to load logo");
}
- if (!image) {
+ if (!texture) {
return data;
}
- String tmp_path = EditorSettings::get_singleton()->get_cache_dir().plus_file("uwp_tmp_logo.png");
+ String tmp_path = EditorPaths::get_singleton()->get_cache_dir().plus_file("uwp_tmp_logo.png");
- Error err = image->get_data()->save_png(tmp_path);
+ Error err = texture->get_image()->save_png(tmp_path);
if (err != OK) {
String err_string = "Couldn't save temp logo file.";
@@ -900,7 +899,7 @@ class EditorExportPlatformUWP : public EditorExportPlatform {
ERR_FAIL_V_MSG(data, err_string);
}
- data.resize(f->get_len());
+ data.resize(f->get_length());
f->get_buffer(data.ptrw(), data.size());
f->close();
@@ -1049,19 +1048,19 @@ public:
// Capabilities
const char **basic = uwp_capabilities;
while (*basic) {
- r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "capabilities/" + String(*basic).camelcase_to_underscore(false)), false));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "capabilities/" + String(*basic)), false));
basic++;
}
const char **uap = uwp_uap_capabilities;
while (*uap) {
- r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "capabilities/" + String(*uap).camelcase_to_underscore(false)), false));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "capabilities/" + String(*uap)), false));
uap++;
}
const char **device = uwp_device_capabilities;
while (*device) {
- r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "capabilities/" + String(*device).camelcase_to_underscore(false)), false));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "capabilities/" + String(*device)), false));
device++;
}
}
@@ -1177,6 +1176,8 @@ public:
}
virtual Error export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags = 0) override {
+ ExportNotifier notifier(*this, p_preset, p_debug, p_path, p_flags);
+
String src_appx;
EditorProgress ep("export", "Exporting for UWP", 7, true);
@@ -1334,7 +1335,7 @@ public:
int base = clf.size();
clf.resize(base + 4 + txt.length());
encode_uint32(txt.length(), &clf.write[base]);
- copymem(&clf.write[base + 4], txt.ptr(), txt.length());
+ memcpy(&clf.write[base + 4], txt.ptr(), txt.length());
print_line(itos(i) + " param: " + cl[i]);
}
@@ -1427,7 +1428,7 @@ public:
EditorExportPlatformUWP() {
Ref<Image> img = memnew(Image(_uwp_logo));
- logo.instance();
+ logo.instantiate();
logo->create_from_image(img);
}
};
@@ -1444,6 +1445,6 @@ void register_uwp_exporter() {
#endif // WINDOWS_ENABLED
Ref<EditorExportPlatformUWP> exporter;
- exporter.instance();
+ exporter.instantiate();
EditorExport::get_singleton()->add_export_platform(exporter);
}
diff --git a/platform/uwp/joypad_uwp.cpp b/platform/uwp/joypad_uwp.cpp
index 5da90db49d..b419fb4fae 100644
--- a/platform/uwp/joypad_uwp.cpp
+++ b/platform/uwp/joypad_uwp.cpp
@@ -134,8 +134,8 @@ void JoypadUWP::OnGamepadRemoved(Platform::Object ^ sender, Windows::Gaming::Inp
input->joy_connection_changed(idx, false, "Xbox Controller");
}
-InputDefault::JoyAxis JoypadUWP::axis_correct(double p_val, bool p_negate, bool p_trigger) const {
- InputDefault::JoyAxis jx;
+InputDefault::JoyAxisValue JoypadUWP::axis_correct(double p_val, bool p_negate, bool p_trigger) const {
+ InputDefault::JoyAxisValue jx;
jx.min = p_trigger ? 0 : -1;
jx.value = (float)(p_negate ? -p_val : p_val);
diff --git a/platform/uwp/joypad_uwp.h b/platform/uwp/joypad_uwp.h
index 5df4a211ac..d760d9e2fe 100644
--- a/platform/uwp/joypad_uwp.h
+++ b/platform/uwp/joypad_uwp.h
@@ -73,7 +73,7 @@ private:
void OnGamepadAdded(Platform::Object ^ sender, Windows::Gaming::Input::Gamepad ^ value);
void OnGamepadRemoved(Platform::Object ^ sender, Windows::Gaming::Input::Gamepad ^ value);
- InputDefault::JoyAxis axis_correct(double p_val, bool p_negate = false, bool p_trigger = false) const;
+ InputDefault::JoyAxisValue axis_correct(double p_val, bool p_negate = false, bool p_trigger = false) const;
void joypad_vibration_start(int p_device, float p_weak_magnitude, float p_strong_magnitude, float p_duration, uint64_t p_timestamp);
void joypad_vibration_stop(int p_device, uint64_t p_timestamp);
};
diff --git a/platform/uwp/os_uwp.cpp b/platform/uwp/os_uwp.cpp
index fa97948395..e26e204f5f 100644
--- a/platform/uwp/os_uwp.cpp
+++ b/platform/uwp/os_uwp.cpp
@@ -62,6 +62,8 @@ using namespace Windows::Devices::Sensors;
using namespace Windows::ApplicationModel::DataTransfer;
using namespace concurrency;
+static const float earth_gravity = 9.80665;
+
int OS_UWP::get_video_driver_count() const {
return 2;
}
@@ -124,8 +126,6 @@ void OS_UWP::set_keep_screen_on(bool p_enabled) {
}
void OS_UWP::initialize_core() {
- last_button_state = 0;
-
//RedirectIOToConsole();
FileAccess::make_default<FileAccessWindows>(FileAccess::ACCESS_RESOURCES);
@@ -145,7 +145,7 @@ void OS_UWP::initialize_core() {
ticks_start = 0;
ticks_start = get_ticks_usec();
- IP_Unix::make_default();
+ IPUnix::make_default();
cursor_shape = CURSOR_ARROW;
}
@@ -372,9 +372,9 @@ void OS_UWP::ManagedType::on_accelerometer_reading_changed(Accelerometer ^ sende
AccelerometerReading ^ reading = args->Reading;
os->input->set_accelerometer(Vector3(
- reading->AccelerationX,
- reading->AccelerationY,
- reading->AccelerationZ));
+ reading->AccelerationX * earth_gravity,
+ reading->AccelerationY * earth_gravity,
+ reading->AccelerationZ * earth_gravity));
}
void OS_UWP::ManagedType::on_magnetometer_reading_changed(Magnetometer ^ sender, MagnetometerReadingChangedEventArgs ^ args) {
@@ -398,14 +398,12 @@ void OS_UWP::ManagedType::on_gyroscope_reading_changed(Gyrometer ^ sender, Gyrom
void OS_UWP::set_mouse_mode(MouseMode p_mode) {
if (p_mode == MouseMode::MOUSE_MODE_CAPTURED) {
CoreWindow::GetForCurrentThread()->SetPointerCapture();
-
} else {
CoreWindow::GetForCurrentThread()->ReleasePointerCapture();
}
- if (p_mode == MouseMode::MOUSE_MODE_CAPTURED || p_mode == MouseMode::MOUSE_MODE_HIDDEN) {
+ if (p_mode == MouseMode::MOUSE_MODE_HIDDEN || p_mode == MouseMode::MOUSE_MODE_CAPTURED || p_mode == MouseMode::MOUSE_MODE_CONFINED_HIDDEN) {
CoreWindow::GetForCurrentThread()->PointerCursor = nullptr;
-
} else {
CoreWindow::GetForCurrentThread()->PointerCursor = ref new CoreCursor(CoreCursorType::Arrow, 0);
}
@@ -423,7 +421,7 @@ Point2 OS_UWP::get_mouse_position() const {
return Point2(old_x, old_y);
}
-int OS_UWP::get_mouse_button_state() const {
+MouseButton OS_UWP::get_mouse_button_state() const {
return last_button_state;
}
@@ -562,10 +560,10 @@ void OS_UWP::process_key_events() {
KeyEvent &kev = key_event_buffer[i];
Ref<InputEventKey> key_event;
- key_event.instance();
- key_event->set_alt(kev.alt);
- key_event->set_shift(kev.shift);
- key_event->set_control(kev.control);
+ key_event.instantiate();
+ key_event->set_alt_pressed(kev.alt);
+ key_event->set_shift_pressed(kev.shift);
+ key_event->set_ctrl_pressed(kev.control);
key_event->set_echo(kev.echo);
key_event->set_keycode(kev.keycode);
key_event->set_physical_keycode(kev.physical_keycode);
diff --git a/platform/uwp/os_uwp.h b/platform/uwp/os_uwp.h
index a4d3d6d52a..3a735889b0 100644
--- a/platform/uwp/os_uwp.h
+++ b/platform/uwp/os_uwp.h
@@ -106,7 +106,7 @@ private:
bool control_mem;
bool meta_mem;
bool force_quit;
- uint32_t last_button_state;
+ MouseButton last_button_state = MOUSE_BUTTON_NONE;
CursorShape cursor_shape;
@@ -173,7 +173,7 @@ public:
MouseMode get_mouse_mode() const;
virtual Point2 get_mouse_position() const;
- virtual int get_mouse_button_state() const;
+ virtual MouseButton get_mouse_button_state() const;
virtual void set_window_title(const String &p_title);
virtual void set_video_mode(const VideoMode &p_video_mode, int p_screen = 0);
diff --git a/platform/windows/context_gl_windows.cpp b/platform/windows/context_gl_windows.cpp
index 207b0a1168..74b12cbb3b 100644
--- a/platform/windows/context_gl_windows.cpp
+++ b/platform/windows/context_gl_windows.cpp
@@ -66,46 +66,13 @@ int ContextGL_Windows::get_window_height() {
return OS::get_singleton()->get_video_mode().height;
}
-bool ContextGL_Windows::should_vsync_via_compositor() {
- if (OS::get_singleton()->is_window_fullscreen() || !OS::get_singleton()->is_vsync_via_compositor_enabled()) {
- return false;
- }
-
- // Note: All Windows versions supported by Godot have a compositor.
- // It can be disabled on earlier Windows versions.
- BOOL dwm_enabled;
-
- if (SUCCEEDED(DwmIsCompositionEnabled(&dwm_enabled))) {
- return dwm_enabled;
- }
-
- return false;
-}
-
void ContextGL_Windows::swap_buffers() {
SwapBuffers(hDC);
-
- if (use_vsync) {
- bool vsync_via_compositor_now = should_vsync_via_compositor();
-
- if (vsync_via_compositor_now && wglGetSwapIntervalEXT() == 0) {
- DwmFlush();
- }
-
- if (vsync_via_compositor_now != vsync_via_compositor) {
- // The previous frame had a different operating mode than this
- // frame. Set the 'vsync_via_compositor' member variable and the
- // OpenGL swap interval to their proper values.
- set_use_vsync(true);
- }
- }
}
void ContextGL_Windows::set_use_vsync(bool p_use) {
- vsync_via_compositor = p_use && should_vsync_via_compositor();
-
if (wglSwapIntervalEXT) {
- int swap_interval = (p_use && !vsync_via_compositor) ? 1 : 0;
+ int swap_interval = p_use ? 1 : 0;
wglSwapIntervalEXT(swap_interval);
}
@@ -210,7 +177,6 @@ ContextGL_Windows::ContextGL_Windows(HWND hwnd, bool p_opengl_3_context) {
opengl_3_context = p_opengl_3_context;
hWnd = hwnd;
use_vsync = false;
- vsync_via_compositor = false;
pixel_format = 0;
}
diff --git a/platform/windows/context_gl_windows.h b/platform/windows/context_gl_windows.h
index e44e2945ca..c8e8a0891d 100644
--- a/platform/windows/context_gl_windows.h
+++ b/platform/windows/context_gl_windows.h
@@ -50,13 +50,10 @@ class ContextGL_Windows {
HWND hWnd;
bool opengl_3_context;
bool use_vsync;
- bool vsync_via_compositor;
PFNWGLSWAPINTERVALEXTPROC wglSwapIntervalEXT;
PFNWGLGETSWAPINTERVALEXTPROC wglGetSwapIntervalEXT;
- static bool should_vsync_via_compositor();
-
public:
void release_current();
diff --git a/platform/windows/crash_handler_windows.cpp b/platform/windows/crash_handler_windows.cpp
index e24e466f88..e2d507eddd 100644
--- a/platform/windows/crash_handler_windows.cpp
+++ b/platform/windows/crash_handler_windows.cpp
@@ -36,7 +36,7 @@
#ifdef CRASH_HANDLER_EXCEPTION
-// Backtrace code code based on: https://stackoverflow.com/questions/6205981/windows-c-stack-trace-from-a-running-app
+// Backtrace code based on: https://stackoverflow.com/questions/6205981/windows-c-stack-trace-from-a-running-app
#include <algorithm>
#include <iterator>
diff --git a/platform/windows/detect.py b/platform/windows/detect.py
index f26dea8d35..7772ba2dbe 100644
--- a/platform/windows/detect.py
+++ b/platform/windows/detect.py
@@ -72,6 +72,7 @@ def get_opts():
BoolVariable("use_llvm", "Use the LLVM compiler", False),
BoolVariable("use_thinlto", "Use ThinLTO", False),
BoolVariable("use_static_cpp", "Link MinGW/MSVC C++ runtime libraries statically", True),
+ BoolVariable("use_asan", "Use address sanitizer (ASAN)", False),
]
@@ -190,18 +191,20 @@ def configure_msvc(env, manual_msvc_config):
if env["target"] == "release":
if env["optimize"] == "speed": # optimize for speed (default)
env.Append(CCFLAGS=["/O2"])
- else: # optimize for size
+ env.Append(LINKFLAGS=["/OPT:REF"])
+ elif env["optimize"] == "size": # optimize for size
env.Append(CCFLAGS=["/O1"])
+ env.Append(LINKFLAGS=["/OPT:REF"])
env.Append(LINKFLAGS=["/ENTRY:mainCRTStartup"])
- env.Append(LINKFLAGS=["/OPT:REF"])
elif env["target"] == "release_debug":
if env["optimize"] == "speed": # optimize for speed (default)
env.Append(CCFLAGS=["/O2"])
- else: # optimize for size
+ env.Append(LINKFLAGS=["/OPT:REF"])
+ elif env["optimize"] == "size": # optimize for size
env.Append(CCFLAGS=["/O1"])
+ env.Append(LINKFLAGS=["/OPT:REF"])
env.AppendUnique(CPPDEFINES=["DEBUG_ENABLED"])
- env.Append(LINKFLAGS=["/OPT:REF"])
elif env["target"] == "debug":
env.AppendUnique(CCFLAGS=["/Zi", "/FS", "/Od", "/EHsc"])
@@ -306,6 +309,12 @@ def configure_msvc(env, manual_msvc_config):
env.Prepend(CPPPATH=[p for p in os.getenv("INCLUDE").split(";")])
env.Append(LIBPATH=[p for p in os.getenv("LIB").split(";")])
+ # Sanitizers
+ if env["use_asan"]:
+ env.extra_suffix += ".s"
+ env.Append(LINKFLAGS=["/INFERASANLIBS"])
+ env.Append(CCFLAGS=["/fsanitize=address"])
+
# Incremental linking fix
env["BUILDERS"]["ProgramOriginal"] = env["BUILDERS"]["Program"]
env["BUILDERS"]["Program"] = methods.precious_program
diff --git a/platform/windows/display_server_windows.cpp b/platform/windows/display_server_windows.cpp
index b9b78f7bd4..4f64809abc 100644
--- a/platform/windows/display_server_windows.cpp
+++ b/platform/windows/display_server_windows.cpp
@@ -84,7 +84,8 @@ void DisplayServerWindows::alert(const String &p_alert, const String &p_title) {
}
void DisplayServerWindows::_set_mouse_mode_impl(MouseMode p_mode) {
- if (p_mode == MOUSE_MODE_CAPTURED || p_mode == MOUSE_MODE_CONFINED) {
+ if (p_mode == MOUSE_MODE_CAPTURED || p_mode == MOUSE_MODE_CONFINED || p_mode == MOUSE_MODE_CONFINED_HIDDEN) {
+ // Mouse is grabbed (captured or confined).
WindowData &wd = windows[MAIN_WINDOW_ID];
RECT clipRect;
@@ -100,11 +101,12 @@ void DisplayServerWindows::_set_mouse_mode_impl(MouseMode p_mode) {
SetCapture(wd.hWnd);
}
} else {
+ // Mouse is free to move around (not captured or confined).
ReleaseCapture();
ClipCursor(nullptr);
}
- if (p_mode == MOUSE_MODE_CAPTURED || p_mode == MOUSE_MODE_HIDDEN) {
+ if (p_mode == MOUSE_MODE_HIDDEN || p_mode == MOUSE_MODE_CAPTURED || p_mode == MOUSE_MODE_CONFINED_HIDDEN) {
if (hCursor == nullptr) {
hCursor = SetCursor(nullptr);
} else {
@@ -159,7 +161,7 @@ Point2i DisplayServerWindows::mouse_get_position() const {
//return Point2(old_x, old_y);
}
-int DisplayServerWindows::mouse_get_button_state() const {
+MouseButton DisplayServerWindows::mouse_get_button_state() const {
return last_button_state;
}
@@ -332,7 +334,7 @@ static BOOL CALLBACK _MonitorEnumProcUsableSize(HMONITOR hMonitor, HDC hdcMonito
EnumRectData *data = (EnumRectData *)dwData;
if (data->count == data->screen) {
MONITORINFO minfo;
- zeromem(&minfo, sizeof(MONITORINFO));
+ memset(&minfo, 0, sizeof(MONITORINFO));
minfo.cbSize = sizeof(MONITORINFO);
GetMonitorInfoA(hMonitor, &minfo);
@@ -475,10 +477,10 @@ DisplayServer::WindowID DisplayServerWindows::get_window_at_screen_position(cons
return INVALID_WINDOW_ID;
}
-DisplayServer::WindowID DisplayServerWindows::create_sub_window(WindowMode p_mode, uint32_t p_flags, const Rect2i &p_rect) {
+DisplayServer::WindowID DisplayServerWindows::create_sub_window(WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Rect2i &p_rect) {
_THREAD_SAFE_METHOD_
- WindowID window_id = _create_window(p_mode, p_flags, p_rect);
+ WindowID window_id = _create_window(p_mode, p_vsync_mode, p_flags, p_rect);
ERR_FAIL_COND_V_MSG(window_id == INVALID_WINDOW_ID, INVALID_WINDOW_ID, "Failed to create sub window.");
WindowData &wd = windows[window_id];
@@ -715,7 +717,7 @@ void DisplayServerWindows::window_set_position(const Point2i &p_position, Window
MoveWindow(wd.hWnd, rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top, TRUE);
#endif
// Don't let the mouse leave the window when moved
- if (mouse_mode == MOUSE_MODE_CONFINED) {
+ if (mouse_mode == MOUSE_MODE_CONFINED || mouse_mode == MOUSE_MODE_CONFINED_HIDDEN) {
RECT rect;
GetClientRect(wd.hWnd, &rect);
ClientToScreen(wd.hWnd, (POINT *)&rect.left);
@@ -841,7 +843,7 @@ void DisplayServerWindows::window_set_size(const Size2i p_size, WindowID p_windo
MoveWindow(wd.hWnd, rect.left, rect.top, w, h, TRUE);
// Don't let the mouse leave the window when resizing to a smaller resolution
- if (mouse_mode == MOUSE_MODE_CONFINED) {
+ if (mouse_mode == MOUSE_MODE_CONFINED || mouse_mode == MOUSE_MODE_CONFINED_HIDDEN) {
RECT crect;
GetClientRect(wd.hWnd, &crect);
ClientToScreen(wd.hWnd, (POINT *)&crect.left);
@@ -1119,7 +1121,7 @@ bool DisplayServerWindows::window_can_draw(WindowID p_window) const {
ERR_FAIL_COND_V(!windows.has(p_window), false);
const WindowData &wd = windows[p_window];
- return wd.minimized;
+ return !wd.minimized;
}
bool DisplayServerWindows::can_any_window_draw() const {
@@ -1253,12 +1255,12 @@ void DisplayServerWindows::GetMaskBitmaps(HBITMAP hSourceBitmap, COLORREF clrTra
HBITMAP hOldAndMaskBitmap = (HBITMAP)SelectObject(hAndMaskDC, hAndMaskBitmap);
HBITMAP hOldXorMaskBitmap = (HBITMAP)SelectObject(hXorMaskDC, hXorMaskBitmap);
- // Assign the monochrome AND mask bitmap pixels so that a pixels of the source bitmap
+ // Assign the monochrome AND mask bitmap pixels so that the pixels of the source bitmap
// with 'clrTransparent' will be white pixels of the monochrome bitmap
SetBkColor(hMainDC, clrTransparent);
BitBlt(hAndMaskDC, 0, 0, bm.bmWidth, bm.bmHeight, hMainDC, 0, 0, SRCCOPY);
- // Assign the color XOR mask bitmap pixels so that a pixels of the source bitmap
+ // Assign the color XOR mask bitmap pixels so that the pixels of the source bitmap
// with 'clrTransparent' will be black and rest the pixels same as corresponding
// pixels of the source bitmap
SetBkColor(hXorMaskDC, RGB(0, 0, 0));
@@ -1299,7 +1301,7 @@ void DisplayServerWindows::cursor_set_custom_image(const RES &p_cursor, CursorSh
Rect2 atlas_rect;
if (texture.is_valid()) {
- image = texture->get_data();
+ image = texture->get_image();
}
if (!image.is_valid() && atlas_texture.is_valid()) {
@@ -1322,7 +1324,7 @@ void DisplayServerWindows::cursor_set_custom_image(const RES &p_cursor, CursorSh
ERR_FAIL_COND(texture_size.width > 256 || texture_size.height > 256);
ERR_FAIL_COND(p_hotspot.x > texture_size.width || p_hotspot.y > texture_size.height);
- image = texture->get_data();
+ image = texture->get_image();
ERR_FAIL_COND(!image.is_valid());
@@ -1419,13 +1421,13 @@ void DisplayServerWindows::enable_for_stealing_focus(OS::ProcessID pid) {
}
int DisplayServerWindows::keyboard_get_layout_count() const {
- return GetKeyboardLayoutList(0, NULL);
+ return GetKeyboardLayoutList(0, nullptr);
}
int DisplayServerWindows::keyboard_get_current_layout() const {
HKL cur_layout = GetKeyboardLayout(0);
- int layout_count = GetKeyboardLayoutList(0, NULL);
+ int layout_count = GetKeyboardLayoutList(0, nullptr);
HKL *layouts = (HKL *)memalloc(layout_count * sizeof(HKL));
GetKeyboardLayoutList(layout_count, layouts);
@@ -1440,7 +1442,7 @@ int DisplayServerWindows::keyboard_get_current_layout() const {
}
void DisplayServerWindows::keyboard_set_current_layout(int p_index) {
- int layout_count = GetKeyboardLayoutList(0, NULL);
+ int layout_count = GetKeyboardLayoutList(0, nullptr);
ERR_FAIL_INDEX(p_index, layout_count);
@@ -1451,7 +1453,7 @@ void DisplayServerWindows::keyboard_set_current_layout(int p_index) {
}
String DisplayServerWindows::keyboard_get_layout_language(int p_index) const {
- int layout_count = GetKeyboardLayoutList(0, NULL);
+ int layout_count = GetKeyboardLayoutList(0, nullptr);
ERR_FAIL_INDEX_V(p_index, layout_count, "");
@@ -1481,7 +1483,7 @@ String _get_full_layout_name_from_registry(HKL p_layout) {
DWORD buffer = 1024;
DWORD vtype = REG_SZ;
- if (RegQueryValueExW(hkey, L"Layout Text", NULL, &vtype, (LPBYTE)layout_text, &buffer) == ERROR_SUCCESS) {
+ if (RegQueryValueExW(hkey, L"Layout Text", nullptr, &vtype, (LPBYTE)layout_text, &buffer) == ERROR_SUCCESS) {
ret = String::utf16((const char16_t *)layout_text);
}
RegCloseKey(hkey);
@@ -1489,7 +1491,7 @@ String _get_full_layout_name_from_registry(HKL p_layout) {
}
String DisplayServerWindows::keyboard_get_layout_name(int p_index) const {
- int layout_count = GetKeyboardLayoutList(0, NULL);
+ int layout_count = GetKeyboardLayoutList(0, nullptr);
ERR_FAIL_INDEX_V(p_index, layout_count, "");
@@ -1695,11 +1697,20 @@ void DisplayServerWindows::set_icon(const Ref<Image> &p_icon) {
SendMessage(windows[MAIN_WINDOW_ID].hWnd, WM_SETICON, ICON_BIG, (LPARAM)hicon);
}
-void DisplayServerWindows::vsync_set_use_via_compositor(bool p_enable) {
+void DisplayServerWindows::window_set_vsync_mode(DisplayServer::VSyncMode p_vsync_mode, WindowID p_window) {
+ _THREAD_SAFE_METHOD_
+#if defined(VULKAN_ENABLED)
+ context_vulkan->set_vsync_mode(p_window, p_vsync_mode);
+#endif
}
-bool DisplayServerWindows::vsync_is_using_via_compositor() const {
- return false;
+DisplayServer::VSyncMode DisplayServerWindows::window_get_vsync_mode(WindowID p_window) const {
+ _THREAD_SAFE_METHOD_
+#if defined(VULKAN_ENABLED)
+ return context_vulkan->get_vsync_mode(p_window);
+#else
+ return DisplayServer::VSYNC_ENABLED;
+#endif
}
void DisplayServerWindows::set_context(Context p_context) {
@@ -1725,7 +1736,7 @@ void DisplayServerWindows::_touch_event(WindowID p_window, bool p_pressed, float
}
Ref<InputEventScreenTouch> event;
- event.instance();
+ event.instantiate();
event->set_index(idx);
event->set_window_id(p_window);
event->set_pressed(p_pressed);
@@ -1744,7 +1755,7 @@ void DisplayServerWindows::_drag_event(WindowID p_window, float p_x, float p_y,
return;
Ref<InputEventScreenDrag> event;
- event.instance();
+ event.instantiate();
event->set_window_id(p_window);
event->set_index(idx);
event->set_position(Vector2(p_x, p_y));
@@ -1889,7 +1900,7 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA
// Run a timer to prevent event catching warning if the focused window is closing.
windows[window_id].focus_timer_id = SetTimer(windows[window_id].hWnd, 2, USER_TIMER_MINIMUM, (TIMERPROC) nullptr);
}
- return 0; // Return To The Message Loop
+ return 0; // Return To The Message Loop
}
case WM_GETMINMAXINFO: {
if (windows[window_id].resizable && !windows[window_id].fullscreen) {
@@ -1963,12 +1974,12 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA
if (raw->header.dwType == RIM_TYPEMOUSE) {
Ref<InputEventMouseMotion> mm;
- mm.instance();
+ mm.instantiate();
mm->set_window_id(window_id);
- mm->set_control(control_mem);
- mm->set_shift(shift_mem);
- mm->set_alt(alt_mem);
+ mm->set_ctrl_pressed(control_mem);
+ mm->set_shift_pressed(shift_mem);
+ mm->set_alt_pressed(alt_mem);
mm->set_pressure((raw->data.mouse.ulButtons & RI_MOUSE_LEFT_BUTTON_DOWN) ? 1.0f : 0.0f);
@@ -2060,11 +2071,11 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA
break;
Ref<InputEventMouseMotion> mm;
- mm.instance();
+ mm.instantiate();
mm->set_window_id(window_id);
- mm->set_control(GetKeyState(VK_CONTROL) < 0);
- mm->set_shift(GetKeyState(VK_SHIFT) < 0);
- mm->set_alt(alt_mem);
+ mm->set_ctrl_pressed(GetKeyState(VK_CONTROL) < 0);
+ mm->set_shift_pressed(GetKeyState(VK_SHIFT) < 0);
+ mm->set_alt_pressed(alt_mem);
mm->set_pressure(windows[window_id].last_pressure);
mm->set_tilt(windows[window_id].last_tilt);
@@ -2189,11 +2200,12 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA
}
// Don't calculate relative mouse movement if we don't have focus in CAPTURED mode.
- if (!windows[window_id].window_has_focus && mouse_mode == MOUSE_MODE_CAPTURED)
+ if (!windows[window_id].window_has_focus && mouse_mode == MOUSE_MODE_CAPTURED) {
break;
+ }
Ref<InputEventMouseMotion> mm;
- mm.instance();
+ mm.instantiate();
mm->set_window_id(window_id);
if (pen_info.penMask & PEN_MASK_PRESSURE) {
@@ -2205,9 +2217,9 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA
mm->set_tilt(Vector2((float)pen_info.tiltX / 90, (float)pen_info.tiltY / 90));
}
- mm->set_control(GetKeyState(VK_CONTROL) < 0);
- mm->set_shift(GetKeyState(VK_SHIFT) < 0);
- mm->set_alt(alt_mem);
+ mm->set_ctrl_pressed(GetKeyState(VK_CONTROL) < 0);
+ mm->set_shift_pressed(GetKeyState(VK_SHIFT) < 0);
+ mm->set_alt_pressed(alt_mem);
mm->set_button_mask(last_button_state);
@@ -2294,18 +2306,19 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA
}
// Don't calculate relative mouse movement if we don't have focus in CAPTURED mode.
- if (!windows[window_id].window_has_focus && mouse_mode == MOUSE_MODE_CAPTURED)
+ if (!windows[window_id].window_has_focus && mouse_mode == MOUSE_MODE_CAPTURED) {
break;
+ }
Ref<InputEventMouseMotion> mm;
- mm.instance();
+ mm.instantiate();
mm->set_window_id(window_id);
- mm->set_control((wParam & MK_CONTROL) != 0);
- mm->set_shift((wParam & MK_SHIFT) != 0);
- mm->set_alt(alt_mem);
+ mm->set_ctrl_pressed((wParam & MK_CONTROL) != 0);
+ mm->set_shift_pressed((wParam & MK_SHIFT) != 0);
+ mm->set_alt_pressed(alt_mem);
if ((tablet_get_current_driver() == "wintab") && wintab_available && windows[window_id].wtctx) {
- // Note: WinTab sends both WT_PACKET and WM_xBUTTONDOWN/UP/MOUSEMOVE events, use mouse 1/0 pressure only when last_pressure was not update recently.
+ // Note: WinTab sends both WT_PACKET and WM_xBUTTONDOWN/UP/MOUSEMOVE events, use mouse 1/0 pressure only when last_pressure was not updated recently.
if (windows[window_id].last_pressure_update < 10) {
windows[window_id].last_pressure_update++;
} else {
@@ -2381,110 +2394,117 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA
case WM_XBUTTONDOWN:
case WM_XBUTTONUP: {
Ref<InputEventMouseButton> mb;
- mb.instance();
+ mb.instantiate();
mb->set_window_id(window_id);
switch (uMsg) {
case WM_LBUTTONDOWN: {
mb->set_pressed(true);
- mb->set_button_index(1);
+ mb->set_button_index(MOUSE_BUTTON_LEFT);
} break;
case WM_LBUTTONUP: {
mb->set_pressed(false);
- mb->set_button_index(1);
+ mb->set_button_index(MOUSE_BUTTON_LEFT);
} break;
case WM_MBUTTONDOWN: {
mb->set_pressed(true);
- mb->set_button_index(3);
+ mb->set_button_index(MOUSE_BUTTON_MIDDLE);
} break;
case WM_MBUTTONUP: {
mb->set_pressed(false);
- mb->set_button_index(3);
+ mb->set_button_index(MOUSE_BUTTON_MIDDLE);
} break;
case WM_RBUTTONDOWN: {
mb->set_pressed(true);
- mb->set_button_index(2);
+ mb->set_button_index(MOUSE_BUTTON_RIGHT);
} break;
case WM_RBUTTONUP: {
mb->set_pressed(false);
- mb->set_button_index(2);
+ mb->set_button_index(MOUSE_BUTTON_RIGHT);
} break;
case WM_LBUTTONDBLCLK: {
mb->set_pressed(true);
- mb->set_button_index(1);
- mb->set_doubleclick(true);
+ mb->set_button_index(MOUSE_BUTTON_LEFT);
+ mb->set_double_click(true);
} break;
case WM_RBUTTONDBLCLK: {
mb->set_pressed(true);
- mb->set_button_index(2);
- mb->set_doubleclick(true);
+ mb->set_button_index(MOUSE_BUTTON_RIGHT);
+ mb->set_double_click(true);
} break;
case WM_MBUTTONDBLCLK: {
mb->set_pressed(true);
- mb->set_button_index(3);
- mb->set_doubleclick(true);
+ mb->set_button_index(MOUSE_BUTTON_MIDDLE);
+ mb->set_double_click(true);
} break;
case WM_MOUSEWHEEL: {
mb->set_pressed(true);
int motion = (short)HIWORD(wParam);
- if (!motion)
+ if (!motion) {
return 0;
+ }
- if (motion > 0)
- mb->set_button_index(BUTTON_WHEEL_UP);
- else
- mb->set_button_index(BUTTON_WHEEL_DOWN);
+ if (motion > 0) {
+ mb->set_button_index(MOUSE_BUTTON_WHEEL_UP);
+ } else {
+ mb->set_button_index(MOUSE_BUTTON_WHEEL_DOWN);
+ }
} break;
case WM_MOUSEHWHEEL: {
mb->set_pressed(true);
int motion = (short)HIWORD(wParam);
- if (!motion)
+ if (!motion) {
return 0;
+ }
if (motion < 0) {
- mb->set_button_index(BUTTON_WHEEL_LEFT);
+ mb->set_button_index(MOUSE_BUTTON_WHEEL_LEFT);
mb->set_factor(fabs((double)motion / (double)WHEEL_DELTA));
} else {
- mb->set_button_index(BUTTON_WHEEL_RIGHT);
+ mb->set_button_index(MOUSE_BUTTON_WHEEL_RIGHT);
mb->set_factor(fabs((double)motion / (double)WHEEL_DELTA));
}
} break;
case WM_XBUTTONDOWN: {
mb->set_pressed(true);
- if (HIWORD(wParam) == XBUTTON1)
- mb->set_button_index(BUTTON_XBUTTON1);
- else
- mb->set_button_index(BUTTON_XBUTTON2);
+ if (HIWORD(wParam) == XBUTTON1) {
+ mb->set_button_index(MOUSE_BUTTON_XBUTTON1);
+ } else {
+ mb->set_button_index(MOUSE_BUTTON_XBUTTON2);
+ }
} break;
case WM_XBUTTONUP: {
mb->set_pressed(false);
- if (HIWORD(wParam) == XBUTTON1)
- mb->set_button_index(BUTTON_XBUTTON1);
- else
- mb->set_button_index(BUTTON_XBUTTON2);
+ if (HIWORD(wParam) == XBUTTON1) {
+ mb->set_button_index(MOUSE_BUTTON_XBUTTON1);
+ } else {
+ mb->set_button_index(MOUSE_BUTTON_XBUTTON2);
+ }
} break;
case WM_XBUTTONDBLCLK: {
mb->set_pressed(true);
- if (HIWORD(wParam) == XBUTTON1)
- mb->set_button_index(BUTTON_XBUTTON1);
- else
- mb->set_button_index(BUTTON_XBUTTON2);
- mb->set_doubleclick(true);
+ if (HIWORD(wParam) == XBUTTON1) {
+ mb->set_button_index(MOUSE_BUTTON_XBUTTON1);
+ } else {
+ mb->set_button_index(MOUSE_BUTTON_XBUTTON2);
+ }
+ mb->set_double_click(true);
} break;
default: {
return 0;
}
}
- mb->set_control((wParam & MK_CONTROL) != 0);
- mb->set_shift((wParam & MK_SHIFT) != 0);
- mb->set_alt(alt_mem);
- //mb->get_alt()=(wParam&MK_MENU)!=0;
- if (mb->is_pressed())
- last_button_state |= (1 << (mb->get_button_index() - 1));
- else
- last_button_state &= ~(1 << (mb->get_button_index() - 1));
+ mb->set_ctrl_pressed((wParam & MK_CONTROL) != 0);
+ mb->set_shift_pressed((wParam & MK_SHIFT) != 0);
+ mb->set_alt_pressed(alt_mem);
+ //mb->is_alt_pressed()=(wParam&MK_MENU)!=0;
+ if (mb->is_pressed()) {
+ last_button_state |= MouseButton(1 << (mb->get_button_index() - 1));
+ } else {
+ last_button_state &= (MouseButton) ~(1 << (mb->get_button_index() - 1));
+ }
mb->set_button_mask(last_button_state);
mb->set_position(Vector2(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)));
@@ -2523,7 +2543,7 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA
//send release for mouse wheel
Ref<InputEventMouseButton> mbd = mb->duplicate();
mbd->set_window_id(window_id);
- last_button_state &= ~(1 << (mbd->get_button_index() - 1));
+ last_button_state &= (MouseButton) ~(1 << (mbd->get_button_index() - 1));
mbd->set_button_mask(last_button_state);
mbd->set_pressed(false);
Input::get_singleton()->accumulate_input_event(mbd);
@@ -2566,6 +2586,8 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA
windows[window_id].preserve_window_size = false;
window_set_size(Size2(windows[window_id].width, windows[window_id].height), window_id);
}
+ } else {
+ windows[window_id].preserve_window_size = true;
}
if (!windows[window_id].rect_changed_callback.is_null()) {
@@ -2724,7 +2746,7 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA
} break;
case WM_SETCURSOR: {
if (LOWORD(lParam) == HTCLIENT) {
- if (windows[window_id].window_has_focus && (mouse_mode == MOUSE_MODE_HIDDEN || mouse_mode == MOUSE_MODE_CAPTURED)) {
+ if (windows[window_id].window_has_focus && (mouse_mode == MOUSE_MODE_HIDDEN || mouse_mode == MOUSE_MODE_CAPTURED || mouse_mode == MOUSE_MODE_CONFINED_HIDDEN)) {
//Hide the cursor
if (hCursor == nullptr) {
hCursor = SetCursor(nullptr);
@@ -2830,20 +2852,20 @@ void DisplayServerWindows::_process_key_events() {
prev_wc = 0;
}
Ref<InputEventKey> k;
- k.instance();
+ k.instantiate();
k->set_window_id(ke.window_id);
- k->set_shift(ke.shift);
- k->set_alt(ke.alt);
- k->set_control(ke.control);
- k->set_metakey(ke.meta);
+ k->set_shift_pressed(ke.shift);
+ k->set_alt_pressed(ke.alt);
+ k->set_ctrl_pressed(ke.control);
+ k->set_meta_pressed(ke.meta);
k->set_pressed(true);
k->set_keycode(KeyMappingWindows::get_keysym(ke.wParam));
k->set_physical_keycode(KeyMappingWindows::get_scansym((ke.lParam >> 16) & 0xFF, ke.lParam & (1 << 24)));
k->set_unicode(unicode);
if (k->get_unicode() && gr_mem) {
- k->set_alt(false);
- k->set_control(false);
+ k->set_alt_pressed(false);
+ k->set_ctrl_pressed(false);
}
if (k->get_unicode() < 32)
@@ -2857,13 +2879,13 @@ void DisplayServerWindows::_process_key_events() {
case WM_KEYUP:
case WM_KEYDOWN: {
Ref<InputEventKey> k;
- k.instance();
+ k.instantiate();
k->set_window_id(ke.window_id);
- k->set_shift(ke.shift);
- k->set_alt(ke.alt);
- k->set_control(ke.control);
- k->set_metakey(ke.meta);
+ k->set_shift_pressed(ke.shift);
+ k->set_alt_pressed(ke.alt);
+ k->set_ctrl_pressed(ke.control);
+ k->set_meta_pressed(ke.meta);
k->set_pressed(ke.uMsg == WM_KEYDOWN);
@@ -2898,8 +2920,8 @@ void DisplayServerWindows::_process_key_events() {
k->set_unicode(unicode);
}
if (k->get_unicode() && gr_mem) {
- k->set_alt(false);
- k->set_control(false);
+ k->set_alt_pressed(false);
+ k->set_ctrl_pressed(false);
}
if (k->get_unicode() < 32)
@@ -2955,7 +2977,7 @@ void DisplayServerWindows::_update_tablet_ctx(const String &p_old_driver, const
}
}
-DisplayServer::WindowID DisplayServerWindows::_create_window(WindowMode p_mode, uint32_t p_flags, const Rect2i &p_rect) {
+DisplayServer::WindowID DisplayServerWindows::_create_window(WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Rect2i &p_rect) {
DWORD dwExStyle;
DWORD dwStyle;
@@ -3017,7 +3039,7 @@ DisplayServer::WindowID DisplayServerWindows::_create_window(WindowMode p_mode,
#ifdef VULKAN_ENABLED
if (rendering_driver == "vulkan") {
- if (context_vulkan->window_create(id, wd.hWnd, hInstance, WindowRect.right - WindowRect.left, WindowRect.bottom - WindowRect.top) == -1) {
+ if (context_vulkan->window_create(id, p_vsync_mode, wd.hWnd, hInstance, WindowRect.right - WindowRect.left, WindowRect.bottom - WindowRect.top) == -1) {
memdelete(context_vulkan);
context_vulkan = nullptr;
windows.erase(id);
@@ -3138,7 +3160,7 @@ void DisplayServerWindows::tablet_set_current_driver(const String &p_driver) {
}
}
-DisplayServerWindows::DisplayServerWindows(const String &p_rendering_driver, WindowMode p_mode, uint32_t p_flags, const Vector2i &p_resolution, Error &r_error) {
+DisplayServerWindows::DisplayServerWindows(const String &p_rendering_driver, WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i &p_resolution, Error &r_error) {
drop_events = false;
key_event_pos = 0;
@@ -3257,7 +3279,6 @@ DisplayServerWindows::DisplayServerWindows(const String &p_rendering_driver, Win
}
context_gles2->set_use_vsync(video_mode.use_vsync);
- set_vsync_via_compositor(video_mode.vsync_via_compositor);
if (RasterizerGLES2::is_viable() == OK) {
RasterizerGLES2::register_config();
@@ -3273,7 +3294,7 @@ DisplayServerWindows::DisplayServerWindows(const String &p_rendering_driver, Win
(screen_get_size(0).width - p_resolution.width) / 2,
(screen_get_size(0).height - p_resolution.height) / 2);
- WindowID main_window = _create_window(p_mode, 0, Rect2i(window_position, p_resolution));
+ WindowID main_window = _create_window(p_mode, p_vsync_mode, 0, Rect2i(window_position, p_resolution));
ERR_FAIL_COND_MSG(main_window == INVALID_WINDOW_ID, "Failed to create main window.");
for (int i = 0; i < WINDOW_FLAG_MAX; i++) {
@@ -3334,8 +3355,8 @@ Vector<String> DisplayServerWindows::get_rendering_drivers_func() {
return drivers;
}
-DisplayServer *DisplayServerWindows::create_func(const String &p_rendering_driver, WindowMode p_mode, uint32_t p_flags, const Vector2i &p_resolution, Error &r_error) {
- DisplayServer *ds = memnew(DisplayServerWindows(p_rendering_driver, p_mode, p_flags, p_resolution, r_error));
+DisplayServer *DisplayServerWindows::create_func(const String &p_rendering_driver, WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i &p_resolution, Error &r_error) {
+ DisplayServer *ds = memnew(DisplayServerWindows(p_rendering_driver, p_mode, p_vsync_mode, p_flags, p_resolution, r_error));
if (r_error != OK) {
ds->alert("Your video card driver does not support any of the supported Vulkan versions.\n"
"Please update your drivers or if you have a very old or integrated GPU upgrade it.",
diff --git a/platform/windows/display_server_windows.h b/platform/windows/display_server_windows.h
index a734077e59..394bed79b0 100644
--- a/platform/windows/display_server_windows.h
+++ b/platform/windows/display_server_windows.h
@@ -389,7 +389,7 @@ class DisplayServerWindows : public DisplayServer {
JoypadWindows *joypad;
- WindowID _create_window(WindowMode p_mode, uint32_t p_flags, const Rect2i &p_rect);
+ WindowID _create_window(WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Rect2i &p_rect);
WindowID window_id_counter = MAIN_WINDOW_ID;
Map<WindowID, WindowData> windows;
@@ -408,7 +408,7 @@ class DisplayServerWindows : public DisplayServer {
bool shift_mem = false;
bool control_mem = false;
bool meta_mem = false;
- uint32_t last_button_state = 0;
+ MouseButton last_button_state = MOUSE_BUTTON_NONE;
bool use_raw_input = false;
bool drop_events = false;
bool in_dispatch_input_event = false;
@@ -449,7 +449,7 @@ public:
virtual void mouse_warp_to_position(const Point2i &p_to);
virtual Point2i mouse_get_position() const;
- virtual int mouse_get_button_state() const;
+ virtual MouseButton mouse_get_button_state() const;
virtual void clipboard_set(const String &p_text);
virtual String clipboard_get() const;
@@ -469,7 +469,7 @@ public:
virtual Vector<DisplayServer::WindowID> get_window_list() const;
- virtual WindowID create_sub_window(WindowMode p_mode, uint32_t p_flags, const Rect2i &p_rect = Rect2i());
+ virtual WindowID create_sub_window(WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Rect2i &p_rect = Rect2i());
virtual void show_window(WindowID p_window);
virtual void delete_sub_window(WindowID p_window);
@@ -525,6 +525,9 @@ public:
virtual void window_set_ime_active(const bool p_active, WindowID p_window = MAIN_WINDOW_ID);
virtual void window_set_ime_position(const Point2i &p_pos, WindowID p_window = MAIN_WINDOW_ID);
+ virtual void window_set_vsync_mode(DisplayServer::VSyncMode p_vsync_mode, WindowID p_window = MAIN_WINDOW_ID) override;
+ virtual DisplayServer::VSyncMode window_get_vsync_mode(WindowID p_vsync_mode) const override;
+
virtual void console_set_visible(bool p_enabled);
virtual bool is_console_visible() const;
@@ -558,16 +561,13 @@ public:
virtual void set_native_icon(const String &p_filename);
virtual void set_icon(const Ref<Image> &p_icon);
- virtual void vsync_set_use_via_compositor(bool p_enable);
- virtual bool vsync_is_using_via_compositor() const;
-
virtual void set_context(Context p_context);
- static DisplayServer *create_func(const String &p_rendering_driver, WindowMode p_mode, uint32_t p_flags, const Vector2i &p_resolution, Error &r_error);
+ static DisplayServer *create_func(const String &p_rendering_driver, WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i &p_resolution, Error &r_error);
static Vector<String> get_rendering_drivers_func();
static void register_windows_driver();
- DisplayServerWindows(const String &p_rendering_driver, WindowMode p_mode, uint32_t p_flags, const Vector2i &p_resolution, Error &r_error);
+ DisplayServerWindows(const String &p_rendering_driver, WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i &p_resolution, Error &r_error);
~DisplayServerWindows();
};
diff --git a/platform/windows/export/export.cpp b/platform/windows/export/export.cpp
index 222597b3ff..10f953f2ec 100644
--- a/platform/windows/export/export.cpp
+++ b/platform/windows/export/export.cpp
@@ -28,7 +28,7 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#include "core/os/file_access.h"
+#include "core/io/file_access.h"
#include "core/os/os.h"
#include "editor/editor_export.h"
#include "editor/editor_node.h"
@@ -310,7 +310,7 @@ Error EditorExportPlatformWindows::_code_sign(const Ref<EditorExportPreset> &p_p
args.push_back(p_path);
#ifndef WINDOWS_ENABLED
args.push_back("-out");
- args.push_back(p_path);
+ args.push_back(p_path + "_signed");
#endif
String str;
@@ -326,6 +326,16 @@ Error EditorExportPlatformWindows::_code_sign(const Ref<EditorExportPreset> &p_p
return FAILED;
}
+#ifndef WINDOWS_ENABLED
+ DirAccessRef tmp_dir = DirAccess::create_for_path(p_path.get_base_dir());
+
+ err = tmp_dir->remove(p_path);
+ ERR_FAIL_COND_V(err != OK, err);
+
+ err = tmp_dir->rename(p_path + "_signed", p_path);
+ ERR_FAIL_COND_V(err != OK, err);
+#endif
+
return OK;
}
@@ -344,11 +354,11 @@ void register_windows_exporter() {
#endif
Ref<EditorExportPlatformWindows> platform;
- platform.instance();
+ platform.instantiate();
Ref<Image> img = memnew(Image(_windows_logo));
Ref<ImageTexture> logo;
- logo.instance();
+ logo.instantiate();
logo->create_from_image(img);
platform->set_logo(logo);
platform->set_name("Windows Desktop");
diff --git a/platform/windows/godot.natvis b/platform/windows/godot.natvis
index 857c6a88f1..bb855e4ac8 100644
--- a/platform/windows/godot.natvis
+++ b/platform/windows/godot.natvis
@@ -40,13 +40,13 @@
<DisplayString Condition="type == Variant::TRANSFORM2D">{_data._transform2d}</DisplayString>
<DisplayString Condition="type == Variant::AABB">{_data._aabb}</DisplayString>
<DisplayString Condition="type == Variant::BASIS">{_data._basis}</DisplayString>
- <DisplayString Condition="type == Variant::TRANSFORM">{_data._transform}</DisplayString>
+ <DisplayString Condition="type == Variant::TRANSFORM3D">{_data._transform}</DisplayString>
<DisplayString Condition="type == Variant::STRING">{*(String *)_data._mem}</DisplayString>
<DisplayString Condition="type == Variant::VECTOR2">{*(Vector2 *)_data._mem}</DisplayString>
<DisplayString Condition="type == Variant::RECT2">{*(Rect2 *)_data._mem}</DisplayString>
<DisplayString Condition="type == Variant::VECTOR3">{*(Vector3 *)_data._mem}</DisplayString>
<DisplayString Condition="type == Variant::PLANE">{*(Plane *)_data._mem}</DisplayString>
- <DisplayString Condition="type == Variant::QUAT">{*(Quat *)_data._mem}</DisplayString>
+ <DisplayString Condition="type == Variant::QUATERNION">{*(Quaternion *)_data._mem}</DisplayString>
<DisplayString Condition="type == Variant::COLOR">{*(Color *)_data._mem}</DisplayString>
<DisplayString Condition="type == Variant::NODE_PATH">{*(NodePath *)_data._mem}</DisplayString>
<DisplayString Condition="type == Variant::RID">{*(::RID *)_data._mem}</DisplayString>
@@ -72,13 +72,13 @@
<Item Name="[value]" Condition="type == Variant::TRANSFORM2D">_data._transform2d</Item>
<Item Name="[value]" Condition="type == Variant::AABB">_data._aabb</Item>
<Item Name="[value]" Condition="type == Variant::BASIS">_data._basis</Item>
- <Item Name="[value]" Condition="type == Variant::TRANSFORM">_data._transform</Item>
+ <Item Name="[value]" Condition="type == Variant::TRANSFORM3D">_data._transform</Item>
<Item Name="[value]" Condition="type == Variant::STRING">*(String *)_data._mem</Item>
<Item Name="[value]" Condition="type == Variant::VECTOR2">*(Vector2 *)_data._mem</Item>
<Item Name="[value]" Condition="type == Variant::RECT2">*(Rect2 *)_data._mem</Item>
<Item Name="[value]" Condition="type == Variant::VECTOR3">*(Vector3 *)_data._mem</Item>
<Item Name="[value]" Condition="type == Variant::PLANE">*(Plane *)_data._mem</Item>
- <Item Name="[value]" Condition="type == Variant::QUAT">*(Quat *)_data._mem</Item>
+ <Item Name="[value]" Condition="type == Variant::QUATERNION">*(Quaternion *)_data._mem</Item>
<Item Name="[value]" Condition="type == Variant::COLOR">*(Color *)_data._mem</Item>
<Item Name="[value]" Condition="type == Variant::NODE_PATH">*(NodePath *)_data._mem</Item>
<Item Name="[value]" Condition="type == Variant::RID">*(::RID *)_data._mem</Item>
@@ -128,8 +128,8 @@
</Expand>
</Type>
- <Type Name="Quat">
- <DisplayString>Quat {{{x},{y},{z},{w}}}</DisplayString>
+ <Type Name="Quaternion">
+ <DisplayString>Quaternion {{{x},{y},{z},{w}}}</DisplayString>
<Expand>
<Item Name="x">x</Item>
<Item Name="y">y</Item>
diff --git a/platform/windows/joypad_windows.cpp b/platform/windows/joypad_windows.cpp
index f46a0dbe2e..94da63e49d 100644
--- a/platform/windows/joypad_windows.cpp
+++ b/platform/windows/joypad_windows.cpp
@@ -110,12 +110,11 @@ bool JoypadWindows::is_xinput_device(const GUID *p_guid) {
if (GetRawInputDeviceList(nullptr, &dev_list_count, sizeof(RAWINPUTDEVICELIST)) == (UINT)-1) {
return false;
}
- dev_list = (PRAWINPUTDEVICELIST)malloc(sizeof(RAWINPUTDEVICELIST) * dev_list_count);
- if (!dev_list)
- return false;
+ dev_list = (PRAWINPUTDEVICELIST)memalloc(sizeof(RAWINPUTDEVICELIST) * dev_list_count);
+ ERR_FAIL_NULL_V_MSG(dev_list, false, "Out of memory.");
if (GetRawInputDeviceList(dev_list, &dev_list_count, sizeof(RAWINPUTDEVICELIST)) == (UINT)-1) {
- free(dev_list);
+ memfree(dev_list);
return false;
}
for (unsigned int i = 0; i < dev_list_count; i++) {
@@ -130,11 +129,11 @@ bool JoypadWindows::is_xinput_device(const GUID *p_guid) {
(MAKELONG(rdi.hid.dwVendorId, rdi.hid.dwProductId) == (LONG)p_guid->Data1) &&
(GetRawInputDeviceInfoA(dev_list[i].hDevice, RIDI_DEVICENAME, &dev_name, &nameSize) != (UINT)-1) &&
(strstr(dev_name, "IG_") != nullptr)) {
- free(dev_list);
+ memfree(dev_list);
return true;
}
}
- free(dev_list);
+ memfree(dev_list);
return false;
}
@@ -331,7 +330,7 @@ void JoypadWindows::process_joypads() {
if (joy.state.dwPacketNumber != joy.last_packet) {
int button_mask = XINPUT_GAMEPAD_DPAD_UP;
for (int j = 0; j <= 16; j++) {
- input->joy_button(joy.id, j, joy.state.Gamepad.wButtons & button_mask);
+ input->joy_button(joy.id, (JoyButton)j, joy.state.Gamepad.wButtons & button_mask);
button_mask = button_mask * 2;
}
@@ -382,12 +381,12 @@ void JoypadWindows::process_joypads() {
for (int j = 0; j < 128; j++) {
if (js.rgbButtons[j] & 0x80) {
if (!joy->last_buttons[j]) {
- input->joy_button(joy->id, j, true);
+ input->joy_button(joy->id, (JoyButton)j, true);
joy->last_buttons[j] = true;
}
} else {
if (joy->last_buttons[j]) {
- input->joy_button(joy->id, j, false);
+ input->joy_button(joy->id, (JoyButton)j, false);
joy->last_buttons[j] = false;
}
}
@@ -401,7 +400,7 @@ void JoypadWindows::process_joypads() {
for (int j = 0; j < joy->joy_axis.size(); j++) {
for (int k = 0; k < count; k++) {
if (joy->joy_axis[j] == axes[k]) {
- input->joy_axis(joy->id, j, axis_correct(values[k]));
+ input->joy_axis(joy->id, (JoyAxis)j, axis_correct(values[k]));
break;
};
};
@@ -411,44 +410,44 @@ void JoypadWindows::process_joypads() {
}
void JoypadWindows::post_hat(int p_device, DWORD p_dpad) {
- int dpad_val = 0;
+ HatMask dpad_val = (HatMask)0;
// Should be -1 when centered, but according to docs:
// "Some drivers report the centered position of the POV indicator as 65,535. Determine whether the indicator is centered as follows:
// BOOL POVCentered = (LOWORD(dwPOV) == 0xFFFF);"
// https://docs.microsoft.com/en-us/previous-versions/windows/desktop/ee416628(v%3Dvs.85)#remarks
if (LOWORD(p_dpad) == 0xFFFF) {
- dpad_val = Input::HAT_MASK_CENTER;
+ dpad_val = (HatMask)HatMask::HAT_MASK_CENTER;
}
if (p_dpad == 0) {
- dpad_val = Input::HAT_MASK_UP;
+ dpad_val = (HatMask)HatMask::HAT_MASK_UP;
} else if (p_dpad == 4500) {
- dpad_val = (Input::HAT_MASK_UP | Input::HAT_MASK_RIGHT);
+ dpad_val = (HatMask)(HatMask::HAT_MASK_UP | HatMask::HAT_MASK_RIGHT);
} else if (p_dpad == 9000) {
- dpad_val = Input::HAT_MASK_RIGHT;
+ dpad_val = (HatMask)HatMask::HAT_MASK_RIGHT;
} else if (p_dpad == 13500) {
- dpad_val = (Input::HAT_MASK_RIGHT | Input::HAT_MASK_DOWN);
+ dpad_val = (HatMask)(HatMask::HAT_MASK_RIGHT | HatMask::HAT_MASK_DOWN);
} else if (p_dpad == 18000) {
- dpad_val = Input::HAT_MASK_DOWN;
+ dpad_val = (HatMask)HatMask::HAT_MASK_DOWN;
} else if (p_dpad == 22500) {
- dpad_val = (Input::HAT_MASK_DOWN | Input::HAT_MASK_LEFT);
+ dpad_val = (HatMask)(HatMask::HAT_MASK_DOWN | HatMask::HAT_MASK_LEFT);
} else if (p_dpad == 27000) {
- dpad_val = Input::HAT_MASK_LEFT;
+ dpad_val = (HatMask)HatMask::HAT_MASK_LEFT;
} else if (p_dpad == 31500) {
- dpad_val = (Input::HAT_MASK_LEFT | Input::HAT_MASK_UP);
+ dpad_val = (HatMask)(HatMask::HAT_MASK_LEFT | HatMask::HAT_MASK_UP);
}
input->joy_hat(p_device, dpad_val);
};
-Input::JoyAxis JoypadWindows::axis_correct(int p_val, bool p_xinput, bool p_trigger, bool p_negate) const {
- Input::JoyAxis jx;
+Input::JoyAxisValue JoypadWindows::axis_correct(int p_val, bool p_xinput, bool p_trigger, bool p_negate) const {
+ Input::JoyAxisValue jx;
if (Math::abs(p_val) < MIN_JOY_AXIS) {
jx.min = p_trigger ? 0 : -1;
jx.value = 0.0f;
diff --git a/platform/windows/joypad_windows.h b/platform/windows/joypad_windows.h
index 4727b4a14c..757fb54fb3 100644
--- a/platform/windows/joypad_windows.h
+++ b/platform/windows/joypad_windows.h
@@ -132,7 +132,7 @@ private:
void joypad_vibration_start_xinput(int p_device, float p_weak_magnitude, float p_strong_magnitude, float p_duration, uint64_t p_timestamp);
void joypad_vibration_stop_xinput(int p_device, uint64_t p_timestamp);
- Input::JoyAxis axis_correct(int p_val, bool p_xinput = false, bool p_trigger = false, bool p_negate = false) const;
+ Input::JoyAxisValue axis_correct(int p_val, bool p_xinput = false, bool p_trigger = false, bool p_negate = false) const;
XInputGetState_t xinput_get_state;
XInputSetState_t xinput_set_state;
};
diff --git a/platform/windows/key_mapping_windows.cpp b/platform/windows/key_mapping_windows.cpp
index 3312c91932..c367c69826 100644
--- a/platform/windows/key_mapping_windows.cpp
+++ b/platform/windows/key_mapping_windows.cpp
@@ -47,7 +47,7 @@ static _WinTranslatePair _vk_to_keycode[] = {
{ KEY_SHIFT, VK_SHIFT }, //(0x10)
- { KEY_CONTROL, VK_CONTROL }, //(0x11)
+ { KEY_CTRL, VK_CONTROL }, //(0x11)
{ KEY_ALT, VK_MENU }, //(0x12)
@@ -166,8 +166,8 @@ static _WinTranslatePair _vk_to_keycode[] = {
{ KEY_SCROLLLOCK, VK_SCROLL }, // (0x91)
{ KEY_SHIFT, VK_LSHIFT }, // (0xA0)
{ KEY_SHIFT, VK_RSHIFT }, // (0xA1)
- { KEY_CONTROL, VK_LCONTROL }, // (0xA2)
- { KEY_CONTROL, VK_RCONTROL }, // (0xA3)
+ { KEY_CTRL, VK_LCONTROL }, // (0xA2)
+ { KEY_CTRL, VK_RCONTROL }, // (0xA3)
{ KEY_MENU, VK_LMENU }, // (0xA4)
{ KEY_MENU, VK_RMENU }, // (0xA5)
@@ -265,7 +265,7 @@ static _WinTranslatePair _scancode_to_keycode[] = {
{ KEY_BRACELEFT, 0x1A },
{ KEY_BRACERIGHT, 0x1B },
{ KEY_ENTER, 0x1C },
- { KEY_CONTROL, 0x1D },
+ { KEY_CTRL, 0x1D },
{ KEY_A, 0x1E },
{ KEY_S, 0x1F },
{ KEY_D, 0x20 },
diff --git a/platform/windows/os_windows.cpp b/platform/windows/os_windows.cpp
index 3280a36e9b..56d673afc3 100644
--- a/platform/windows/os_windows.cpp
+++ b/platform/windows/os_windows.cpp
@@ -204,7 +204,7 @@ void OS_Windows::initialize() {
current_pi.pi.hProcess = GetCurrentProcess();
process_map->insert(GetCurrentProcessId(), current_pi);
- IP_Unix::make_default();
+ IPUnix::make_default();
main_loop = nullptr;
}
@@ -315,8 +315,8 @@ OS::Time OS_Windows::get_time(bool utc) const {
Time time;
time.hour = systemtime.wHour;
- time.min = systemtime.wMinute;
- time.sec = systemtime.wSecond;
+ time.minute = systemtime.wMinute;
+ time.second = systemtime.wSecond;
return time;
}
@@ -334,7 +334,7 @@ OS::TimeZoneInfo OS_Windows::get_time_zone_info() const {
}
// Bias value returned by GetTimeZoneInformation is inverted of what we expect
- // For example on GMT-3 GetTimeZoneInformation return a Bias of 180, so invert the value to get -180
+ // For example, on GMT-3 GetTimeZoneInformation return a Bias of 180, so invert the value to get -180
ret.bias = -info.Bias;
return ret;
}
@@ -631,31 +631,45 @@ MainLoop *OS_Windows::get_main_loop() const {
}
String OS_Windows::get_config_path() const {
- if (has_environment("XDG_CONFIG_HOME")) { // unlikely, but after all why not?
- return get_environment("XDG_CONFIG_HOME");
- } else if (has_environment("APPDATA")) {
- return get_environment("APPDATA");
- } else {
- return ".";
+ // The XDG Base Directory specification technically only applies on Linux/*BSD, but it doesn't hurt to support it on Windows as well.
+ if (has_environment("XDG_CONFIG_HOME")) {
+ if (get_environment("XDG_CONFIG_HOME").is_absolute_path()) {
+ return get_environment("XDG_CONFIG_HOME").replace("\\", "/");
+ } else {
+ WARN_PRINT_ONCE("`XDG_CONFIG_HOME` is a relative path. Ignoring its value and falling back to `%APPDATA%` or `.` per the XDG Base Directory specification.");
+ }
}
+ if (has_environment("APPDATA")) {
+ return get_environment("APPDATA").replace("\\", "/");
+ }
+ return ".";
}
String OS_Windows::get_data_path() const {
+ // The XDG Base Directory specification technically only applies on Linux/*BSD, but it doesn't hurt to support it on Windows as well.
if (has_environment("XDG_DATA_HOME")) {
- return get_environment("XDG_DATA_HOME");
- } else {
- return get_config_path();
+ if (get_environment("XDG_DATA_HOME").is_absolute_path()) {
+ return get_environment("XDG_DATA_HOME").replace("\\", "/");
+ } else {
+ WARN_PRINT_ONCE("`XDG_DATA_HOME` is a relative path. Ignoring its value and falling back to `get_config_path()` per the XDG Base Directory specification.");
+ }
}
+ return get_config_path();
}
String OS_Windows::get_cache_path() const {
+ // The XDG Base Directory specification technically only applies on Linux/*BSD, but it doesn't hurt to support it on Windows as well.
if (has_environment("XDG_CACHE_HOME")) {
- return get_environment("XDG_CACHE_HOME");
- } else if (has_environment("TEMP")) {
- return get_environment("TEMP");
- } else {
- return get_config_path();
+ if (get_environment("XDG_CACHE_HOME").is_absolute_path()) {
+ return get_environment("XDG_CACHE_HOME").replace("\\", "/");
+ } else {
+ WARN_PRINT_ONCE("`XDG_CACHE_HOME` is a relative path. Ignoring its value and falling back to `%TEMP%` or `get_config_path()` per the XDG Base Directory specification.");
+ }
+ }
+ if (has_environment("TEMP")) {
+ return get_environment("TEMP").replace("\\", "/");
}
+ return get_config_path();
}
// Get properly capitalized engine name for system paths
@@ -696,7 +710,7 @@ String OS_Windows::get_system_dir(SystemDir p_dir) const {
PWSTR szPath;
HRESULT res = SHGetKnownFolderPath(id, 0, nullptr, &szPath);
ERR_FAIL_COND_V(res != S_OK, String());
- String path = String::utf16((const char16_t *)szPath);
+ String path = String::utf16((const char16_t *)szPath).replace("\\", "/");
CoTaskMemFree(szPath);
return path;
}
diff --git a/platform/windows/vulkan_context_win.cpp b/platform/windows/vulkan_context_win.cpp
index e5e176ab93..191792b329 100644
--- a/platform/windows/vulkan_context_win.cpp
+++ b/platform/windows/vulkan_context_win.cpp
@@ -35,18 +35,17 @@ const char *VulkanContextWindows::_get_platform_surface_extension() const {
return VK_KHR_WIN32_SURFACE_EXTENSION_NAME;
}
-int VulkanContextWindows::window_create(DisplayServer::WindowID p_window_id, HWND p_window, HINSTANCE p_instance, int p_width, int p_height) {
+int VulkanContextWindows::window_create(DisplayServer::WindowID p_window_id, DisplayServer::VSyncMode p_vsync_mode, HWND p_window, HINSTANCE p_instance, int p_width, int p_height) {
VkWin32SurfaceCreateInfoKHR createInfo;
createInfo.sType = VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR;
createInfo.pNext = nullptr;
createInfo.flags = 0;
createInfo.hinstance = p_instance;
createInfo.hwnd = p_window;
-
VkSurfaceKHR surface;
VkResult err = vkCreateWin32SurfaceKHR(_get_instance(), &createInfo, nullptr, &surface);
ERR_FAIL_COND_V(err, -1);
- return _window_create(p_window_id, surface, p_width, p_height);
+ return _window_create(p_window_id, p_vsync_mode, surface, p_width, p_height);
}
VulkanContextWindows::VulkanContextWindows() {
diff --git a/platform/windows/vulkan_context_win.h b/platform/windows/vulkan_context_win.h
index 4fe987218d..39dd2641fd 100644
--- a/platform/windows/vulkan_context_win.h
+++ b/platform/windows/vulkan_context_win.h
@@ -38,7 +38,7 @@ class VulkanContextWindows : public VulkanContext {
virtual const char *_get_platform_surface_extension() const;
public:
- int window_create(DisplayServer::WindowID p_window_id, HWND p_window, HINSTANCE p_instance, int p_width, int p_height);
+ int window_create(DisplayServer::WindowID p_window_id, DisplayServer::VSyncMode p_vsync_mode, HWND p_window, HINSTANCE p_instance, int p_width, int p_height);
VulkanContextWindows();
~VulkanContextWindows();
diff --git a/platform/windows/windows_terminal_logger.cpp b/platform/windows/windows_terminal_logger.cpp
index 56b620a6d9..8cab7ca521 100644
--- a/platform/windows/windows_terminal_logger.cpp
+++ b/platform/windows/windows_terminal_logger.cpp
@@ -53,7 +53,8 @@ void WindowsTerminalLogger::logv(const char *p_format, va_list p_list, bool p_er
if (wlen < 0)
return;
- wchar_t *wbuf = (wchar_t *)malloc((len + 1) * sizeof(wchar_t));
+ wchar_t *wbuf = (wchar_t *)memalloc((len + 1) * sizeof(wchar_t));
+ ERR_FAIL_NULL_MSG(wbuf, "Out of memory.");
MultiByteToWideChar(CP_UTF8, 0, buf, len, wbuf, wlen);
wbuf[wlen] = 0;
@@ -62,7 +63,7 @@ void WindowsTerminalLogger::logv(const char *p_format, va_list p_list, bool p_er
else
wprintf(L"%ls", wbuf);
- free(wbuf);
+ memfree(wbuf);
#ifdef DEBUG_ENABLED
fflush(stdout);
@@ -107,47 +108,47 @@ void WindowsTerminalLogger::log_error(const char *p_function, const char *p_file
SetConsoleTextAttribute(hCon, basecol | FOREGROUND_INTENSITY);
switch (p_type) {
case ERR_ERROR:
- logf("ERROR:");
+ logf_error("ERROR:");
break;
case ERR_WARNING:
- logf("WARNING:");
+ logf_error("WARNING:");
break;
case ERR_SCRIPT:
- logf("SCRIPT ERROR:");
+ logf_error("SCRIPT ERROR:");
break;
case ERR_SHADER:
- logf("SHADER ERROR:");
+ logf_error("SHADER ERROR:");
break;
}
SetConsoleTextAttribute(hCon, basecol);
if (p_rationale && p_rationale[0]) {
- logf(" %s\n", p_rationale);
+ logf_error(" %s\n", p_rationale);
} else {
- logf(" %s\n", p_code);
+ logf_error(" %s\n", p_code);
}
// `FOREGROUND_INTENSITY` alone results in gray text.
SetConsoleTextAttribute(hCon, FOREGROUND_INTENSITY);
switch (p_type) {
case ERR_ERROR:
- logf(" at: ");
+ logf_error(" at: ");
break;
case ERR_WARNING:
- logf(" at: ");
+ logf_error(" at: ");
break;
case ERR_SCRIPT:
- logf(" at: ");
+ logf_error(" at: ");
break;
case ERR_SHADER:
- logf(" at: ");
+ logf_error(" at: ");
break;
}
if (p_rationale && p_rationale[0]) {
- logf("(%s:%i)\n", p_file, p_line);
+ logf_error("(%s:%i)\n", p_file, p_line);
} else {
- logf("%s (%s:%i)\n", p_function, p_file, p_line);
+ logf_error("%s (%s:%i)\n", p_function, p_file, p_line);
}
SetConsoleTextAttribute(hCon, sbi.wAttributes);