summaryrefslogtreecommitdiff
path: root/platform
diff options
context:
space:
mode:
Diffstat (limited to 'platform')
-rw-r--r--platform/android/api/java_class_wrapper.h1
-rw-r--r--platform/android/audio_driver_opensl.h1
-rw-r--r--platform/android/export/export.cpp142
-rw-r--r--platform/android/export/gradle_export_util.h65
-rw-r--r--platform/android/file_access_android.cpp1
-rw-r--r--platform/android/java/app/build.gradle39
-rw-r--r--platform/android/java/app/config.gradle40
-rw-r--r--platform/android/java/build.gradle2
-rw-r--r--platform/android/java/lib/build.gradle34
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/Godot.java2
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/GodotIO.java6
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/vulkan/VkRenderer.kt1
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/vulkan/VkSurfaceView.kt1
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/vulkan/VkThread.kt2
-rw-r--r--platform/iphone/SCsub2
-rw-r--r--platform/iphone/display_server_iphone.mm11
-rw-r--r--platform/iphone/export/export.cpp14
-rw-r--r--platform/iphone/godot_app_delegate.h41
-rw-r--r--platform/iphone/godot_app_delegate.m497
-rw-r--r--platform/iphone/godot_view.h4
-rw-r--r--platform/iphone/godot_view.mm35
-rw-r--r--platform/iphone/keyboard_input_view.h37
-rw-r--r--platform/iphone/keyboard_input_view.mm195
-rw-r--r--platform/iphone/main.m5
-rw-r--r--platform/iphone/native_video_view.m6
-rw-r--r--platform/iphone/view_controller.h2
-rw-r--r--platform/iphone/view_controller.mm14
-rw-r--r--platform/javascript/.eslintrc.engine.js10
-rw-r--r--platform/javascript/.eslintrc.js43
-rw-r--r--platform/javascript/.eslintrc.libs.js22
-rw-r--r--platform/javascript/SCsub23
-rw-r--r--platform/javascript/detect.py1
-rw-r--r--platform/javascript/display_server_javascript.cpp2
-rw-r--r--platform/javascript/engine/preloader.js139
-rw-r--r--platform/javascript/export/export.cpp6
-rw-r--r--platform/javascript/js/engine/engine.externs.js (renamed from platform/javascript/engine/externs.js)0
-rw-r--r--platform/javascript/js/engine/engine.js (renamed from platform/javascript/engine/engine.js)150
-rw-r--r--platform/javascript/js/engine/preloader.js127
-rw-r--r--platform/javascript/js/engine/utils.js (renamed from platform/javascript/engine/utils.js)43
-rw-r--r--platform/javascript/js/libs/audio.worklet.js (renamed from platform/javascript/native/audio.worklet.js)32
-rw-r--r--platform/javascript/js/libs/library_godot_audio.js (renamed from platform/javascript/native/library_godot_audio.js)157
-rw-r--r--platform/javascript/js/libs/library_godot_display.js (renamed from platform/javascript/native/library_godot_display.js)238
-rw-r--r--platform/javascript/js/libs/library_godot_editor_tools.js (renamed from platform/javascript/native/library_godot_editor_tools.js)9
-rw-r--r--platform/javascript/js/libs/library_godot_eval.js (renamed from platform/javascript/native/library_godot_eval.js)68
-rw-r--r--platform/javascript/js/libs/library_godot_http_request.js (renamed from platform/javascript/native/http_request.js)89
-rw-r--r--platform/javascript/js/libs/library_godot_os.js (renamed from platform/javascript/native/library_godot_os.js)148
-rw-r--r--platform/javascript/js/libs/library_godot_runtime.js120
-rw-r--r--platform/javascript/package-lock.json1605
-rw-r--r--platform/javascript/package.json24
-rw-r--r--platform/linuxbsd/detect.py2
-rw-r--r--platform/linuxbsd/display_server_x11.cpp269
-rw-r--r--platform/linuxbsd/display_server_x11.h7
-rw-r--r--platform/linuxbsd/joypad_linux.cpp8
-rw-r--r--platform/linuxbsd/key_mapping_x11.cpp3
-rw-r--r--platform/osx/detect.py11
-rw-r--r--platform/server/detect.py2
-rw-r--r--platform/uwp/export/export.cpp6
-rw-r--r--platform/windows/detect.py7
-rw-r--r--platform/windows/key_mapping_windows.cpp2
-rw-r--r--platform/windows/os_windows.cpp4
60 files changed, 3722 insertions, 855 deletions
diff --git a/platform/android/api/java_class_wrapper.h b/platform/android/api/java_class_wrapper.h
index 4718de29ad..64da049407 100644
--- a/platform/android/api/java_class_wrapper.h
+++ b/platform/android/api/java_class_wrapper.h
@@ -47,7 +47,6 @@ class JavaClass : public Reference {
#ifdef ANDROID_ENABLED
enum ArgumentType{
-
ARG_TYPE_VOID,
ARG_TYPE_BOOLEAN,
ARG_TYPE_BYTE,
diff --git a/platform/android/audio_driver_opensl.h b/platform/android/audio_driver_opensl.h
index 9858a40822..b30711705b 100644
--- a/platform/android/audio_driver_opensl.h
+++ b/platform/android/audio_driver_opensl.h
@@ -42,7 +42,6 @@ class AudioDriverOpenSL : public AudioDriver {
Mutex mutex;
enum {
-
BUFFER_COUNT = 2
};
diff --git a/platform/android/export/export.cpp b/platform/android/export/export.cpp
index 5007b3f570..d24c96f87a 100644
--- a/platform/android/export/export.cpp
+++ b/platform/android/export/export.cpp
@@ -29,7 +29,6 @@
/*************************************************************************/
#include "export.h"
-#include "gradle_export_util.h"
#include "core/config/project_settings.h"
#include "core/io/image_loader.h"
@@ -827,7 +826,7 @@ class EditorExportPlatformAndroid : public EditorExportPlatform {
int version_code = p_preset->get("version/code");
String package_name = p_preset->get("package/unique_name");
- int orientation = p_preset->get("screen/orientation");
+ const int screen_orientation = _get_android_orientation_value(_get_screen_orientation());
bool screen_support_small = p_preset->get("screen/support_small");
bool screen_support_normal = p_preset->get("screen/support_normal");
@@ -937,7 +936,7 @@ class EditorExportPlatformAndroid : public EditorExportPlatform {
}
if (tname == "activity" && attrname == "screenOrientation") {
- encode_uint32(orientation == 0 ? 0 : 1, &p_manifest.write[iofs + 16]);
+ encode_uint32(screen_orientation, &p_manifest.write[iofs + 16]);
}
if (tname == "supports-screens") {
@@ -1611,12 +1610,6 @@ public:
}
virtual void get_export_options(List<ExportOption> *r_options) override {
- r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "graphics/32_bits_framebuffer"), true));
- 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, "one_click_deploy/clear_previous_install"), false));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "custom_template/debug", PROPERTY_HINT_GLOBAL_FILE, "*.apk"), ""));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "custom_template/release", PROPERTY_HINT_GLOBAL_FILE, "*.apk"), ""));
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "custom_template/use_custom_build"), false));
@@ -1629,39 +1622,52 @@ public:
}
plugins_changed = false;
- r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "command_line/extra_args"), ""));
+ Vector<String> abis = get_abis();
+ for (int i = 0; i < abis.size(); ++i) {
+ String abi = abis[i];
+ bool is_default = (abi == "armeabi-v7a" || abi == "arm64-v8a");
+ r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "architectures/" + abi), is_default));
+ }
+
+ r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "keystore/debug", PROPERTY_HINT_GLOBAL_FILE, "*.keystore,*.jks"), ""));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "keystore/debug_user"), ""));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "keystore/debug_password"), ""));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "keystore/release", PROPERTY_HINT_GLOBAL_FILE, "*.keystore,*.jks"), ""));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "keystore/release_user"), ""));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "keystore/release_password"), ""));
+
+ r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "one_click_deploy/clear_previous_install"), false));
+
r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "version/code", PROPERTY_HINT_RANGE, "1,4096,1,or_greater"), 1));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "version/name"), "1.0"));
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::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"), ""));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, launcher_adaptive_icon_background_option, PROPERTY_HINT_FILE, "*.png"), ""));
+
+ r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "graphics/32_bits_framebuffer"), true));
+ 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::INT, "screen/orientation", PROPERTY_HINT_ENUM, "Landscape,Portrait"), 0));
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "screen/support_small"), true));
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "screen/support_normal"), true));
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, "screen/opengl_debug"), false));
- 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"), ""));
- r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, launcher_adaptive_icon_background_option, PROPERTY_HINT_FILE, "*.png"), ""));
- r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "keystore/debug", PROPERTY_HINT_GLOBAL_FILE, "*.keystore"), ""));
- r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "keystore/debug_user"), ""));
- r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "keystore/debug_password"), ""));
- r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "keystore/release", PROPERTY_HINT_GLOBAL_FILE, "*.keystore"), ""));
- r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "keystore/release_user"), ""));
- r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "keystore/release_password"), ""));
+
+ r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "command_line/extra_args"), ""));
+
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "apk_expansion/enable"), false));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "apk_expansion/SALT"), ""));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "apk_expansion/public_key", PROPERTY_HINT_MULTILINE_TEXT), ""));
- Vector<String> abis = get_abis();
- for (int i = 0; i < abis.size(); ++i) {
- String abi = abis[i];
- bool is_default = (abi == "armeabi-v7a" || abi == "arm64-v8a");
- r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "architectures/" + abi), is_default));
- }
-
r_options->push_back(ExportOption(PropertyInfo(Variant::PACKED_STRING_ARRAY, "permissions/custom_permissions"), PackedStringArray()));
const char **perms = android_perms;
@@ -1968,9 +1974,12 @@ public:
valid = false;
} else {
Error errn;
+ // Check for the platform-tools directory.
DirAccessRef da = DirAccess::open(sdk_path.plus_file("platform-tools"), &errn);
if (errn != OK) {
- err += TTR("Invalid Android SDK path for custom build in Editor Settings.") + "\n";
+ err += TTR("Invalid Android SDK path for custom build in Editor Settings.");
+ err += TTR("Missing 'platform-tools' directory!");
+ err += "\n";
valid = false;
}
}
@@ -2137,7 +2146,7 @@ public:
command_line_strings.push_back("--use_immersive");
}
- bool debug_opengl = p_preset->get("screen/opengl_debug");
+ bool debug_opengl = p_preset->get("graphics/opengl_debug");
if (debug_opengl) {
command_line_strings.push_back("--debug_opengl");
}
@@ -2159,14 +2168,16 @@ public:
}
}
- Error sign_apk(const Ref<EditorExportPreset> &p_preset, bool p_debug, String apk_path, EditorProgress ep) {
+ Error sign_apk(const Ref<EditorExportPreset> &p_preset, bool p_debug, String export_path, EditorProgress ep) {
+ int export_format = int(p_preset->get("custom_template/export_format"));
+ String export_label = export_format == 1 ? "AAB" : "APK";
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");
String jarsigner = EditorSettings::get_singleton()->get("export/android/jarsigner");
if (!FileAccess::exists(jarsigner)) {
- EditorNode::add_io_error("'jarsigner' could not be found.\nPlease supply a path in the Editor Settings.\nThe resulting APK is unsigned.");
+ EditorNode::add_io_error("'jarsigner' could not be found.\nPlease supply a path in the Editor Settings.\nThe resulting " + export_label + " is unsigned.");
return OK;
}
@@ -2184,7 +2195,7 @@ public:
user = EditorSettings::get_singleton()->get("export/android/debug_keystore_user");
}
- if (ep.step("Signing debug APK...", 103)) {
+ if (ep.step("Signing debug " + export_label + "...", 103)) {
return ERR_SKIP;
}
@@ -2193,7 +2204,7 @@ public:
password = release_password;
user = release_username;
- if (ep.step("Signing release APK...", 103)) {
+ if (ep.step("Signing release " + export_label + "...", 103)) {
return ERR_SKIP;
}
}
@@ -2218,7 +2229,7 @@ public:
args.push_back(keystore);
args.push_back("-storepass");
args.push_back(password);
- args.push_back(apk_path);
+ args.push_back(export_path);
args.push_back(user);
int retval;
OS::get_singleton()->execute(jarsigner, args, true, NULL, NULL, &retval);
@@ -2227,7 +2238,7 @@ public:
return ERR_CANT_CREATE;
}
- if (ep.step("Verifying APK...", 104)) {
+ if (ep.step("Verifying " + export_label + "...", 104)) {
return ERR_SKIP;
}
@@ -2235,17 +2246,26 @@ public:
args.push_back("-verify");
args.push_back("-keystore");
args.push_back(keystore);
- args.push_back(apk_path);
+ args.push_back(export_path);
args.push_back("-verbose");
OS::get_singleton()->execute(jarsigner, args, true, NULL, NULL, &retval);
if (retval) {
- EditorNode::add_io_error("'jarsigner' verification of APK failed. Make sure to use a jarsigner from OpenJDK 8.");
+ EditorNode::add_io_error("'jarsigner' verification of " + export_label + " failed. Make sure to use a jarsigner from OpenJDK 8.");
return ERR_CANT_CREATE;
}
return OK;
}
+ void _clear_assets_directory() {
+ DirAccessRef da_res = DirAccess::create(DirAccess::ACCESS_RESOURCES);
+ if (da_res->dir_exists("res://android/build/assets")) {
+ DirAccessRef da_assets = DirAccess::open("res://android/build/assets");
+ da_assets->erase_contents_recursive();
+ da_res->remove("res://android/build/assets");
+ }
+ }
+
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);
@@ -2325,13 +2345,8 @@ public:
_write_tmp_manifest(p_preset, p_give_internet, p_debug);
//stores all the project files inside the Gradle project directory. Also includes all ABIs
+ _clear_assets_directory();
if (!apk_expansion) {
- DirAccess *da_res = DirAccess::create(DirAccess::ACCESS_RESOURCES);
- if (da_res->dir_exists("res://android/build/assets")) {
- DirAccess *da_assets = DirAccess::open("res://android/build/assets");
- da_assets->erase_contents_recursive();
- da_res->remove("res://android/build/assets");
- }
err = export_project_files(p_preset, rename_and_store_file_in_gradle_project, NULL, ignore_so_file);
if (err != OK) {
EditorNode::add_io_error("Could not export project files to gradle project\n");
@@ -2362,6 +2377,8 @@ public:
String version_code = itos(p_preset->get("version/code"));
String version_name = p_preset->get("version/name");
String enabled_abi_string = String("|").join(enabled_abis);
+ String sign_flag = _signed ? "true" : "false";
+ String zipalign_flag = "true";
Vector<PluginConfig> enabled_plugins = get_enabled_plugins(p_preset);
String local_plugins_binaries = get_plugins_binaries(BINARY_TYPE_LOCAL, enabled_plugins);
@@ -2390,15 +2407,25 @@ public:
cmdline.push_back("-Pplugins_local_binaries=" + local_plugins_binaries); // argument to specify the list of plugins local dependencies.
cmdline.push_back("-Pplugins_remote_binaries=" + remote_plugins_binaries); // argument to specify the list of plugins remote dependencies.
cmdline.push_back("-Pplugins_maven_repos=" + custom_maven_repos); // argument to specify the list of custom maven repos for the plugins dependencies.
+ cmdline.push_back("-Pperform_zipalign=" + zipalign_flag); // argument to specify whether the build should be zipaligned.
+ cmdline.push_back("-Pperform_signing=" + sign_flag); // argument to specify whether the build should be signed.
+ if (_signed && !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;
+ }
+
+ 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.
+ }
cmdline.push_back("-p"); // argument to specify the start directory.
cmdline.push_back(build_path); // start directory.
- /*{ used for debug
- int ec;
- String pipe;
- OS::get_singleton()->execute(build_command, cmdline, true, nullptr, nullptr, &ec);
- print_line("exit code: " + itos(ec));
- }
- */
+
int result = EditorNode::get_singleton()->execute_and_show_output(TTR("Building Android Project (gradle)"), build_command, cmdline);
if (result != 0) {
EditorNode::get_singleton()->show_warning(TTR("Building of Android project failed, check output for the error.\nAlternatively visit docs.godotengine.org for Android build documentation."));
@@ -2420,6 +2447,10 @@ public:
String export_filename = p_path.get_file();
String export_path = p_path.get_base_dir();
+ if (export_path.is_rel_path()) {
+ export_path = OS::get_singleton()->get_resource_dir().plus_file(export_path);
+ }
+ export_path = ProjectSettings::get_singleton()->globalize_path(export_path).simplify_path();
copy_args.push_back("-Pexport_path=file:" + export_path);
copy_args.push_back("-Pexport_filename=" + export_filename);
@@ -2429,12 +2460,7 @@ public:
EditorNode::get_singleton()->show_warning(TTR("Unable to copy and rename export file, check gradle project directory for outputs."));
return ERR_CANT_CREATE;
}
- if (_signed) {
- err = sign_apk(p_preset, p_debug, p_path, ep);
- if (err != OK) {
- return err;
- }
- }
+
return OK;
}
// This is the start of the Legacy build system
@@ -2781,7 +2807,7 @@ void register_android_exporter() {
EDITOR_DEF("export/android/jarsigner", "");
EditorSettings::get_singleton()->add_property_hint(PropertyInfo(Variant::STRING, "export/android/jarsigner", PROPERTY_HINT_GLOBAL_FILE, exe_ext));
EDITOR_DEF("export/android/debug_keystore", "");
- EditorSettings::get_singleton()->add_property_hint(PropertyInfo(Variant::STRING, "export/android/debug_keystore", PROPERTY_HINT_GLOBAL_FILE, "*.keystore"));
+ EditorSettings::get_singleton()->add_property_hint(PropertyInfo(Variant::STRING, "export/android/debug_keystore", PROPERTY_HINT_GLOBAL_FILE, "*.keystore,*.jks"));
EDITOR_DEF("export/android/debug_keystore_user", "androiddebugkey");
EDITOR_DEF("export/android/debug_keystore_pass", "android");
EDITOR_DEF("export/android/force_system_user", false);
diff --git a/platform/android/export/gradle_export_util.h b/platform/android/export/gradle_export_util.h
index 95f870bc35..a9f38869e0 100644
--- a/platform/android/export/gradle_export_util.h
+++ b/platform/android/export/gradle_export_util.h
@@ -44,6 +44,67 @@ 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:
+ return 1;
+ case DisplayServer::SCREEN_REVERSE_LANDSCAPE:
+ return 8;
+ case DisplayServer::SCREEN_REVERSE_PORTRAIT:
+ return 9;
+ case DisplayServer::SCREEN_SENSOR_LANDSCAPE:
+ return 11;
+ case DisplayServer::SCREEN_SENSOR_PORTRAIT:
+ return 12;
+ case DisplayServer::SCREEN_SENSOR:
+ return 13;
+ case DisplayServer::SCREEN_LANDSCAPE:
+ default:
+ return 0;
+ }
+}
+
+String _get_android_orientation_label(DisplayServer::ScreenOrientation screen_orientation) {
+ switch (screen_orientation) {
+ case DisplayServer::SCREEN_PORTRAIT:
+ return "portrait";
+ case DisplayServer::SCREEN_REVERSE_LANDSCAPE:
+ return "reverseLandscape";
+ case DisplayServer::SCREEN_REVERSE_PORTRAIT:
+ return "reversePortrait";
+ case DisplayServer::SCREEN_SENSOR_LANDSCAPE:
+ return "userLandscape";
+ case DisplayServer::SCREEN_SENSOR_PORTRAIT:
+ return "userPortrait";
+ case DisplayServer::SCREEN_SENSOR:
+ return "fullUser";
+ case DisplayServer::SCREEN_LANDSCAPE:
+ default:
+ return "landscape";
+ }
+}
+
// Utility method used to create a directory.
Error create_directory(const String &p_dir) {
if (!DirAccess::exists(p_dir)) {
@@ -209,7 +270,7 @@ String _get_plugins_tag(const String &plugins_names) {
String _get_activity_tag(const Ref<EditorExportPreset> &p_preset) {
bool uses_xr = (int)(p_preset->get("xr_features/xr_mode")) == 1;
- String orientation = (int)(p_preset->get("screen/orientation")) == 1 ? "portrait" : "landscape";
+ String orientation = _get_android_orientation_label(_get_screen_orientation());
String manifest_activity_text = vformat(
" <activity android:name=\"com.godot.game.GodotApp\" "
"tools:replace=\"android:screenOrientation\" "
@@ -230,7 +291,7 @@ String _get_application_tag(const Ref<EditorExportPreset> &p_preset, const Strin
String manifest_application_text =
" <application android:label=\"@string/godot_project_name_string\"\n"
" android:allowBackup=\"false\" tools:ignore=\"GoogleAppIndexingWarning\"\n"
- " android:icon=\"@mipmap/icon\">)\n\n"
+ " android:icon=\"@mipmap/icon\">\n\n"
" <meta-data tools:node=\"remove\" android:name=\"xr_mode_metadata_name\" />\n";
manifest_application_text += _get_plugins_tag(plugins_names);
diff --git a/platform/android/file_access_android.cpp b/platform/android/file_access_android.cpp
index 11faeff3e8..2446ca2829 100644
--- a/platform/android/file_access_android.cpp
+++ b/platform/android/file_access_android.cpp
@@ -34,7 +34,6 @@
AAssetManager *FileAccessAndroid::asset_manager = nullptr;
/*void FileAccessAndroid::make_default() {
-
create_func=create_android;
}*/
diff --git a/platform/android/java/app/build.gradle b/platform/android/java/app/build.gradle
index 6de1d2dd30..53d11fda5b 100644
--- a/platform/android/java/app/build.gradle
+++ b/platform/android/java/app/build.gradle
@@ -106,10 +106,41 @@ android {
// doNotStrip '**/*.so'
}
- // Both signing and zip-aligning will be done at export time
- buildTypes.all { buildType ->
- buildType.zipAlignEnabled false
- buildType.signingConfig null
+ signingConfigs {
+ release {
+ File keystoreFile = new File(getReleaseKeystoreFile())
+ if (keystoreFile.isFile()) {
+ storeFile keystoreFile
+ storePassword getReleaseKeystorePassword()
+ keyAlias getReleaseKeyAlias()
+ keyPassword getReleaseKeystorePassword()
+ }
+ }
+ }
+
+ buildTypes {
+
+ debug {
+ // Signing and zip-aligning are skipped for prebuilt builds, but
+ // performed for custom builds.
+ zipAlignEnabled shouldZipAlign()
+ if (shouldSign()) {
+ signingConfig signingConfigs.debug
+ } else {
+ signingConfig null
+ }
+ }
+
+ release {
+ // Signing and zip-aligning are skipped for prebuilt builds, but
+ // performed for custom builds.
+ zipAlignEnabled shouldZipAlign()
+ if (shouldSign()) {
+ signingConfig signingConfigs.release
+ } else {
+ signingConfig null
+ }
+ }
}
sourceSets {
diff --git a/platform/android/java/app/config.gradle b/platform/android/java/app/config.gradle
index e6c45b73a7..80cf6f7ede 100644
--- a/platform/android/java/app/config.gradle
+++ b/platform/android/java/app/config.gradle
@@ -34,7 +34,11 @@ ext.getExportVersionCode = { ->
if (versionCode == null || versionCode.isEmpty()) {
versionCode = "1"
}
- return Integer.parseInt(versionCode)
+ try {
+ return Integer.parseInt(versionCode)
+ } catch (NumberFormatException ignored) {
+ return 1
+ }
}
ext.getExportVersionName = { ->
@@ -136,3 +140,37 @@ ext.getGodotPluginsLocalBinaries = { ->
return binDeps
}
+
+ext.getReleaseKeystoreFile = { ->
+ String keystoreFile = project.hasProperty("release_keystore_file") ? project.property("release_keystore_file") : ""
+ if (keystoreFile == null || keystoreFile.isEmpty()) {
+ keystoreFile = "."
+ }
+ return keystoreFile
+}
+
+ext.getReleaseKeystorePassword = { ->
+ String keystorePassword = project.hasProperty("release_keystore_password") ? project.property("release_keystore_password") : ""
+ return keystorePassword
+}
+
+ext.getReleaseKeyAlias = { ->
+ String keyAlias = project.hasProperty("release_keystore_alias") ? project.property("release_keystore_alias") : ""
+ return keyAlias
+}
+
+ext.shouldZipAlign = { ->
+ String zipAlignFlag = project.hasProperty("perform_zipalign") ? project.property("perform_zipalign") : ""
+ if (zipAlignFlag == null || zipAlignFlag.isEmpty()) {
+ zipAlignFlag = "false"
+ }
+ return Boolean.parseBoolean(zipAlignFlag)
+}
+
+ext.shouldSign = { ->
+ String signFlag = project.hasProperty("perform_signing") ? project.property("perform_signing") : ""
+ if (signFlag == null || signFlag.isEmpty()) {
+ signFlag = "false"
+ }
+ return Boolean.parseBoolean(signFlag)
+}
diff --git a/platform/android/java/build.gradle b/platform/android/java/build.gradle
index 821a4dc584..73c136ed0e 100644
--- a/platform/android/java/build.gradle
+++ b/platform/android/java/build.gradle
@@ -22,8 +22,6 @@ allprojects {
}
ext {
- sconsExt = org.gradle.internal.os.OperatingSystem.current().isWindows() ? ".bat" : ""
-
supportedAbis = ["armv7", "arm64v8", "x86", "x86_64"]
supportedTargets = ["release", "debug"]
diff --git a/platform/android/java/lib/build.gradle b/platform/android/java/lib/build.gradle
index e3c5a02203..89ce3d15e6 100644
--- a/platform/android/java/lib/build.gradle
+++ b/platform/android/java/lib/build.gradle
@@ -64,10 +64,42 @@ android {
throw new GradleException("Invalid default abi: " + defaultAbi)
}
+ // Find scons' executable path
+ File sconsExecutableFile = null
+ def sconsName = "scons"
+ def sconsExts = (org.gradle.internal.os.OperatingSystem.current().isWindows()
+ ? [".bat", ".exe"]
+ : [""])
+ logger.lifecycle("Looking for $sconsName executable path")
+ for (ext in sconsExts) {
+ String sconsNameExt = sconsName + ext
+ logger.lifecycle("Checking $sconsNameExt")
+
+ sconsExecutableFile = org.gradle.internal.os.OperatingSystem.current().findInPath(sconsNameExt)
+ if (sconsExecutableFile != null) {
+ // We're done!
+ break
+ }
+
+ // Check all the options in path
+ List<File> allOptions = org.gradle.internal.os.OperatingSystem.current().findAllInPath(sconsNameExt)
+ if (!allOptions.isEmpty()) {
+ // Pick the first option and we're done!
+ sconsExecutableFile = allOptions.get(0)
+ break
+ }
+ }
+
+ if (sconsExecutableFile == null) {
+ throw new GradleException("Unable to find executable path for the '$sconsName' command.")
+ } else {
+ logger.lifecycle("Found executable path for $sconsName: ${sconsExecutableFile.absolutePath}")
+ }
+
// Creating gradle task to generate the native libraries for the default abi.
def taskName = getSconsTaskName(buildType)
tasks.create(name: taskName, type: Exec) {
- executable "scons" + sconsExt
+ executable sconsExecutableFile.absolutePath
args "--directory=${pathToRootDir}", "platform=android", "target=${releaseTarget}", "android_arch=${defaultAbi}", "-j" + Runtime.runtime.availableProcessors()
}
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 6cf340c418..3bbe35091c 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/Godot.java
+++ b/platform/android/java/lib/src/org/godotengine/godot/Godot.java
@@ -760,9 +760,7 @@ public class Godot extends Fragment implements SensorEventListener, IDownloaderC
/*
@Override public boolean dispatchKeyEvent(KeyEvent event) {
-
if (event.getKeyCode()==KeyEvent.KEYCODE_BACK) {
-
System.out.printf("** BACK REQUEST!\n");
GodotLib.quit();
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 874fd88848..894009e30f 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/GodotIO.java
+++ b/platform/android/java/lib/src/org/godotengine/godot/GodotIO.java
@@ -515,13 +515,13 @@ public class GodotIO {
activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT);
} break;
case SCREEN_SENSOR_LANDSCAPE: {
- activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE);
+ activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_USER_LANDSCAPE);
} break;
case SCREEN_SENSOR_PORTRAIT: {
- activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT);
+ activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_USER_PORTRAIT);
} break;
case SCREEN_SENSOR: {
- activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_FULL_SENSOR);
+ activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_FULL_USER);
} break;
}
}
diff --git a/platform/android/java/lib/src/org/godotengine/godot/vulkan/VkRenderer.kt b/platform/android/java/lib/src/org/godotengine/godot/vulkan/VkRenderer.kt
index 7fa8e3b4e5..f93cf0fa38 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/vulkan/VkRenderer.kt
+++ b/platform/android/java/lib/src/org/godotengine/godot/vulkan/VkRenderer.kt
@@ -52,7 +52,6 @@ import org.godotengine.godot.plugin.GodotPluginRegistry
* @see [VkSurfaceView.startRenderer]
*/
internal class VkRenderer {
-
private val pluginRegistry: GodotPluginRegistry = GodotPluginRegistry.getPluginRegistry()
/**
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 6b0e12b21a..e5c7a39bfb 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
@@ -50,7 +50,6 @@ import android.view.SurfaceView
* </ul>
*/
open internal class VkSurfaceView(context: Context) : SurfaceView(context), SurfaceHolder.Callback {
-
companion object {
fun checkState(expression: Boolean, errorMessage: Any) {
check(expression) { errorMessage.toString() }
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 7557c8aa22..fb02e3a69f 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
@@ -41,7 +41,6 @@ import kotlin.concurrent.withLock
* The implementation is modeled after [android.opengl.GLSurfaceView]'s GLThread.
*/
internal class VkThread(private val vkSurfaceView: VkSurfaceView, private val vkRenderer: VkRenderer) : Thread(TAG) {
-
companion object {
private val TAG = VkThread::class.java.simpleName
}
@@ -226,5 +225,4 @@ internal class VkThread(private val vkSurfaceView: VkSurfaceView, private val vk
threadExiting()
}
}
-
}
diff --git a/platform/iphone/SCsub b/platform/iphone/SCsub
index 0a558f8e3d..ee7b2f4ab5 100644
--- a/platform/iphone/SCsub
+++ b/platform/iphone/SCsub
@@ -14,9 +14,11 @@ iphone_lib = [
"joypad_iphone.mm",
"godot_view.mm",
"display_layer.mm",
+ "godot_app_delegate.m",
"godot_view_renderer.mm",
"godot_view_gesture_recognizer.mm",
"device_metrics.m",
+ "keyboard_input_view.mm",
"native_video_view.m",
]
diff --git a/platform/iphone/display_server_iphone.mm b/platform/iphone/display_server_iphone.mm
index 6fa5e6ee4a..d47d131719 100644
--- a/platform/iphone/display_server_iphone.mm
+++ b/platform/iphone/display_server_iphone.mm
@@ -35,6 +35,7 @@
#import "device_metrics.h"
#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"
@@ -529,11 +530,17 @@ bool DisplayServerIPhone::screen_is_touchscreen(int p_screen) const {
}
void DisplayServerIPhone::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) {
- [AppDelegate.viewController.godotView becomeFirstResponderWithString:p_existing_text];
+ NSString *existingString = [[NSString alloc] initWithUTF8String:p_existing_text.utf8().get_data()];
+
+ [AppDelegate.viewController.keyboardView
+ becomeFirstResponderWithString:existingString
+ multiline:p_multiline
+ cursorStart:p_cursor_start
+ cursorEnd:p_cursor_end];
}
void DisplayServerIPhone::virtual_keyboard_hide() {
- [AppDelegate.viewController.godotView resignFirstResponder];
+ [AppDelegate.viewController.keyboardView resignFirstResponder];
}
void DisplayServerIPhone::virtual_keyboard_set_height(int height) {
diff --git a/platform/iphone/export/export.cpp b/platform/iphone/export/export.cpp
index cbef136247..fff82c9467 100644
--- a/platform/iphone/export/export.cpp
+++ b/platform/iphone/export/export.cpp
@@ -338,12 +338,16 @@ void EditorExportPlatformIOS::get_export_options(List<ExportOption> *r_options)
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "custom_template/debug", PROPERTY_HINT_GLOBAL_FILE, "*.zip"), ""));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "custom_template/release", PROPERTY_HINT_GLOBAL_FILE, "*.zip"), ""));
+ Vector<ExportArchitecture> architectures = _get_supported_architectures();
+ for (int i = 0; i < architectures.size(); ++i) {
+ r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "architectures/" + architectures[i].name), architectures[i].is_default));
+ }
+
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/app_store_team_id"), ""));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/provisioning_profile_uuid_debug"), ""));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/code_sign_identity_debug", PROPERTY_HINT_PLACEHOLDER_TEXT, "iPhone Developer"), "iPhone Developer"));
r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "application/export_method_debug", PROPERTY_HINT_ENUM, "App Store,Development,Ad-Hoc,Enterprise"), 1));
-
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/provisioning_profile_uuid_release"), ""));
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));
@@ -357,11 +361,9 @@ void EditorExportPlatformIOS::get_export_options(List<ExportOption> *r_options)
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/copyright"), ""));
Vector<PluginConfig> found_plugins = get_plugins();
-
for (int i = 0; i < found_plugins.size(); i++) {
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "plugins/" + found_plugins[i].name), false));
}
-
plugins_changed = false;
plugins = found_plugins;
@@ -404,11 +406,6 @@ void EditorExportPlatformIOS::get_export_options(List<ExportOption> *r_options)
for (uint64_t i = 0; i < sizeof(loading_screen_infos) / sizeof(loading_screen_infos[0]); ++i) {
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, loading_screen_infos[i].preset_key, PROPERTY_HINT_FILE, "*.png"), ""));
}
-
- Vector<ExportArchitecture> architectures = _get_supported_architectures();
- for (int i = 0; i < architectures.size(); ++i) {
- r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "architectures/" + architectures[i].name), architectures[i].is_default));
- }
}
void EditorExportPlatformIOS::_fix_config_file(const Ref<EditorExportPreset> &p_preset, Vector<uint8_t> &pfile, const IOSConfigData &p_config, bool p_debug) {
@@ -690,7 +687,6 @@ static const IconInfo icon_infos[] = {
{ "optional_icons/spotlight_80x80", "iphone", "Icon-80.png", "80", "2x", "40x40", false },
{ "optional_icons/spotlight_80x80", "ipad", "Icon-80.png", "80", "2x", "40x40", false }
-
};
Error EditorExportPlatformIOS::_export_icons(const Ref<EditorExportPreset> &p_preset, const String &p_iconset_dir) {
diff --git a/platform/iphone/godot_app_delegate.h b/platform/iphone/godot_app_delegate.h
new file mode 100644
index 0000000000..ebb21c499b
--- /dev/null
+++ b/platform/iphone/godot_app_delegate.h
@@ -0,0 +1,41 @@
+/*************************************************************************/
+/* godot_app_delegate.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#import <UIKit/UIKit.h>
+
+typedef NSObject<UIApplicationDelegate> ApplicationDelegateService;
+
+@interface GodotApplicalitionDelegate : NSObject <UIApplicationDelegate>
+
+@property(class, readonly, strong) NSArray<ApplicationDelegateService *> *services;
+
++ (void)addService:(ApplicationDelegateService *)service;
+
+@end
diff --git a/platform/iphone/godot_app_delegate.m b/platform/iphone/godot_app_delegate.m
new file mode 100644
index 0000000000..a5aad26bd5
--- /dev/null
+++ b/platform/iphone/godot_app_delegate.m
@@ -0,0 +1,497 @@
+/*************************************************************************/
+/* godot_app_delegate.m */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#import "godot_app_delegate.h"
+
+#import "app_delegate.h"
+
+@interface GodotApplicalitionDelegate ()
+
+@end
+
+@implementation GodotApplicalitionDelegate
+
+static NSMutableArray<ApplicationDelegateService *> *services = nil;
+
++ (NSArray<ApplicationDelegateService *> *)services {
+ return services;
+}
+
++ (void)load {
+ services = [NSMutableArray new];
+ [services addObject:[AppDelegate new]];
+}
+
++ (void)addService:(ApplicationDelegateService *)service {
+ if (!services || !service) {
+ return;
+ }
+ [services addObject:service];
+}
+
+// UIApplicationDelegate documantation can be found here: https://developer.apple.com/documentation/uikit/uiapplicationdelegate
+
+// MARK: Window
+
+- (UIWindow *)window {
+ UIWindow *result = nil;
+
+ for (ApplicationDelegateService *service in services) {
+ if (![service respondsToSelector:_cmd]) {
+ continue;
+ }
+
+ UIWindow *value = [service window];
+
+ if (value) {
+ result = value;
+ }
+ }
+
+ return result;
+}
+
+// MARK: Initializing
+
+- (BOOL)application:(UIApplication *)application willFinishLaunchingWithOptions:(NSDictionary<UIApplicationLaunchOptionsKey, id> *)launchOptions {
+ BOOL result = NO;
+
+ for (ApplicationDelegateService *service in services) {
+ if (![service respondsToSelector:_cmd]) {
+ continue;
+ }
+
+ if ([service application:application willFinishLaunchingWithOptions:launchOptions]) {
+ result = YES;
+ }
+ }
+
+ return result;
+}
+
+- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary<UIApplicationLaunchOptionsKey, id> *)launchOptions {
+ BOOL result = NO;
+
+ for (ApplicationDelegateService *service in services) {
+ if (![service respondsToSelector:_cmd]) {
+ continue;
+ }
+
+ if ([service application:application didFinishLaunchingWithOptions:launchOptions]) {
+ result = YES;
+ }
+ }
+
+ return result;
+}
+
+/* Can be handled by Info.plist. Not yet supported by Godot.
+
+// MARK: Scene
+
+- (UISceneConfiguration *)application:(UIApplication *)application configurationForConnectingSceneSession:(UISceneSession *)connectingSceneSession options:(UISceneConnectionOptions *)options {}
+
+- (void)application:(UIApplication *)application didDiscardSceneSessions:(NSSet<UISceneSession *> *)sceneSessions {}
+
+*/
+
+// MARK: Life-Cycle
+
+- (void)applicationDidBecomeActive:(UIApplication *)application {
+ for (ApplicationDelegateService *service in services) {
+ if (![service respondsToSelector:_cmd]) {
+ continue;
+ }
+
+ [service applicationDidBecomeActive:application];
+ }
+}
+
+- (void)applicationWillResignActive:(UIApplication *)application {
+ for (ApplicationDelegateService *service in services) {
+ if (![service respondsToSelector:_cmd]) {
+ continue;
+ }
+
+ [service applicationWillResignActive:application];
+ }
+}
+
+- (void)applicationDidEnterBackground:(UIApplication *)application {
+ for (ApplicationDelegateService *service in services) {
+ if (![service respondsToSelector:_cmd]) {
+ continue;
+ }
+
+ [service applicationDidEnterBackground:application];
+ }
+}
+
+- (void)applicationWillEnterForeground:(UIApplication *)application {
+ for (ApplicationDelegateService *service in services) {
+ if (![service respondsToSelector:_cmd]) {
+ continue;
+ }
+
+ [service applicationWillEnterForeground:application];
+ }
+}
+
+- (void)applicationWillTerminate:(UIApplication *)application {
+ for (ApplicationDelegateService *service in services) {
+ if (![service respondsToSelector:_cmd]) {
+ continue;
+ }
+
+ [service applicationWillTerminate:application];
+ }
+}
+
+// MARK: Environment Changes
+
+- (void)applicationProtectedDataDidBecomeAvailable:(UIApplication *)application {
+ for (ApplicationDelegateService *service in services) {
+ if (![service respondsToSelector:_cmd]) {
+ continue;
+ }
+
+ [service applicationProtectedDataDidBecomeAvailable:application];
+ }
+}
+
+- (void)applicationProtectedDataWillBecomeUnavailable:(UIApplication *)application {
+ for (ApplicationDelegateService *service in services) {
+ if (![service respondsToSelector:_cmd]) {
+ continue;
+ }
+
+ [service applicationProtectedDataWillBecomeUnavailable:application];
+ }
+}
+
+- (void)applicationDidReceiveMemoryWarning:(UIApplication *)application {
+ for (ApplicationDelegateService *service in services) {
+ if (![service respondsToSelector:_cmd]) {
+ continue;
+ }
+
+ [service applicationDidReceiveMemoryWarning:application];
+ }
+}
+
+- (void)applicationSignificantTimeChange:(UIApplication *)application {
+ for (ApplicationDelegateService *service in services) {
+ if (![service respondsToSelector:_cmd]) {
+ continue;
+ }
+
+ [service applicationSignificantTimeChange:application];
+ }
+}
+
+// MARK: App State Restoration
+
+- (BOOL)application:(UIApplication *)application shouldSaveSecureApplicationState:(NSCoder *)coder API_AVAILABLE(ios(13.2)) {
+ BOOL result = NO;
+
+ for (ApplicationDelegateService *service in services) {
+ if (![service respondsToSelector:_cmd]) {
+ continue;
+ }
+
+ if ([service application:application shouldSaveSecureApplicationState:coder]) {
+ result = YES;
+ }
+ }
+
+ return result;
+}
+
+- (BOOL)application:(UIApplication *)application shouldRestoreSecureApplicationState:(NSCoder *)coder API_AVAILABLE(ios(13.2)) {
+ BOOL result = NO;
+
+ for (ApplicationDelegateService *service in services) {
+ if (![service respondsToSelector:_cmd]) {
+ continue;
+ }
+
+ if ([service application:application shouldRestoreSecureApplicationState:coder]) {
+ result = YES;
+ }
+ }
+
+ return result;
+}
+
+- (UIViewController *)application:(UIApplication *)application viewControllerWithRestorationIdentifierPath:(NSArray<NSString *> *)identifierComponents coder:(NSCoder *)coder {
+ for (ApplicationDelegateService *service in services) {
+ if (![service respondsToSelector:_cmd]) {
+ continue;
+ }
+
+ UIViewController *controller = [service application:application viewControllerWithRestorationIdentifierPath:identifierComponents coder:coder];
+
+ if (controller) {
+ return controller;
+ }
+ }
+
+ return nil;
+}
+
+- (void)application:(UIApplication *)application willEncodeRestorableStateWithCoder:(NSCoder *)coder {
+ for (ApplicationDelegateService *service in services) {
+ if (![service respondsToSelector:_cmd]) {
+ continue;
+ }
+
+ [service application:application willEncodeRestorableStateWithCoder:coder];
+ }
+}
+
+- (void)application:(UIApplication *)application didDecodeRestorableStateWithCoder:(NSCoder *)coder {
+ for (ApplicationDelegateService *service in services) {
+ if (![service respondsToSelector:_cmd]) {
+ continue;
+ }
+
+ [service application:application didDecodeRestorableStateWithCoder:coder];
+ }
+}
+
+// MARK: Download Data in Background
+
+- (void)application:(UIApplication *)application handleEventsForBackgroundURLSession:(NSString *)identifier completionHandler:(void (^)(void))completionHandler {
+ for (ApplicationDelegateService *service in services) {
+ if (![service respondsToSelector:_cmd]) {
+ continue;
+ }
+
+ [service application:application handleEventsForBackgroundURLSession:identifier completionHandler:completionHandler];
+ }
+
+ completionHandler();
+}
+
+// MARK: Remote Notification
+
+- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken {
+ for (ApplicationDelegateService *service in services) {
+ if (![service respondsToSelector:_cmd]) {
+ continue;
+ }
+
+ [service application:application didRegisterForRemoteNotificationsWithDeviceToken:deviceToken];
+ }
+}
+
+- (void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error {
+ for (ApplicationDelegateService *service in services) {
+ if (![service respondsToSelector:_cmd]) {
+ continue;
+ }
+
+ [service application:application didFailToRegisterForRemoteNotificationsWithError:error];
+ }
+}
+
+- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult result))completionHandler {
+ for (ApplicationDelegateService *service in services) {
+ if (![service respondsToSelector:_cmd]) {
+ continue;
+ }
+
+ [service application:application didReceiveRemoteNotification:userInfo fetchCompletionHandler:completionHandler];
+ }
+
+ completionHandler(UIBackgroundFetchResultNoData);
+}
+
+// MARK: User Activity and Handling Quick Actions
+
+- (BOOL)application:(UIApplication *)application willContinueUserActivityWithType:(NSString *)userActivityType {
+ BOOL result = NO;
+
+ for (ApplicationDelegateService *service in services) {
+ if (![service respondsToSelector:_cmd]) {
+ continue;
+ }
+
+ if ([service application:application willContinueUserActivityWithType:userActivityType]) {
+ result = YES;
+ }
+ }
+
+ return result;
+}
+
+- (BOOL)application:(UIApplication *)application continueUserActivity:(NSUserActivity *)userActivity restorationHandler:(void (^)(NSArray<id<UIUserActivityRestoring>> *restorableObjects))restorationHandler {
+ BOOL result = NO;
+
+ for (ApplicationDelegateService *service in services) {
+ if (![service respondsToSelector:_cmd]) {
+ continue;
+ }
+
+ if ([service application:application continueUserActivity:userActivity restorationHandler:restorationHandler]) {
+ result = YES;
+ }
+ }
+
+ return result;
+}
+
+- (void)application:(UIApplication *)application didUpdateUserActivity:(NSUserActivity *)userActivity {
+ for (ApplicationDelegateService *service in services) {
+ if (![service respondsToSelector:_cmd]) {
+ continue;
+ }
+
+ [service application:application didUpdateUserActivity:userActivity];
+ }
+}
+
+- (void)application:(UIApplication *)application didFailToContinueUserActivityWithType:(NSString *)userActivityType error:(NSError *)error {
+ for (ApplicationDelegateService *service in services) {
+ if (![service respondsToSelector:_cmd]) {
+ continue;
+ }
+
+ [service application:application didFailToContinueUserActivityWithType:userActivityType error:error];
+ }
+}
+
+- (void)application:(UIApplication *)application performActionForShortcutItem:(UIApplicationShortcutItem *)shortcutItem completionHandler:(void (^)(BOOL succeeded))completionHandler {
+ for (ApplicationDelegateService *service in services) {
+ if (![service respondsToSelector:_cmd]) {
+ continue;
+ }
+
+ [service application:application performActionForShortcutItem:shortcutItem completionHandler:completionHandler];
+ }
+}
+
+// MARK: WatchKit
+
+- (void)application:(UIApplication *)application handleWatchKitExtensionRequest:(NSDictionary *)userInfo reply:(void (^)(NSDictionary *replyInfo))reply {
+ for (ApplicationDelegateService *service in services) {
+ if (![service respondsToSelector:_cmd]) {
+ continue;
+ }
+
+ [service application:application handleWatchKitExtensionRequest:userInfo reply:reply];
+ }
+}
+
+// MARK: HealthKit
+
+- (void)applicationShouldRequestHealthAuthorization:(UIApplication *)application {
+ for (ApplicationDelegateService *service in services) {
+ if (![service respondsToSelector:_cmd]) {
+ continue;
+ }
+
+ [service applicationShouldRequestHealthAuthorization:application];
+ }
+}
+
+// MARK: Opening an URL
+
+- (BOOL)application:(UIApplication *)app openURL:(NSURL *)url options:(NSDictionary<UIApplicationOpenURLOptionsKey, id> *)options {
+ for (ApplicationDelegateService *service in services) {
+ if (![service respondsToSelector:_cmd]) {
+ continue;
+ }
+
+ if ([service application:app openURL:url options:options]) {
+ return YES;
+ }
+ }
+
+ return NO;
+}
+
+// MARK: Disallowing Specified App Extension Types
+
+- (BOOL)application:(UIApplication *)application shouldAllowExtensionPointIdentifier:(UIApplicationExtensionPointIdentifier)extensionPointIdentifier {
+ BOOL result = NO;
+
+ for (ApplicationDelegateService *service in services) {
+ if (![service respondsToSelector:_cmd]) {
+ continue;
+ }
+
+ if ([service application:application shouldAllowExtensionPointIdentifier:extensionPointIdentifier]) {
+ result = YES;
+ }
+ }
+
+ return result;
+}
+
+// MARK: SiriKit
+
+- (id)application:(UIApplication *)application handlerForIntent:(INIntent *)intent API_AVAILABLE(ios(14.0)) {
+ for (ApplicationDelegateService *service in services) {
+ if (![service respondsToSelector:_cmd]) {
+ continue;
+ }
+
+ id result = [service application:application handlerForIntent:intent];
+
+ if (result) {
+ return result;
+ }
+ }
+
+ return nil;
+}
+
+// MARK: CloudKit
+
+- (void)application:(UIApplication *)application userDidAcceptCloudKitShareWithMetadata:(CKShareMetadata *)cloudKitShareMetadata {
+ for (ApplicationDelegateService *service in services) {
+ if (![service respondsToSelector:_cmd]) {
+ continue;
+ }
+
+ [service application:application userDidAcceptCloudKitShareWithMetadata:cloudKitShareMetadata];
+ }
+}
+
+/* Handled By Info.plist file for now
+
+// MARK: Interface Geometry
+
+- (UIInterfaceOrientationMask)application:(UIApplication *)application supportedInterfaceOrientationsForWindow:(UIWindow *)window {}
+
+*/
+
+@end
diff --git a/platform/iphone/godot_view.h b/platform/iphone/godot_view.h
index 62fa2f5a32..a8f4cb38d9 100644
--- a/platform/iphone/godot_view.h
+++ b/platform/iphone/godot_view.h
@@ -35,7 +35,7 @@ class String;
@protocol DisplayLayer;
@protocol GodotViewRendererProtocol;
-@interface GodotView : UIView <UIKeyInput>
+@interface GodotView : UIView
@property(assign, nonatomic) id<GodotViewRendererProtocol> renderer;
@@ -51,6 +51,4 @@ class String;
- (void)stopRendering;
- (void)startRendering;
-- (BOOL)becomeFirstResponderWithString:(String)p_existing;
-
@end
diff --git a/platform/iphone/godot_view.mm b/platform/iphone/godot_view.mm
index eaf7e962ce..0c50842cfa 100644
--- a/platform/iphone/godot_view.mm
+++ b/platform/iphone/godot_view.mm
@@ -42,7 +42,6 @@ static const int max_touches = 8;
@interface GodotView () {
UITouch *godot_touches[max_touches];
- String keyboard_text;
}
@property(assign, nonatomic) BOOL isActive;
@@ -278,40 +277,6 @@ static const int max_touches = 8;
// MARK: - Input
-// MARK: Keyboard
-
-- (BOOL)canBecomeFirstResponder {
- return YES;
-}
-
-- (BOOL)becomeFirstResponderWithString:(String)p_existing {
- keyboard_text = p_existing;
- return [self becomeFirstResponder];
-}
-
-- (BOOL)resignFirstResponder {
- keyboard_text = String();
- return [super resignFirstResponder];
-}
-
-- (void)deleteBackward {
- if (keyboard_text.length()) {
- keyboard_text.erase(keyboard_text.length() - 1, 1);
- }
- DisplayServerIPhone::get_singleton()->key(KEY_BACKSPACE, true);
-}
-
-- (BOOL)hasText {
- return keyboard_text.length() > 0;
-}
-
-- (void)insertText:(NSString *)p_text {
- String character;
- character.parse_utf8([p_text UTF8String]);
- keyboard_text = keyboard_text + character;
- DisplayServerIPhone::get_singleton()->key(character[0] == 10 ? KEY_ENTER : character[0], true);
-}
-
// MARK: Touches
- (void)initTouches {
diff --git a/platform/iphone/keyboard_input_view.h b/platform/iphone/keyboard_input_view.h
new file mode 100644
index 0000000000..5382a2e9a9
--- /dev/null
+++ b/platform/iphone/keyboard_input_view.h
@@ -0,0 +1,37 @@
+/*************************************************************************/
+/* keyboard_input_view.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#import <UIKit/UIKit.h>
+
+@interface GodotKeyboardInputView : UITextView
+
+- (BOOL)becomeFirstResponderWithString:(NSString *)existingString multiline:(BOOL)flag cursorStart:(NSInteger)start cursorEnd:(NSInteger)end;
+
+@end
diff --git a/platform/iphone/keyboard_input_view.mm b/platform/iphone/keyboard_input_view.mm
new file mode 100644
index 0000000000..1a37403de7
--- /dev/null
+++ b/platform/iphone/keyboard_input_view.mm
@@ -0,0 +1,195 @@
+/*************************************************************************/
+/* keyboard_input_view.mm */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#import "keyboard_input_view.h"
+
+#include "core/os/keyboard.h"
+#include "display_server_iphone.h"
+#include "os_iphone.h"
+
+@interface GodotKeyboardInputView () <UITextViewDelegate>
+
+@property(nonatomic, copy) NSString *previousText;
+@property(nonatomic, assign) NSRange previousSelectedRange;
+
+@end
+
+@implementation GodotKeyboardInputView
+
+- (instancetype)initWithCoder:(NSCoder *)coder {
+ self = [super initWithCoder:coder];
+
+ if (self) {
+ [self godot_commonInit];
+ }
+
+ return self;
+}
+
+- (instancetype)initWithFrame:(CGRect)frame textContainer:(NSTextContainer *)textContainer {
+ self = [super initWithFrame:frame textContainer:textContainer];
+
+ if (self) {
+ [self godot_commonInit];
+ }
+
+ return self;
+}
+
+- (void)godot_commonInit {
+ self.hidden = YES;
+ self.delegate = self;
+
+ [[NSNotificationCenter defaultCenter] addObserver:self
+ selector:@selector(observeTextChange:)
+ name:UITextViewTextDidChangeNotification
+ object:self];
+}
+
+- (void)dealloc {
+ self.delegate = nil;
+ [[NSNotificationCenter defaultCenter] removeObserver:self];
+}
+
+// MARK: Keyboard
+
+- (BOOL)canBecomeFirstResponder {
+ return YES;
+}
+
+- (BOOL)becomeFirstResponderWithString:(NSString *)existingString multiline:(BOOL)flag cursorStart:(NSInteger)start cursorEnd:(NSInteger)end {
+ self.text = existingString;
+ self.previousText = existingString;
+
+ NSRange textRange;
+
+ // Either a simple cursor or a selection.
+ if (end > 0) {
+ textRange = NSMakeRange(start, end - start);
+ } else {
+ textRange = NSMakeRange(start, 0);
+ }
+
+ self.selectedRange = textRange;
+ self.previousSelectedRange = textRange;
+
+ return [self becomeFirstResponder];
+}
+
+- (BOOL)resignFirstResponder {
+ self.text = nil;
+ self.previousText = nil;
+ return [super resignFirstResponder];
+}
+
+// MARK: OS Messages
+
+- (void)deleteText:(NSInteger)charactersToDelete {
+ for (int i = 0; i < charactersToDelete; i++) {
+ DisplayServerIPhone::get_singleton()->key(KEY_BACKSPACE, true);
+ DisplayServerIPhone::get_singleton()->key(KEY_BACKSPACE, false);
+ }
+}
+
+- (void)enterText:(NSString *)substring {
+ String characters;
+ characters.parse_utf8([substring UTF8String]);
+
+ for (int i = 0; i < characters.size(); i++) {
+ int character = characters[i];
+
+ switch (character) {
+ case 10:
+ character = KEY_ENTER;
+ break;
+ case 8198:
+ character = KEY_SPACE;
+ break;
+ default:
+ break;
+ }
+
+ DisplayServerIPhone::get_singleton()->key(character, true);
+ DisplayServerIPhone::get_singleton()->key(character, false);
+ }
+}
+
+// MARK: Observer
+
+- (void)observeTextChange:(NSNotification *)notification {
+ if (notification.object != self) {
+ return;
+ }
+
+ if (self.previousSelectedRange.length == 0) {
+ // We are deleting all text before cursor if no range was selected.
+ // This way any inserted or changed text will be updated.
+ NSString *substringToDelete = [self.previousText substringToIndex:self.previousSelectedRange.location];
+ [self deleteText:substringToDelete.length];
+ } else {
+ // If text was previously selected
+ // we are sending only one `backspace`.
+ // It will remove all text from text input.
+ [self deleteText:1];
+ }
+
+ NSString *substringToEnter;
+
+ if (self.selectedRange.length == 0) {
+ // If previous cursor had a selection
+ // we have to calculate an inserted text.
+ if (self.previousSelectedRange.length != 0) {
+ NSInteger rangeEnd = self.selectedRange.location + self.selectedRange.length;
+ NSInteger rangeStart = MIN(self.previousSelectedRange.location, self.selectedRange.location);
+ NSInteger rangeLength = MAX(0, rangeEnd - rangeStart);
+
+ NSRange calculatedRange;
+
+ if (rangeLength >= 0) {
+ calculatedRange = NSMakeRange(rangeStart, rangeLength);
+ } else {
+ calculatedRange = NSMakeRange(rangeStart, 0);
+ }
+
+ substringToEnter = [self.text substringWithRange:calculatedRange];
+ } else {
+ substringToEnter = [self.text substringToIndex:self.selectedRange.location];
+ }
+ } else {
+ substringToEnter = [self.text substringWithRange:self.selectedRange];
+ }
+
+ [self enterText:substringToEnter];
+
+ self.previousText = self.text;
+ self.previousSelectedRange = self.selectedRange;
+}
+
+@end
diff --git a/platform/iphone/main.m b/platform/iphone/main.m
index c292f02822..351b40a881 100644
--- a/platform/iphone/main.m
+++ b/platform/iphone/main.m
@@ -28,7 +28,7 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#import "app_delegate.h"
+#import "godot_app_delegate.h"
#import <UIKit/UIKit.h>
#include <stdio.h>
@@ -49,7 +49,8 @@ int main(int argc, char *argv[]) {
printf("running app main\n");
@autoreleasepool {
- UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
+ NSString *className = NSStringFromClass([GodotApplicalitionDelegate class]);
+ UIApplicationMain(argc, argv, nil, className);
}
printf("main done\n");
return 0;
diff --git a/platform/iphone/native_video_view.m b/platform/iphone/native_video_view.m
index a4e9f209f0..1193946f2b 100644
--- a/platform/iphone/native_video_view.m
+++ b/platform/iphone/native_video_view.m
@@ -71,6 +71,12 @@
[self observeVideoAudio];
}
+- (void)layoutSubviews {
+ [super layoutSubviews];
+
+ self.avPlayerLayer.frame = self.bounds;
+}
+
- (void)observeVideoAudio {
printf("******** adding observer for sound routing changes\n");
[[NSNotificationCenter defaultCenter]
diff --git a/platform/iphone/view_controller.h b/platform/iphone/view_controller.h
index 2cc4e4c388..ff76359842 100644
--- a/platform/iphone/view_controller.h
+++ b/platform/iphone/view_controller.h
@@ -32,11 +32,13 @@
@class GodotView;
@class GodotNativeVideoView;
+@class GodotKeyboardInputView;
@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
diff --git a/platform/iphone/view_controller.mm b/platform/iphone/view_controller.mm
index 5d18a72be1..7e44d30851 100644
--- a/platform/iphone/view_controller.mm
+++ b/platform/iphone/view_controller.mm
@@ -33,6 +33,7 @@
#include "display_server_iphone.h"
#import "godot_view.h"
#import "godot_view_renderer.h"
+#import "keyboard_input_view.h"
#import "native_video_view.h"
#include "os_iphone.h"
@@ -43,6 +44,7 @@
@property(strong, nonatomic) GodotViewRenderer *renderer;
@property(strong, nonatomic) GodotNativeVideoView *videoView;
+@property(strong, nonatomic) GodotKeyboardInputView *keyboardView;
@end
@@ -102,6 +104,10 @@
}
- (void)observeKeyboard {
+ printf("******** setting up keyboard input view\n");
+ self.keyboardView = [GodotKeyboardInputView new];
+ [self.view addSubview:self.keyboardView];
+
printf("******** adding observer for keyboard show/hide\n");
[[NSNotificationCenter defaultCenter]
addObserver:self
@@ -119,6 +125,9 @@
[self.videoView stopVideo];
self.videoView = nil;
+
+ self.keyboardView = nil;
+
self.renderer = nil;
[[NSNotificationCenter defaultCenter] removeObserver:self];
@@ -209,8 +218,11 @@
} else {
// Create autoresizing view for video playback.
GodotNativeVideoView *videoView = [[GodotNativeVideoView alloc] initWithFrame:self.view.bounds];
- videoView.autoresizingMask = UIViewAutoresizingFlexibleWidth & UIViewAutoresizingFlexibleHeight;
+ videoView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
[self.view addSubview:videoView];
+
+ self.videoView = videoView;
+
return [self.videoView playVideoAtPath:filePath volume:videoVolume audio:audioTrack subtitle:subtitleTrack];
}
}
diff --git a/platform/javascript/.eslintrc.engine.js b/platform/javascript/.eslintrc.engine.js
new file mode 100644
index 0000000000..00f0f147a9
--- /dev/null
+++ b/platform/javascript/.eslintrc.engine.js
@@ -0,0 +1,10 @@
+module.exports = {
+ "extends": [
+ "./.eslintrc.js",
+ ],
+ "globals": {
+ "Godot": true,
+ "Preloader": true,
+ "Utils": true,
+ },
+};
diff --git a/platform/javascript/.eslintrc.js b/platform/javascript/.eslintrc.js
new file mode 100644
index 0000000000..0ff9d67d26
--- /dev/null
+++ b/platform/javascript/.eslintrc.js
@@ -0,0 +1,43 @@
+module.exports = {
+ "env": {
+ "browser": true,
+ "es2021": true,
+ },
+ "extends": [
+ "airbnb-base",
+ ],
+ "parserOptions": {
+ "ecmaVersion": 12,
+ },
+ "ignorePatterns": "*.externs.js",
+ "rules": {
+ "func-names": "off",
+ // Use tabs for consistency with the C++ codebase.
+ "indent": ["error", "tab"],
+ "max-len": "off",
+ "no-else-return": ["error", {allowElseIf: true}],
+ "curly": ["error", "all"],
+ "brace-style": ["error", "1tbs", { "allowSingleLine": false }],
+ "no-bitwise": "off",
+ "no-continue": "off",
+ "no-self-assign": "off",
+ "no-tabs": "off",
+ "no-param-reassign": ["error", { "props": false }],
+ "no-plusplus": "off",
+ "no-unused-vars": ["error", { "args": "none" }],
+ "prefer-destructuring": "off",
+ "prefer-rest-params": "off",
+ "prefer-spread": "off",
+ "camelcase": "off",
+ "no-underscore-dangle": "off",
+ "max-classes-per-file": "off",
+ "prefer-arrow-callback": "off",
+ // Messes up with copyright headers in source files.
+ "spaced-comment": "off",
+ // Completely breaks emscripten libraries.
+ "object-shorthand": "off",
+ // Closure compiler (exported properties)
+ "quote-props": ["error", "consistent"],
+ "dot-notation": "off",
+ }
+};
diff --git a/platform/javascript/.eslintrc.libs.js b/platform/javascript/.eslintrc.libs.js
new file mode 100644
index 0000000000..e5f0c3d147
--- /dev/null
+++ b/platform/javascript/.eslintrc.libs.js
@@ -0,0 +1,22 @@
+module.exports = {
+ "extends": [
+ "./.eslintrc.js",
+ ],
+ "globals": {
+ "LibraryManager": true,
+ "mergeInto": true,
+ "autoAddDeps": true,
+ "HEAP8": true,
+ "HEAPU8": true,
+ "HEAP32": true,
+ "HEAPF32": true,
+ "ERRNO_CODES": true,
+ "FS": true,
+ "IDBFS": true,
+ "GodotOS": true,
+ "GodotConfig": true,
+ "GodotRuntime": true,
+ "GodotFS": true,
+ "IDHandler": true,
+ },
+};
diff --git a/platform/javascript/SCsub b/platform/javascript/SCsub
index a0e6fa0e18..627ae778b1 100644
--- a/platform/javascript/SCsub
+++ b/platform/javascript/SCsub
@@ -20,27 +20,28 @@ build = env.add_program(build_targets, javascript_files)
env.AddJSLibraries(
[
- "native/http_request.js",
- "native/library_godot_audio.js",
- "native/library_godot_display.js",
- "native/library_godot_os.js",
+ "js/libs/library_godot_audio.js",
+ "js/libs/library_godot_display.js",
+ "js/libs/library_godot_http_request.js",
+ "js/libs/library_godot_os.js",
+ "js/libs/library_godot_runtime.js",
]
)
if env["tools"]:
- env.AddJSLibraries(["native/library_godot_editor_tools.js"])
+ env.AddJSLibraries(["js/libs/library_godot_editor_tools.js"])
if env["javascript_eval"]:
- env.AddJSLibraries(["native/library_godot_eval.js"])
+ env.AddJSLibraries(["js/libs/library_godot_eval.js"])
for lib in env["JS_LIBS"]:
env.Append(LINKFLAGS=["--js-library", lib])
env.Depends(build, env["JS_LIBS"])
engine = [
- "engine/preloader.js",
- "engine/utils.js",
- "engine/engine.js",
+ "js/engine/preloader.js",
+ "js/engine/utils.js",
+ "js/engine/engine.js",
]
-externs = [env.File("#platform/javascript/engine/externs.js")]
+externs = [env.File("#platform/javascript/js/engine/engine.externs.js")]
js_engine = env.CreateEngineFile("#bin/godot${PROGSUFFIX}.engine.js", engine, externs)
env.Depends(js_engine, externs)
@@ -59,7 +60,7 @@ out_files = [
zip_dir.File(binary_name + ".audio.worklet.js"),
]
html_file = "#misc/dist/html/editor.html" if env["tools"] else "#misc/dist/html/full-size.html"
-in_files = [js_wrapped, build[1], html_file, "#platform/javascript/native/audio.worklet.js"]
+in_files = [js_wrapped, build[1], html_file, "#platform/javascript/js/libs/audio.worklet.js"]
if env["threads_enabled"]:
in_files.append(build[2])
out_files.append(zip_dir.File(binary_name + ".worker.js"))
diff --git a/platform/javascript/detect.py b/platform/javascript/detect.py
index e6e35f6aa9..71189cf697 100644
--- a/platform/javascript/detect.py
+++ b/platform/javascript/detect.py
@@ -107,7 +107,6 @@ def configure(env):
env["CC"] = "emcc"
env["CXX"] = "em++"
- env["LINK"] = "emcc"
env["AR"] = "emar"
env["RANLIB"] = "emranlib"
diff --git a/platform/javascript/display_server_javascript.cpp b/platform/javascript/display_server_javascript.cpp
index 768e326e80..af8800d565 100644
--- a/platform/javascript/display_server_javascript.cpp
+++ b/platform/javascript/display_server_javascript.cpp
@@ -600,13 +600,11 @@ void DisplayServerJavaScript::process_joypads() {
#if 0
bool DisplayServerJavaScript::is_joy_known(int p_device) {
-
return Input::get_singleton()->is_joy_mapped(p_device);
}
String DisplayServerJavaScript::get_joy_guid(int p_device) const {
-
return Input::get_singleton()->get_joy_guid_remapped(p_device);
}
#endif
diff --git a/platform/javascript/engine/preloader.js b/platform/javascript/engine/preloader.js
deleted file mode 100644
index 17918eae38..0000000000
--- a/platform/javascript/engine/preloader.js
+++ /dev/null
@@ -1,139 +0,0 @@
-var Preloader = /** @constructor */ function() {
-
- var DOWNLOAD_ATTEMPTS_MAX = 4;
- var progressFunc = null;
- var lastProgress = { loaded: 0, total: 0 };
-
- var loadingFiles = {};
- this.preloadedFiles = [];
-
- function loadXHR(resolve, reject, file, tracker) {
- var xhr = new XMLHttpRequest;
- xhr.open('GET', file);
- if (!file.endsWith('.js')) {
- xhr.responseType = 'arraybuffer';
- }
- ['loadstart', 'progress', 'load', 'error', 'abort'].forEach(function(ev) {
- xhr.addEventListener(ev, onXHREvent.bind(xhr, resolve, reject, file, tracker));
- });
- xhr.send();
- }
-
- function onXHREvent(resolve, reject, file, tracker, ev) {
-
- if (this.status >= 400) {
-
- if (this.status < 500 || ++tracker[file].attempts >= DOWNLOAD_ATTEMPTS_MAX) {
- reject(new Error("Failed loading file '" + file + "': " + this.statusText));
- this.abort();
- return;
- } else {
- setTimeout(loadXHR.bind(null, resolve, reject, file, tracker), 1000);
- }
- }
-
- switch (ev.type) {
- case 'loadstart':
- if (tracker[file] === undefined) {
- tracker[file] = {
- total: ev.total,
- loaded: ev.loaded,
- attempts: 0,
- final: false,
- };
- }
- break;
-
- case 'progress':
- tracker[file].loaded = ev.loaded;
- tracker[file].total = ev.total;
- break;
-
- case 'load':
- tracker[file].final = true;
- resolve(this);
- break;
-
- case 'error':
- if (++tracker[file].attempts >= DOWNLOAD_ATTEMPTS_MAX) {
- tracker[file].final = true;
- reject(new Error("Failed loading file '" + file + "'"));
- } else {
- setTimeout(loadXHR.bind(null, resolve, reject, file, tracker), 1000);
- }
- break;
-
- case 'abort':
- tracker[file].final = true;
- reject(new Error("Loading file '" + file + "' was aborted."));
- break;
- }
- }
-
- this.loadPromise = function(file) {
- return new Promise(function(resolve, reject) {
- loadXHR(resolve, reject, file, loadingFiles);
- });
- }
-
- this.preload = function(pathOrBuffer, destPath) {
- if (pathOrBuffer instanceof ArrayBuffer) {
- pathOrBuffer = new Uint8Array(pathOrBuffer);
- } else if (ArrayBuffer.isView(pathOrBuffer)) {
- pathOrBuffer = new Uint8Array(pathOrBuffer.buffer);
- }
- if (pathOrBuffer instanceof Uint8Array) {
- this.preloadedFiles.push({
- path: destPath,
- buffer: pathOrBuffer
- });
- return Promise.resolve();
- } else if (typeof pathOrBuffer === 'string') {
- var me = this;
- return this.loadPromise(pathOrBuffer).then(function(xhr) {
- me.preloadedFiles.push({
- path: destPath || pathOrBuffer,
- buffer: xhr.response
- });
- return Promise.resolve();
- });
- } else {
- throw Promise.reject("Invalid object for preloading");
- }
- };
-
- var animateProgress = function() {
-
- var loaded = 0;
- var total = 0;
- var totalIsValid = true;
- var progressIsFinal = true;
-
- Object.keys(loadingFiles).forEach(function(file) {
- const stat = loadingFiles[file];
- if (!stat.final) {
- progressIsFinal = false;
- }
- if (!totalIsValid || stat.total === 0) {
- totalIsValid = false;
- total = 0;
- } else {
- total += stat.total;
- }
- loaded += stat.loaded;
- });
- if (loaded !== lastProgress.loaded || total !== lastProgress.total) {
- lastProgress.loaded = loaded;
- lastProgress.total = total;
- if (typeof progressFunc === 'function')
- progressFunc(loaded, total);
- }
- if (!progressIsFinal)
- requestAnimationFrame(animateProgress);
- }
- this.animateProgress = animateProgress; // Also exposed to start it.
-
- this.setProgressFunc = function(callback) {
- progressFunc = callback;
- }
-};
diff --git a/platform/javascript/export/export.cpp b/platform/javascript/export/export.cpp
index d520931067..c3b7e0304e 100644
--- a/platform/javascript/export/export.cpp
+++ b/platform/javascript/export/export.cpp
@@ -294,13 +294,15 @@ void EditorExportPlatformJavaScript::get_preset_features(const Ref<EditorExportP
}
void EditorExportPlatformJavaScript::get_export_options(List<ExportOption> *r_options) {
+ r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "custom_template/debug", PROPERTY_HINT_GLOBAL_FILE, "*.zip"), ""));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "custom_template/release", PROPERTY_HINT_GLOBAL_FILE, "*.zip"), ""));
+
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::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::BOOL, "html/full_window_size"), true));
- r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "custom_template/release", PROPERTY_HINT_GLOBAL_FILE, "*.zip"), ""));
- r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "custom_template/debug", PROPERTY_HINT_GLOBAL_FILE, "*.zip"), ""));
}
String EditorExportPlatformJavaScript::get_name() const {
diff --git a/platform/javascript/engine/externs.js b/platform/javascript/js/engine/engine.externs.js
index 1a94dd15ec..1a94dd15ec 100644
--- a/platform/javascript/engine/externs.js
+++ b/platform/javascript/js/engine/engine.externs.js
diff --git a/platform/javascript/engine/engine.js b/platform/javascript/js/engine/engine.js
index 3745e04479..74153b672a 100644
--- a/platform/javascript/engine/engine.js
+++ b/platform/javascript/js/engine/engine.js
@@ -1,14 +1,14 @@
-Function('return this')()['Engine'] = (function() {
- var preloader = new Preloader();
+const Engine = (function () {
+ const preloader = new Preloader();
- var wasmExt = '.wasm';
- var unloadAfterInit = true;
- var loadPath = '';
- var loadPromise = null;
- var initPromise = null;
- var stderr = null;
- var stdout = null;
- var progressFunc = null;
+ let wasmExt = '.wasm';
+ let unloadAfterInit = true;
+ let loadPath = '';
+ let loadPromise = null;
+ let initPromise = null;
+ let stderr = null;
+ let stdout = null;
+ let progressFunc = null;
function load(basePath) {
if (loadPromise == null) {
@@ -18,14 +18,14 @@ Function('return this')()['Engine'] = (function() {
requestAnimationFrame(preloader.animateProgress);
}
return loadPromise;
- };
+ }
function unload() {
loadPromise = null;
- };
+ }
/** @constructor */
- function Engine() {
+ function Engine() { // eslint-disable-line no-shadow
this.canvas = null;
this.executableName = '';
this.rtenv = null;
@@ -34,30 +34,32 @@ Function('return this')()['Engine'] = (function() {
this.onExecute = null;
this.onExit = null;
this.persistentPaths = ['/userfs'];
- };
+ }
- Engine.prototype.init = /** @param {string=} basePath */ function(basePath) {
+ Engine.prototype.init = /** @param {string=} basePath */ function (basePath) {
if (initPromise) {
return initPromise;
}
if (loadPromise == null) {
if (!basePath) {
- initPromise = Promise.reject(new Error("A base path must be provided when calling `init` and the engine is not loaded."));
+ initPromise = Promise.reject(new Error('A base path must be provided when calling `init` and the engine is not loaded.'));
return initPromise;
}
load(basePath);
}
- var config = {};
- if (typeof stdout === 'function')
+ let config = {};
+ if (typeof stdout === 'function') {
config.print = stdout;
- if (typeof stderr === 'function')
+ }
+ if (typeof stderr === 'function') {
config.printErr = stderr;
- var me = this;
- initPromise = new Promise(function(resolve, reject) {
+ }
+ const me = this;
+ initPromise = new Promise(function (resolve, reject) {
config['locateFile'] = Utils.createLocateRewrite(loadPath);
config['instantiateWasm'] = Utils.createInstantiatePromise(loadPromise);
- Godot(config).then(function(module) {
- module['initFS'](me.persistentPaths).then(function(fs_err) {
+ Godot(config).then(function (module) {
+ module['initFS'](me.persistentPaths).then(function (fs_err) {
me.rtenv = module;
if (unloadAfterInit) {
unload();
@@ -71,25 +73,28 @@ Function('return this')()['Engine'] = (function() {
};
/** @type {function(string, string):Object} */
- Engine.prototype.preloadFile = function(file, path) {
+ Engine.prototype.preloadFile = function (file, path) {
return preloader.preload(file, path);
};
/** @type {function(...string):Object} */
- Engine.prototype.start = function() {
+ Engine.prototype.start = function () {
// Start from arguments.
- var args = [];
- for (var i = 0; i < arguments.length; i++) {
+ const args = [];
+ for (let i = 0; i < arguments.length; i++) {
args.push(arguments[i]);
}
- var me = this;
- return me.init().then(function() {
+ const me = this;
+ return me.init().then(function () {
if (!me.rtenv) {
return Promise.reject(new Error('The engine must be initialized before it can be started'));
}
if (!(me.canvas instanceof HTMLCanvasElement)) {
me.canvas = Utils.findCanvas();
+ if (!me.canvas) {
+ return Promise.reject(new Error('No canvas found in page'));
+ }
}
// Canvas can grab focus on click, or key events won't work.
@@ -98,18 +103,18 @@ Function('return this')()['Engine'] = (function() {
}
// Disable right-click context menu.
- me.canvas.addEventListener('contextmenu', function(ev) {
+ me.canvas.addEventListener('contextmenu', function (ev) {
ev.preventDefault();
}, false);
// Until context restoration is implemented warn the user of context loss.
- me.canvas.addEventListener('webglcontextlost', function(ev) {
- alert("WebGL context lost, please reload the page");
+ me.canvas.addEventListener('webglcontextlost', function (ev) {
+ alert('WebGL context lost, please reload the page'); // eslint-disable-line no-alert
ev.preventDefault();
}, false);
// Browser locale, or custom one if defined.
- var locale = me.customLocale;
+ let locale = me.customLocale;
if (!locale) {
locale = navigator.languages ? navigator.languages[0] : navigator.language;
locale = locale.split('.')[0];
@@ -122,14 +127,14 @@ Function('return this')()['Engine'] = (function() {
'resizeCanvasOnStart': me.resizeCanvasOnStart,
'canvas': me.canvas,
'locale': locale,
- 'onExecute': function(p_args) {
+ 'onExecute': function (p_args) {
if (me.onExecute) {
me.onExecute(p_args);
return 0;
}
return 1;
},
- 'onExit': function(p_code) {
+ 'onExit': function (p_code) {
me.rtenv['deinitFS']();
if (me.onExit) {
me.onExit(p_code);
@@ -138,8 +143,8 @@ Function('return this')()['Engine'] = (function() {
},
});
- return new Promise(function(resolve, reject) {
- preloader.preloadedFiles.forEach(function(file) {
+ return new Promise(function (resolve, reject) {
+ preloader.preloadedFiles.forEach(function (file) {
me.rtenv['copyToFS'](file.path, file.buffer);
});
preloader.preloadedFiles.length = 0; // Clear memory
@@ -150,95 +155,101 @@ Function('return this')()['Engine'] = (function() {
});
};
- Engine.prototype.startGame = function(execName, mainPack, extraArgs) {
+ Engine.prototype.startGame = function (execName, mainPack, extraArgs) {
// Start and init with execName as loadPath if not inited.
this.executableName = execName;
- var me = this;
+ const me = this;
return Promise.all([
this.init(execName),
- this.preloadFile(mainPack, mainPack)
- ]).then(function() {
- var args = ['--main-pack', mainPack];
- if (extraArgs)
+ this.preloadFile(mainPack, mainPack),
+ ]).then(function () {
+ let args = ['--main-pack', mainPack];
+ if (extraArgs) {
args = args.concat(extraArgs);
+ }
return me.start.apply(me, args);
});
};
- Engine.prototype.setWebAssemblyFilenameExtension = function(override) {
+ Engine.prototype.setWebAssemblyFilenameExtension = function (override) {
if (String(override).length === 0) {
throw new Error('Invalid WebAssembly filename extension override');
}
wasmExt = String(override);
};
- Engine.prototype.setUnloadAfterInit = function(enabled) {
+ Engine.prototype.setUnloadAfterInit = function (enabled) {
unloadAfterInit = enabled;
};
- Engine.prototype.setCanvas = function(canvasElem) {
+ Engine.prototype.setCanvas = function (canvasElem) {
this.canvas = canvasElem;
};
- Engine.prototype.setCanvasResizedOnStart = function(enabled) {
+ Engine.prototype.setCanvasResizedOnStart = function (enabled) {
this.resizeCanvasOnStart = enabled;
};
- Engine.prototype.setLocale = function(locale) {
+ Engine.prototype.setLocale = function (locale) {
this.customLocale = locale;
};
- Engine.prototype.setExecutableName = function(newName) {
+ Engine.prototype.setExecutableName = function (newName) {
this.executableName = newName;
};
- Engine.prototype.setProgressFunc = function(func) {
+ Engine.prototype.setProgressFunc = function (func) {
progressFunc = func;
};
- Engine.prototype.setStdoutFunc = function(func) {
- var print = function(text) {
+ Engine.prototype.setStdoutFunc = function (func) {
+ const print = function (text) {
+ let msg = text;
if (arguments.length > 1) {
- text = Array.prototype.slice.call(arguments).join(" ");
+ msg = Array.prototype.slice.call(arguments).join(' ');
}
- func(text);
+ func(msg);
};
- if (this.rtenv)
+ if (this.rtenv) {
this.rtenv.print = print;
+ }
stdout = print;
};
- Engine.prototype.setStderrFunc = function(func) {
- var printErr = function(text) {
- if (arguments.length > 1)
- text = Array.prototype.slice.call(arguments).join(" ");
- func(text);
+ Engine.prototype.setStderrFunc = function (func) {
+ const printErr = function (text) {
+ let msg = text;
+ if (arguments.length > 1) {
+ msg = Array.prototype.slice.call(arguments).join(' ');
+ }
+ func(msg);
};
- if (this.rtenv)
+ if (this.rtenv) {
this.rtenv.printErr = printErr;
+ }
stderr = printErr;
};
- Engine.prototype.setOnExecute = function(onExecute) {
+ Engine.prototype.setOnExecute = function (onExecute) {
this.onExecute = onExecute;
};
- Engine.prototype.setOnExit = function(onExit) {
+ Engine.prototype.setOnExit = function (onExit) {
this.onExit = onExit;
};
- Engine.prototype.copyToFS = function(path, buffer) {
+ Engine.prototype.copyToFS = function (path, buffer) {
if (this.rtenv == null) {
- throw new Error("Engine must be inited before copying files");
+ throw new Error('Engine must be inited before copying files');
}
this.rtenv['copyToFS'](path, buffer);
};
- Engine.prototype.setPersistentPaths = function(persistentPaths) {
+ Engine.prototype.setPersistentPaths = function (persistentPaths) {
this.persistentPaths = persistentPaths;
};
- Engine.prototype.requestQuit = function() {
+ Engine.prototype.requestQuit = function () {
if (this.rtenv) {
this.rtenv['request_quit']();
}
@@ -268,4 +279,7 @@ Function('return this')()['Engine'] = (function() {
Engine.prototype['setPersistentPaths'] = Engine.prototype.setPersistentPaths;
Engine.prototype['requestQuit'] = Engine.prototype.requestQuit;
return Engine;
-})();
+}());
+if (typeof window !== 'undefined') {
+ window['Engine'] = Engine;
+}
diff --git a/platform/javascript/js/engine/preloader.js b/platform/javascript/js/engine/preloader.js
new file mode 100644
index 0000000000..ec34fb93f2
--- /dev/null
+++ b/platform/javascript/js/engine/preloader.js
@@ -0,0 +1,127 @@
+const Preloader = /** @constructor */ function () { // eslint-disable-line no-unused-vars
+ const loadXHR = function (resolve, reject, file, tracker, attempts) {
+ const xhr = new XMLHttpRequest();
+ tracker[file] = {
+ total: 0,
+ loaded: 0,
+ final: false,
+ };
+ xhr.onerror = function () {
+ if (attempts <= 1) {
+ reject(new Error(`Failed loading file '${file}'`));
+ } else {
+ setTimeout(function () {
+ loadXHR(resolve, reject, file, tracker, attempts - 1);
+ }, 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();
+ };
+
+ const DOWNLOAD_ATTEMPTS_MAX = 4;
+ const loadingFiles = {};
+ const lastProgress = { loaded: 0, total: 0 };
+ let progressFunc = null;
+
+ const animateProgress = function () {
+ let loaded = 0;
+ let total = 0;
+ let totalIsValid = true;
+ let progressIsFinal = true;
+
+ Object.keys(loadingFiles).forEach(function (file) {
+ const stat = loadingFiles[file];
+ if (!stat.final) {
+ progressIsFinal = false;
+ }
+ if (!totalIsValid || stat.total === 0) {
+ totalIsValid = false;
+ total = 0;
+ } else {
+ total += stat.total;
+ }
+ loaded += stat.loaded;
+ });
+ if (loaded !== lastProgress.loaded || total !== lastProgress.total) {
+ lastProgress.loaded = loaded;
+ lastProgress.total = total;
+ if (typeof progressFunc === 'function') {
+ progressFunc(loaded, total);
+ }
+ }
+ if (!progressIsFinal) {
+ requestAnimationFrame(animateProgress);
+ }
+ };
+
+ this.animateProgress = animateProgress;
+
+ this.setProgressFunc = function (callback) {
+ progressFunc = callback;
+ };
+
+ this.loadPromise = function (file) {
+ return new Promise(function (resolve, reject) {
+ loadXHR(resolve, reject, file, loadingFiles, DOWNLOAD_ATTEMPTS_MAX);
+ });
+ };
+
+ this.preloadedFiles = [];
+ this.preload = function (pathOrBuffer, destPath) {
+ let buffer = null;
+ if (typeof pathOrBuffer === 'string') {
+ const me = this;
+ return this.loadPromise(pathOrBuffer).then(function (xhr) {
+ me.preloadedFiles.push({
+ path: destPath || pathOrBuffer,
+ buffer: xhr.response,
+ });
+ return Promise.resolve();
+ });
+ } else if (pathOrBuffer instanceof ArrayBuffer) {
+ buffer = new Uint8Array(pathOrBuffer);
+ } else if (ArrayBuffer.isView(pathOrBuffer)) {
+ buffer = new Uint8Array(pathOrBuffer.buffer);
+ }
+ if (buffer) {
+ this.preloadedFiles.push({
+ path: destPath,
+ buffer: pathOrBuffer,
+ });
+ return Promise.resolve();
+ }
+ return Promise.reject(new Error('Invalid object for preloading'));
+ };
+};
diff --git a/platform/javascript/engine/utils.js b/platform/javascript/js/engine/utils.js
index 10e3abe91e..d0fca4e1cb 100644
--- a/platform/javascript/engine/utils.js
+++ b/platform/javascript/js/engine/utils.js
@@ -1,53 +1,56 @@
-var Utils = {
+const Utils = { // eslint-disable-line no-unused-vars
- createLocateRewrite: function(execName) {
+ createLocateRewrite: function (execName) {
function rw(path) {
if (path.endsWith('.worker.js')) {
- return execName + '.worker.js';
+ return `${execName}.worker.js`;
} else if (path.endsWith('.audio.worklet.js')) {
- return execName + '.audio.worklet.js';
+ return `${execName}.audio.worklet.js`;
} else if (path.endsWith('.js')) {
- return execName + '.js';
+ return `${execName}.js`;
} else if (path.endsWith('.wasm')) {
- return execName + '.wasm';
+ return `${execName}.wasm`;
}
+ return path;
}
return rw;
},
- createInstantiatePromise: function(wasmLoader) {
+ createInstantiatePromise: function (wasmLoader) {
+ let loader = wasmLoader;
function instantiateWasm(imports, onSuccess) {
- wasmLoader.then(function(xhr) {
- WebAssembly.instantiate(xhr.response, imports).then(function(result) {
+ loader.then(function (xhr) {
+ WebAssembly.instantiate(xhr.response, imports).then(function (result) {
onSuccess(result['instance'], result['module']);
});
});
- wasmLoader = null;
+ loader = null;
return {};
- };
+ }
return instantiateWasm;
},
- findCanvas: function() {
- var nodes = document.getElementsByTagName('canvas');
+ findCanvas: function () {
+ const nodes = document.getElementsByTagName('canvas');
if (nodes.length && nodes[0] instanceof HTMLCanvasElement) {
return nodes[0];
}
- throw new Error("No canvas found");
+ return null;
},
- isWebGLAvailable: function(majorVersion = 1) {
-
- var testContext = false;
+ isWebGLAvailable: function (majorVersion = 1) {
+ let testContext = false;
try {
- var testCanvas = document.createElement('canvas');
+ const testCanvas = document.createElement('canvas');
if (majorVersion === 1) {
testContext = testCanvas.getContext('webgl') || testCanvas.getContext('experimental-webgl');
} else if (majorVersion === 2) {
testContext = testCanvas.getContext('webgl2') || testCanvas.getContext('experimental-webgl2');
}
- } catch (e) {}
+ } catch (e) {
+ // Not available
+ }
return !!testContext;
- }
+ },
};
diff --git a/platform/javascript/native/audio.worklet.js b/platform/javascript/js/libs/audio.worklet.js
index ad7957e45c..414dc37097 100644
--- a/platform/javascript/native/audio.worklet.js
+++ b/platform/javascript/js/libs/audio.worklet.js
@@ -27,8 +27,8 @@
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-class RingBuffer {
+class RingBuffer {
constructor(p_buffer, p_state) {
this.buffer = p_buffer;
this.avail = p_state;
@@ -46,7 +46,7 @@ class RingBuffer {
read(output) {
const size = this.buffer.length;
- let from = 0
+ let from = 0;
let to_write = output.length;
if (this.rpos + to_write > size) {
const high = size - this.rpos;
@@ -105,7 +105,7 @@ class GodotProcessor extends AudioWorkletProcessor {
}
parse_message(p_cmd, p_data) {
- if (p_cmd == "start" && p_data) {
+ if (p_cmd === 'start' && p_data) {
const state = p_data[0];
let idx = 0;
this.lock = state.subarray(idx, ++idx);
@@ -114,14 +114,14 @@ class GodotProcessor extends AudioWorkletProcessor {
const avail_out = state.subarray(idx, ++idx);
this.input = new RingBuffer(p_data[1], avail_in);
this.output = new RingBuffer(p_data[2], avail_out);
- } else if (p_cmd == "stop") {
+ } else if (p_cmd === 'stop') {
this.runing = false;
this.output = null;
this.input = null;
}
}
- array_has_data(arr) {
+ static array_has_data(arr) {
return arr.length && arr[0].length && arr[0][0].length;
}
@@ -132,39 +132,39 @@ class GodotProcessor extends AudioWorkletProcessor {
if (this.output === null) {
return true; // Not ready yet, keep processing.
}
- const process_input = this.array_has_data(inputs);
+ const process_input = GodotProcessor.array_has_data(inputs);
if (process_input) {
const input = inputs[0];
const chunk = input[0].length * input.length;
- if (this.input_buffer.length != chunk) {
+ if (this.input_buffer.length !== chunk) {
this.input_buffer = new Float32Array(chunk);
}
if (this.input.space_left() >= chunk) {
- this.write_input(this.input_buffer, input);
+ GodotProcessor.write_input(this.input_buffer, input);
this.input.write(this.input_buffer);
} else {
- this.port.postMessage("Input buffer is full! Skipping input frame.");
+ this.port.postMessage('Input buffer is full! Skipping input frame.');
}
}
- const process_output = this.array_has_data(outputs);
+ const process_output = GodotProcessor.array_has_data(outputs);
if (process_output) {
const output = outputs[0];
const chunk = output[0].length * output.length;
- if (this.output_buffer.length != chunk) {
- this.output_buffer = new Float32Array(chunk)
+ if (this.output_buffer.length !== chunk) {
+ this.output_buffer = new Float32Array(chunk);
}
if (this.output.data_left() >= chunk) {
this.output.read(this.output_buffer);
- this.write_output(output, this.output_buffer);
+ GodotProcessor.write_output(output, this.output_buffer);
} else {
- this.port.postMessage("Output buffer has not enough frames! Skipping output frame.");
+ this.port.postMessage('Output buffer has not enough frames! Skipping output frame.');
}
}
this.process_notify();
return true;
}
- write_output(dest, source) {
+ static write_output(dest, source) {
const channels = dest.length;
for (let ch = 0; ch < channels; ch++) {
for (let sample = 0; sample < dest[ch].length; sample++) {
@@ -173,7 +173,7 @@ class GodotProcessor extends AudioWorkletProcessor {
}
}
- write_input(dest, source) {
+ static write_input(dest, source) {
const channels = source.length;
for (let ch = 0; ch < channels; ch++) {
for (let sample = 0; sample < source[ch].length; sample++) {
diff --git a/platform/javascript/native/library_godot_audio.js b/platform/javascript/js/libs/library_godot_audio.js
index 846359b8b2..0c1f477f34 100644
--- a/platform/javascript/native/library_godot_audio.js
+++ b/platform/javascript/js/libs/library_godot_audio.js
@@ -29,74 +29,79 @@
/*************************************************************************/
const GodotAudio = {
-
- $GodotAudio__deps: ['$GodotOS'],
+ $GodotAudio__deps: ['$GodotRuntime', '$GodotOS'],
$GodotAudio: {
ctx: null,
input: null,
driver: null,
interval: 0,
- init: function(mix_rate, latency, onstatechange, onlatencyupdate) {
+ init: function (mix_rate, latency, onstatechange, onlatencyupdate) {
const ctx = new (window.AudioContext || window.webkitAudioContext)({
sampleRate: mix_rate,
// latencyHint: latency / 1000 // Do not specify, leave 'interactive' for good performance.
});
GodotAudio.ctx = ctx;
- onstatechange(ctx.state); // Immeditately notify state.
- ctx.onstatechange = function() {
+ ctx.onstatechange = function () {
let state = 0;
switch (ctx.state) {
- case 'suspended':
- state = 0;
- break;
- case 'running':
- state = 1;
- break;
- case 'closed':
- state = 2;
- break;
+ case 'suspended':
+ state = 0;
+ break;
+ case 'running':
+ state = 1;
+ break;
+ case 'closed':
+ state = 2;
+ break;
+
+ // no default
}
onstatechange(state);
- }
+ };
+ ctx.onstatechange(); // Immeditately notify state.
// Update computed latency
- GodotAudio.interval = setInterval(function() {
- let latency = 0;
+ GodotAudio.interval = setInterval(function () {
+ let computed_latency = 0;
if (ctx.baseLatency) {
- latency += GodotAudio.ctx.baseLatency;
+ computed_latency += GodotAudio.ctx.baseLatency;
}
if (ctx.outputLatency) {
- latency += GodotAudio.ctx.outputLatency;
+ computed_latency += GodotAudio.ctx.outputLatency;
}
- onlatencyupdate(latency);
+ onlatencyupdate(computed_latency);
}, 1000);
GodotOS.atexit(GodotAudio.close_async);
return ctx.destination.channelCount;
},
- create_input: function(callback) {
+ create_input: function (callback) {
if (GodotAudio.input) {
return; // Already started.
}
function gotMediaInput(stream) {
GodotAudio.input = GodotAudio.ctx.createMediaStreamSource(stream);
- callback(GodotAudio.input)
+ callback(GodotAudio.input);
}
if (navigator.mediaDevices.getUserMedia) {
navigator.mediaDevices.getUserMedia({
- "audio": true
- }).then(gotMediaInput, function(e) { out(e) });
+ 'audio': true,
+ }).then(gotMediaInput, function (e) {
+ GodotRuntime.print(e);
+ });
} else {
if (!navigator.getUserMedia) {
navigator.getUserMedia = navigator.webkitGetUserMedia || navigator.mozGetUserMedia;
}
navigator.getUserMedia({
- "audio": true
- }, gotMediaInput, function(e) { out(e) });
+ 'audio': true,
+ }, gotMediaInput, function (e) {
+ GodotRuntime.print(e);
+ });
}
},
- close_async: function(resolve, reject) {
+ close_async: function (resolve, reject) {
const ctx = GodotAudio.ctx;
GodotAudio.ctx = null;
// Audio was not initialized.
@@ -119,14 +124,14 @@ const GodotAudio = {
if (GodotAudio.driver) {
closed = GodotAudio.driver.close();
}
- closed.then(function() {
+ closed.then(function () {
return ctx.close();
- }).then(function() {
+ }).then(function () {
ctx.onstatechange = null;
resolve();
- }).catch(function(e) {
+ }).catch(function (e) {
ctx.onstatechange = null;
- console.error("Error closing AudioContext", e);
+ GodotRuntime.error('Error closing AudioContext', e);
resolve();
});
},
@@ -140,30 +145,30 @@ const GodotAudio = {
return 1;
},
- godot_audio_init: function(p_mix_rate, p_latency, p_state_change, p_latency_update) {
- const statechange = GodotOS.get_func(p_state_change);
- const latencyupdate = GodotOS.get_func(p_latency_update);
+ godot_audio_init: function (p_mix_rate, p_latency, p_state_change, p_latency_update) {
+ const statechange = GodotRuntime.get_func(p_state_change);
+ const latencyupdate = GodotRuntime.get_func(p_latency_update);
return GodotAudio.init(p_mix_rate, p_latency, statechange, latencyupdate);
},
- godot_audio_resume: function() {
- if (GodotAudio.ctx && GodotAudio.ctx.state != 'running') {
+ godot_audio_resume: function () {
+ if (GodotAudio.ctx && GodotAudio.ctx.state !== 'running') {
GodotAudio.ctx.resume();
}
},
godot_audio_capture_start__proxy: 'sync',
- godot_audio_capture_start: function() {
+ godot_audio_capture_start: function () {
if (GodotAudio.input) {
return; // Already started.
}
- GodotAudio.create_input(function(input) {
+ GodotAudio.create_input(function (input) {
input.connect(GodotAudio.driver.get_node());
});
},
godot_audio_capture_stop__proxy: 'sync',
- godot_audio_capture_stop: function() {
+ godot_audio_capture_stop: function () {
if (GodotAudio.input) {
const tracks = GodotAudio.input['mediaStream']['getTracks']();
for (let i = 0; i < tracks.length; i++) {
@@ -175,55 +180,54 @@ const GodotAudio = {
},
};
-autoAddDeps(GodotAudio, "$GodotAudio");
+autoAddDeps(GodotAudio, '$GodotAudio');
mergeInto(LibraryManager.library, GodotAudio);
/**
* The AudioWorklet API driver, used when threads are available.
*/
const GodotAudioWorklet = {
-
- $GodotAudioWorklet__deps: ['$GodotAudio'],
+ $GodotAudioWorklet__deps: ['$GodotAudio', '$GodotConfig'],
$GodotAudioWorklet: {
promise: null,
worklet: null,
- create: function(channels) {
- const path = Module['locateFile']('godot.audio.worklet.js');
- GodotAudioWorklet.promise = GodotAudio.ctx.audioWorklet.addModule(path).then(function() {
+ create: function (channels) {
+ const path = GodotConfig.locate_file('godot.audio.worklet.js');
+ GodotAudioWorklet.promise = GodotAudio.ctx.audioWorklet.addModule(path).then(function () {
GodotAudioWorklet.worklet = new AudioWorkletNode(
GodotAudio.ctx,
'godot-processor',
{
- 'outputChannelCount': [channels]
- }
+ 'outputChannelCount': [channels],
+ },
);
return Promise.resolve();
});
GodotAudio.driver = GodotAudioWorklet;
},
- start: function(in_buf, out_buf, state) {
- GodotAudioWorklet.promise.then(function() {
+ start: function (in_buf, out_buf, state) {
+ GodotAudioWorklet.promise.then(function () {
const node = GodotAudioWorklet.worklet;
node.connect(GodotAudio.ctx.destination);
node.port.postMessage({
'cmd': 'start',
'data': [state, in_buf, out_buf],
});
- node.port.onmessage = function(event) {
- console.error(event.data);
+ node.port.onmessage = function (event) {
+ GodotRuntime.error(event.data);
};
});
},
- get_node: function() {
+ get_node: function () {
return GodotAudioWorklet.worklet;
},
- close: function() {
- return new Promise(function(resolve, reject) {
- GodotAudioWorklet.promise.then(function() {
+ close: function () {
+ return new Promise(function (resolve, reject) {
+ GodotAudioWorklet.promise.then(function () {
GodotAudioWorklet.worklet.port.postMessage({
'cmd': 'stop',
'data': null,
@@ -237,53 +241,52 @@ const GodotAudioWorklet = {
},
},
- godot_audio_worklet_create: function(channels) {
+ godot_audio_worklet_create: function (channels) {
GodotAudioWorklet.create(channels);
},
- godot_audio_worklet_start: function(p_in_buf, p_in_size, p_out_buf, p_out_size, p_state) {
- const out_buffer = GodotOS.heapSub(HEAPF32, p_out_buf, p_out_size);
- const in_buffer = GodotOS.heapSub(HEAPF32, p_in_buf, p_in_size);
- const state = GodotOS.heapSub(HEAP32, p_state, 4);
+ godot_audio_worklet_start: function (p_in_buf, p_in_size, p_out_buf, p_out_size, p_state) {
+ const out_buffer = GodotRuntime.heapSub(HEAPF32, p_out_buf, p_out_size);
+ const in_buffer = GodotRuntime.heapSub(HEAPF32, p_in_buf, p_in_size);
+ const state = GodotRuntime.heapSub(HEAP32, p_state, 4);
GodotAudioWorklet.start(in_buffer, out_buffer, state);
},
- godot_audio_worklet_state_wait: function(p_state, p_idx, p_expected, p_timeout) {
+ godot_audio_worklet_state_wait: function (p_state, p_idx, p_expected, p_timeout) {
Atomics.wait(HEAP32, (p_state >> 2) + p_idx, p_expected, p_timeout);
return Atomics.load(HEAP32, (p_state >> 2) + p_idx);
},
- godot_audio_worklet_state_add: function(p_state, p_idx, p_value) {
+ godot_audio_worklet_state_add: function (p_state, p_idx, p_value) {
return Atomics.add(HEAP32, (p_state >> 2) + p_idx, p_value);
},
- godot_audio_worklet_state_get: function(p_state, p_idx) {
+ godot_audio_worklet_state_get: function (p_state, p_idx) {
return Atomics.load(HEAP32, (p_state >> 2) + p_idx);
},
};
-autoAddDeps(GodotAudioWorklet, "$GodotAudioWorklet");
+autoAddDeps(GodotAudioWorklet, '$GodotAudioWorklet');
mergeInto(LibraryManager.library, GodotAudioWorklet);
/*
* The deprecated ScriptProcessorNode API, used when threads are disabled.
*/
const GodotAudioScript = {
-
$GodotAudioScript__deps: ['$GodotAudio'],
$GodotAudioScript: {
script: null,
- create: function(buffer_length, channel_count) {
+ create: function (buffer_length, channel_count) {
GodotAudioScript.script = GodotAudio.ctx.createScriptProcessor(buffer_length, 2, channel_count);
GodotAudio.driver = GodotAudioScript;
return GodotAudioScript.script.bufferSize;
},
- start: function(p_in_buf, p_in_size, p_out_buf, p_out_size, onprocess) {
- GodotAudioScript.script.onaudioprocess = function(event) {
+ start: function (p_in_buf, p_in_size, p_out_buf, p_out_size, onprocess) {
+ GodotAudioScript.script.onaudioprocess = function (event) {
// Read input
- const inb = GodotOS.heapSub(HEAPF32, p_in_buf, p_in_size);
+ const inb = GodotRuntime.heapSub(HEAPF32, p_in_buf, p_in_size);
const input = event.inputBuffer;
if (GodotAudio.input) {
const inlen = input.getChannelData(0).length;
@@ -299,7 +302,7 @@ const GodotAudioScript = {
onprocess();
// Write the output.
- const outb = GodotOS.heapSub(HEAPF32, p_out_buf, p_out_size);
+ const outb = GodotRuntime.heapSub(HEAPF32, p_out_buf, p_out_size);
const output = event.outputBuffer;
const channels = output.numberOfChannels;
for (let ch = 0; ch < channels; ch++) {
@@ -313,12 +316,12 @@ const GodotAudioScript = {
GodotAudioScript.script.connect(GodotAudio.ctx.destination);
},
- get_node: function() {
+ get_node: function () {
return GodotAudioScript.script;
},
- close: function() {
- return new Promise(function(resolve, reject) {
+ close: function () {
+ return new Promise(function (resolve, reject) {
GodotAudioScript.script.disconnect();
GodotAudioScript.script.onaudioprocess = null;
GodotAudioScript.script = null;
@@ -327,15 +330,15 @@ const GodotAudioScript = {
},
},
- godot_audio_script_create: function(buffer_length, channel_count) {
+ godot_audio_script_create: function (buffer_length, channel_count) {
return GodotAudioScript.create(buffer_length, channel_count);
},
- godot_audio_script_start: function(p_in_buf, p_in_size, p_out_buf, p_out_size, p_cb) {
- const onprocess = GodotOS.get_func(p_cb);
+ godot_audio_script_start: function (p_in_buf, p_in_size, p_out_buf, p_out_size, p_cb) {
+ const onprocess = GodotRuntime.get_func(p_cb);
GodotAudioScript.start(p_in_buf, p_in_size, p_out_buf, p_out_size, onprocess);
},
};
-autoAddDeps(GodotAudioScript, "$GodotAudioScript");
+autoAddDeps(GodotAudioScript, '$GodotAudioScript');
mergeInto(LibraryManager.library, GodotAudioScript);
diff --git a/platform/javascript/native/library_godot_display.js b/platform/javascript/js/libs/library_godot_display.js
index 490b9181d0..9651b48952 100644
--- a/platform/javascript/native/library_godot_display.js
+++ b/platform/javascript/js/libs/library_godot_display.js
@@ -33,32 +33,33 @@
* Keeps track of registered event listeners so it can remove them on shutdown.
*/
const GodotDisplayListeners = {
+ $GodotDisplayListeners__deps: ['$GodotOS'],
$GodotDisplayListeners__postset: 'GodotOS.atexit(function(resolve, reject) { GodotDisplayListeners.clear(); resolve(); });',
$GodotDisplayListeners: {
handlers: [],
- has: function(target, event, method, capture) {
- return GodotDisplayListeners.handlers.findIndex(function(e) {
- return e.target === target && e.event === event && e.method === method && e.capture == capture;
+ has: function (target, event, method, capture) {
+ return GodotDisplayListeners.handlers.findIndex(function (e) {
+ return e.target === target && e.event === event && e.method === method && e.capture === capture;
}) !== -1;
},
- add: function(target, event, method, capture) {
+ add: function (target, event, method, capture) {
if (GodotDisplayListeners.has(target, event, method, capture)) {
return;
}
- function Handler(target, event, method, capture) {
- this.target = target;
- this.event = event;
- this.method = method;
- this.capture = capture;
- };
+ function Handler(p_target, p_event, p_method, p_capture) {
+ this.target = p_target;
+ this.event = p_event;
+ this.method = p_method;
+ this.capture = p_capture;
+ }
GodotDisplayListeners.handlers.push(new Handler(target, event, method, capture));
target.addEventListener(event, method, capture);
},
- clear: function() {
- GodotDisplayListeners.handlers.forEach(function(h) {
+ clear: function () {
+ GodotDisplayListeners.handlers.forEach(function (h) {
h.target.removeEventListener(h.event, h.method, h.capture);
});
GodotDisplayListeners.handlers.length = 0;
@@ -78,26 +79,25 @@ mergeInto(LibraryManager.library, GodotDisplayListeners);
* deferred callbacks won't be able to access the files.
*/
const GodotDisplayDragDrop = {
-
$GodotDisplayDragDrop__deps: ['$FS', '$GodotFS'],
$GodotDisplayDragDrop: {
promises: [],
pending_files: [],
- add_entry: function(entry) {
+ add_entry: function (entry) {
if (entry.isDirectory) {
GodotDisplayDragDrop.add_dir(entry);
} else if (entry.isFile) {
GodotDisplayDragDrop.add_file(entry);
} else {
- console.error("Unrecognized entry...", entry);
+ GodotRuntime.error('Unrecognized entry...', entry);
}
},
- add_dir: function(entry) {
- GodotDisplayDragDrop.promises.push(new Promise(function(resolve, reject) {
+ add_dir: function (entry) {
+ GodotDisplayDragDrop.promises.push(new Promise(function (resolve, reject) {
const reader = entry.createReader();
- reader.readEntries(function(entries) {
+ reader.readEntries(function (entries) {
for (let i = 0; i < entries.length; i++) {
GodotDisplayDragDrop.add_entry(entries[i]);
}
@@ -106,58 +106,58 @@ const GodotDisplayDragDrop = {
}));
},
- add_file: function(entry) {
- GodotDisplayDragDrop.promises.push(new Promise(function(resolve, reject) {
- entry.file(function(file) {
+ add_file: function (entry) {
+ GodotDisplayDragDrop.promises.push(new Promise(function (resolve, reject) {
+ entry.file(function (file) {
const reader = new FileReader();
- reader.onload = function() {
+ reader.onload = function () {
const f = {
- "path": file.relativePath || file.webkitRelativePath,
- "name": file.name,
- "type": file.type,
- "size": file.size,
- "data": reader.result
+ 'path': file.relativePath || file.webkitRelativePath,
+ 'name': file.name,
+ 'type': file.type,
+ 'size': file.size,
+ 'data': reader.result,
};
if (!f['path']) {
f['path'] = f['name'];
}
GodotDisplayDragDrop.pending_files.push(f);
- resolve()
+ resolve();
};
- reader.onerror = function() {
- console.log("Error reading file");
+ reader.onerror = function () {
+ GodotRuntime.print('Error reading file');
reject();
- }
+ };
reader.readAsArrayBuffer(file);
- }, function(err) {
- console.log("Error!");
+ }, function (err) {
+ GodotRuntime.print('Error!');
reject();
});
}));
},
- process: function(resolve, reject) {
- if (GodotDisplayDragDrop.promises.length == 0) {
+ process: function (resolve, reject) {
+ if (GodotDisplayDragDrop.promises.length === 0) {
resolve();
return;
}
- GodotDisplayDragDrop.promises.pop().then(function() {
- setTimeout(function() {
+ GodotDisplayDragDrop.promises.pop().then(function () {
+ setTimeout(function () {
GodotDisplayDragDrop.process(resolve, reject);
}, 0);
});
},
- _process_event: function(ev, callback) {
+ _process_event: function (ev, callback) {
ev.preventDefault();
if (ev.dataTransfer.items) {
// Use DataTransferItemList interface to access the file(s)
for (let i = 0; i < ev.dataTransfer.items.length; i++) {
const item = ev.dataTransfer.items[i];
let entry = null;
- if ("getAsEntry" in item) {
+ if ('getAsEntry' in item) {
entry = item.getAsEntry();
- } else if ("webkitGetAsEntry" in item) {
+ } else if ('webkitGetAsEntry' in item) {
entry = item.webkitGetAsEntry();
}
if (entry) {
@@ -165,25 +165,25 @@ const GodotDisplayDragDrop = {
}
}
} else {
- console.error("File upload not supported");
+ GodotRuntime.error('File upload not supported');
}
- new Promise(GodotDisplayDragDrop.process).then(function() {
- const DROP = "/tmp/drop-" + parseInt(Math.random() * Math.pow(2, 31)) + "/";
+ new Promise(GodotDisplayDragDrop.process).then(function () {
+ const DROP = `/tmp/drop-${parseInt(Math.random() * (1 << 30), 10)}/`;
const drops = [];
const files = [];
FS.mkdir(DROP);
GodotDisplayDragDrop.pending_files.forEach((elem) => {
const path = elem['path'];
GodotFS.copy_to_fs(DROP + path, elem['data']);
- let idx = path.indexOf("/");
- if (idx == -1) {
+ let idx = path.indexOf('/');
+ if (idx === -1) {
// Root file
drops.push(DROP + path);
} else {
// Subdir
const sub = path.substr(0, idx);
- idx = sub.indexOf("/");
- if (idx < 0 && drops.indexOf(DROP + sub) == -1) {
+ idx = sub.indexOf('/');
+ if (idx < 0 && drops.indexOf(DROP + sub) === -1) {
drops.push(DROP + sub);
}
}
@@ -192,37 +192,38 @@ const GodotDisplayDragDrop = {
GodotDisplayDragDrop.promises = [];
GodotDisplayDragDrop.pending_files = [];
callback(drops);
- const dirs = [DROP.substr(0, DROP.length -1)];
+ 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("/");
+ let dir = file.replace(DROP, '');
+ let idx = dir.lastIndexOf('/');
while (idx > 0) {
dir = dir.substr(0, idx);
- if (dirs.indexOf(DROP + dir) == -1) {
+ if (dirs.indexOf(DROP + dir) === -1) {
dirs.push(DROP + dir);
}
- idx = dir.lastIndexOf("/");
+ idx = dir.lastIndexOf('/');
}
});
// Remove dirs.
- dirs.sort(function(a, b) {
+ dirs.sort(function (a, b) {
const al = (a.match(/\//g) || []).length;
const bl = (b.match(/\//g) || []).length;
- if (al > bl)
+ if (al > bl) {
return -1;
- else if (al < bl)
+ } else if (al < bl) {
return 1;
+ }
return 0;
- }).forEach(function(dir) {
+ }).forEach(function (dir) {
FS.rmdir(dir);
});
});
},
- handler: function(callback) {
- return function(ev) {
+ handler: function (callback) {
+ return function (ev) {
GodotDisplayDragDrop._process_event(ev, callback);
};
},
@@ -235,31 +236,31 @@ mergeInto(LibraryManager.library, GodotDisplayDragDrop);
* Keeps track of cursor status and custom shapes.
*/
const GodotDisplayCursor = {
+ $GodotDisplayCursor__deps: ['$GodotOS', '$GodotConfig'],
$GodotDisplayCursor__postset: 'GodotOS.atexit(function(resolve, reject) { GodotDisplayCursor.clear(); resolve(); });',
- $GodotDisplayCursor__deps: ['$GodotConfig', '$GodotOS'],
$GodotDisplayCursor: {
shape: 'auto',
visible: true,
cursors: {},
- set_style: function(style) {
+ set_style: function (style) {
GodotConfig.canvas.style.cursor = style;
},
- set_shape: function(shape) {
+ set_shape: function (shape) {
GodotDisplayCursor.shape = shape;
let css = shape;
if (shape in GodotDisplayCursor.cursors) {
const c = GodotDisplayCursor.cursors[shape];
- css = 'url("' + c.url + '") ' + c.x + ' ' + c.y + ', auto';
+ css = `url("${c.url}") ${c.x} ${c.y}, auto`;
}
if (GodotDisplayCursor.visible) {
GodotDisplayCursor.set_style(css);
}
},
- clear: function() {
+ clear: function () {
GodotDisplayCursor.set_style('');
GodotDisplayCursor.shape = 'auto';
GodotDisplayCursor.visible = true;
- Object.keys(GodotDisplayCursor.cursors).forEach(function(key) {
+ Object.keys(GodotDisplayCursor.cursors).forEach(function (key) {
URL.revokeObjectURL(GodotDisplayCursor.cursors[key]);
delete GodotDisplayCursor.cursors[key];
});
@@ -274,75 +275,74 @@ mergeInto(LibraryManager.library, GodotDisplayCursor);
* Exposes all the functions needed by DisplayServer implementation.
*/
const GodotDisplay = {
- $GodotDisplay__deps: ['$GodotConfig', '$GodotOS', '$GodotDisplayCursor', '$GodotDisplayListeners', '$GodotDisplayDragDrop'],
+ $GodotDisplay__deps: ['$GodotConfig', '$GodotRuntime', '$GodotDisplayCursor', '$GodotDisplayListeners', '$GodotDisplayDragDrop'],
$GodotDisplay: {
window_icon: '',
},
- godot_js_display_is_swap_ok_cancel: function() {
+ godot_js_display_is_swap_ok_cancel: function () {
const win = (['Windows', 'Win64', 'Win32', 'WinCE']);
- const plat = navigator.platform || "";
+ const plat = navigator.platform || '';
if (win.indexOf(plat) !== -1) {
return 1;
}
return 0;
},
- godot_js_display_alert: function(p_text) {
- window.alert(UTF8ToString(p_text));
+ godot_js_display_alert: function (p_text) {
+ window.alert(GodotRuntime.parseString(p_text)); // eslint-disable-line no-alert
},
- godot_js_display_pixel_ratio_get: function() {
+ godot_js_display_pixel_ratio_get: function () {
return window.devicePixelRatio || 1;
},
/*
* Canvas
*/
- godot_js_display_canvas_focus: function() {
+ godot_js_display_canvas_focus: function () {
GodotConfig.canvas.focus();
},
- godot_js_display_canvas_is_focused: function() {
- return document.activeElement == GodotConfig.canvas;
+ godot_js_display_canvas_is_focused: function () {
+ return document.activeElement === GodotConfig.canvas;
},
- godot_js_display_canvas_bounding_rect_position_get: function(r_x, r_y) {
+ godot_js_display_canvas_bounding_rect_position_get: function (r_x, r_y) {
const brect = GodotConfig.canvas.getBoundingClientRect();
- setValue(r_x, brect.x, 'i32');
- setValue(r_y, brect.y, 'i32');
+ GodotRuntime.setHeapValue(r_x, brect.x, 'i32');
+ GodotRuntime.setHeapValue(r_y, brect.y, 'i32');
},
/*
* Touchscreen
*/
- godot_js_display_touchscreen_is_available: function() {
+ godot_js_display_touchscreen_is_available: function () {
return 'ontouchstart' in window;
},
/*
* Clipboard
*/
- godot_js_display_clipboard_set: function(p_text) {
- const text = UTF8ToString(p_text);
+ godot_js_display_clipboard_set: function (p_text) {
+ const text = GodotRuntime.parseString(p_text);
if (!navigator.clipboard || !navigator.clipboard.writeText) {
return 1;
}
- navigator.clipboard.writeText(text).catch(function(e) {
+ navigator.clipboard.writeText(text).catch(function (e) {
// Setting OS clipboard is only possible from an input callback.
- console.error("Setting OS clipboard is only possible from an input callback for the HTML5 plafrom. Exception:", e);
+ GodotRuntime.error('Setting OS clipboard is only possible from an input callback for the HTML5 plafrom. Exception:', e);
});
return 0;
},
- godot_js_display_clipboard_get_deps: ['$GodotOS'],
- godot_js_display_clipboard_get: function(callback) {
- const func = GodotOS.get_func(callback);
+ godot_js_display_clipboard_get: function (callback) {
+ const func = GodotRuntime.get_func(callback);
try {
navigator.clipboard.readText().then(function (result) {
- const ptr = allocate(intArrayFromString(result), ALLOC_NORMAL);
+ const ptr = GodotRuntime.allocString(result);
func(ptr);
- _free(ptr);
+ GodotRuntime.free(ptr);
}).catch(function (e) {
// Fail graciously.
});
@@ -354,19 +354,19 @@ const GodotDisplay = {
/*
* Window
*/
- godot_js_display_window_request_fullscreen: function() {
+ godot_js_display_window_request_fullscreen: function () {
const canvas = GodotConfig.canvas;
- (canvas.requestFullscreen || canvas.msRequestFullscreen ||
- canvas.mozRequestFullScreen || canvas.mozRequestFullscreen ||
- canvas.webkitRequestFullscreen
+ (canvas.requestFullscreen || canvas.msRequestFullscreen
+ || canvas.mozRequestFullScreen || canvas.mozRequestFullscreen
+ || canvas.webkitRequestFullscreen
).call(canvas);
},
- godot_js_display_window_title_set: function(p_data) {
- document.title = UTF8ToString(p_data);
+ godot_js_display_window_title_set: function (p_data) {
+ document.title = GodotRuntime.parseString(p_data);
},
- godot_js_display_window_icon_set: function(p_ptr, p_len) {
+ godot_js_display_window_icon_set: function (p_ptr, p_len) {
let link = document.getElementById('-gd-engine-icon');
if (link === null) {
link = document.createElement('link');
@@ -375,7 +375,7 @@ const GodotDisplay = {
document.head.appendChild(link);
}
const old_icon = GodotDisplay.window_icon;
- const png = new Blob([GodotOS.heapCopy(HEAPU8, p_ptr, p_len)], { type: "image/png" });
+ const png = new Blob([GodotRuntime.heapCopy(HEAPU8, p_ptr, p_len)], { type: 'image/png' });
GodotDisplay.window_icon = URL.createObjectURL(png);
link.href = GodotDisplay.window_icon;
if (old_icon) {
@@ -386,9 +386,9 @@ const GodotDisplay = {
/*
* Cursor
*/
- godot_js_display_cursor_set_visible: function(p_visible) {
- const visible = p_visible != 0;
- if (visible == GodotDisplayCursor.visible) {
+ godot_js_display_cursor_set_visible: function (p_visible) {
+ const visible = p_visible !== 0;
+ if (visible === GodotDisplayCursor.visible) {
return;
}
GodotDisplayCursor.visible = visible;
@@ -399,19 +399,19 @@ const GodotDisplay = {
}
},
- godot_js_display_cursor_is_hidden: function() {
+ godot_js_display_cursor_is_hidden: function () {
return !GodotDisplayCursor.visible;
},
- godot_js_display_cursor_set_shape: function(p_string) {
- GodotDisplayCursor.set_shape(UTF8ToString(p_string));
+ godot_js_display_cursor_set_shape: function (p_string) {
+ GodotDisplayCursor.set_shape(GodotRuntime.parseString(p_string));
},
- godot_js_display_cursor_set_custom_shape: function(p_shape, p_ptr, p_len, p_hotspot_x, p_hotspot_y) {
- const shape = UTF8ToString(p_shape);
+ godot_js_display_cursor_set_custom_shape: function (p_shape, p_ptr, p_len, p_hotspot_x, p_hotspot_y) {
+ const shape = GodotRuntime.parseString(p_shape);
const old_shape = GodotDisplayCursor.cursors[shape];
if (p_len > 0) {
- const png = new Blob([GodotOS.heapCopy(HEAPU8, p_ptr, p_len)], { type: 'image/png' });
+ const png = new Blob([GodotRuntime.heapCopy(HEAPU8, p_ptr, p_len)], { type: 'image/png' });
const url = URL.createObjectURL(png);
GodotDisplayCursor.cursors[shape] = {
url: url,
@@ -421,7 +421,7 @@ const GodotDisplay = {
} else {
delete GodotDisplayCursor.cursors[shape];
}
- if (shape == GodotDisplayCursor.shape) {
+ if (shape === GodotDisplayCursor.shape) {
GodotDisplayCursor.set_shape(GodotDisplayCursor.shape);
}
if (old_shape) {
@@ -432,41 +432,41 @@ const GodotDisplay = {
/*
* Listeners
*/
- godot_js_display_notification_cb: function(callback, p_enter, p_exit, p_in, p_out) {
+ godot_js_display_notification_cb: function (callback, p_enter, p_exit, p_in, p_out) {
const canvas = GodotConfig.canvas;
- const func = GodotOS.get_func(callback);
+ const func = GodotRuntime.get_func(callback);
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() {
+ ['mouseover', 'mouseleave', 'focus', 'blur'].forEach(function (evt_name, idx) {
+ GodotDisplayListeners.add(canvas, evt_name, function () {
func.bind(null, notif[idx]);
}, true);
});
},
- godot_js_display_paste_cb: function(callback) {
- const func = GodotOS.get_func(callback);
- GodotDisplayListeners.add(window, 'paste', function(evt) {
+ godot_js_display_paste_cb: function (callback) {
+ const func = GodotRuntime.get_func(callback);
+ GodotDisplayListeners.add(window, 'paste', function (evt) {
const text = evt.clipboardData.getData('text');
- const ptr = allocate(intArrayFromString(text), ALLOC_NORMAL);
+ const ptr = GodotRuntime.allocString(text);
func(ptr);
- _free(ptr);
+ GodotRuntime.free(ptr);
}, false);
},
- godot_js_display_drop_files_cb: function(callback) {
- const func = GodotOS.get_func(callback)
- const dropFiles = function(files) {
+ godot_js_display_drop_files_cb: function (callback) {
+ const func = GodotRuntime.get_func(callback);
+ const dropFiles = function (files) {
const args = files || [];
if (!args.length) {
return;
}
const argc = args.length;
- const argv = GodotOS.allocStringArray(args);
+ const argv = GodotRuntime.allocStringArray(args);
func(argv, argc);
- GodotOS.freeStringArray(argv, argc);
+ GodotRuntime.freeStringArray(argv, argc);
};
const canvas = GodotConfig.canvas;
- GodotDisplayListeners.add(canvas, 'dragover', function(ev) {
+ GodotDisplayListeners.add(canvas, 'dragover', function (ev) {
// Prevent default behavior (which would try to open the file(s))
ev.preventDefault();
}, false);
diff --git a/platform/javascript/native/library_godot_editor_tools.js b/platform/javascript/js/libs/library_godot_editor_tools.js
index bd62bbf4e1..f39fed04a8 100644
--- a/platform/javascript/native/library_godot_editor_tools.js
+++ b/platform/javascript/js/libs/library_godot_editor_tools.js
@@ -29,12 +29,11 @@
/*************************************************************************/
const GodotEditorTools = {
-
godot_js_editor_download_file__deps: ['$FS'],
- godot_js_editor_download_file: function(p_path, p_name, p_mime) {
- const path = UTF8ToString(p_path);
- const name = UTF8ToString(p_name);
- const mime = UTF8ToString(p_mime);
+ 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');
diff --git a/platform/javascript/native/library_godot_eval.js b/platform/javascript/js/libs/library_godot_eval.js
index e83c61dd9d..33ff231726 100644
--- a/platform/javascript/native/library_godot_eval.js
+++ b/platform/javascript/js/libs/library_godot_eval.js
@@ -29,59 +29,57 @@
/*************************************************************************/
const GodotEval = {
-
- godot_js_eval__deps: ['$GodotOS'],
- 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 = UTF8ToString(p_js);
+ godot_js_eval__deps: ['$GodotRuntime'],
+ 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;
+ const global_eval = eval; // eslint-disable-line no-eval
eval_ret = global_eval(js_code);
} else {
- eval_ret = eval(js_code);
+ eval_ret = eval(js_code); // eslint-disable-line no-eval
}
} catch (e) {
- err(e);
+ GodotRuntime.error(e);
}
switch (typeof eval_ret) {
+ case 'boolean':
+ GodotRuntime.setHeapValue(p_union_ptr, eval_ret, 'i32');
+ return 1; // BOOL
- case 'boolean':
- setValue(p_union_ptr, eval_ret, 'i32');
- return 1; // BOOL
+ case 'number':
+ GodotRuntime.setHeapValue(p_union_ptr, eval_ret, 'double');
+ return 3; // REAL
- case 'number':
- setValue(p_union_ptr, eval_ret, 'double');
- return 3; // REAL
+ case 'string':
+ GodotRuntime.setHeapValue(p_union_ptr, GodotRuntime.allocString(eval_ret), '*');
+ return 4; // STRING
- case 'string':
- let array_ptr = GodotOS.allocString(eval_ret);
- setValue(p_union_ptr, array_ptr , '*');
- return 4; // STRING
+ case 'object':
+ if (eval_ret === null) {
+ break;
+ }
- 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;
- 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 = GodotOS.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/native/http_request.js b/platform/javascript/js/libs/library_godot_http_request.js
index f621689f9d..2b9aa88208 100644
--- a/platform/javascript/native/http_request.js
+++ b/platform/javascript/js/libs/library_godot_http_request.js
@@ -27,120 +27,119 @@
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-var GodotHTTPRequest = {
+const GodotHTTPRequest = {
+ $GodotHTTPRequest__deps: ['$GodotRuntime'],
$GodotHTTPRequest: {
-
requests: [],
- getUnusedRequestId: function() {
- var idMax = GodotHTTPRequest.requests.length;
- for (var potentialId = 0; potentialId < idMax; ++potentialId) {
+ 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)
+ GodotHTTPRequest.requests.push(null);
return idMax;
},
- setupRequest: function(xhr) {
+ setupRequest: function (xhr) {
xhr.responseType = 'arraybuffer';
},
},
- godot_xhr_new: function() {
- var newId = GodotHTTPRequest.getUnusedRequestId();
- GodotHTTPRequest.requests[newId] = new XMLHttpRequest;
+ godot_xhr_new: function () {
+ const newId = GodotHTTPRequest.getUnusedRequestId();
+ GodotHTTPRequest.requests[newId] = new XMLHttpRequest();
GodotHTTPRequest.setupRequest(GodotHTTPRequest.requests[newId]);
return newId;
},
- godot_xhr_reset: function(xhrId) {
- GodotHTTPRequest.requests[xhrId] = new XMLHttpRequest;
+ godot_xhr_reset: function (xhrId) {
+ GodotHTTPRequest.requests[xhrId] = new XMLHttpRequest();
GodotHTTPRequest.setupRequest(GodotHTTPRequest.requests[xhrId]);
},
- godot_xhr_free: function(xhrId) {
+ godot_xhr_free: function (xhrId) {
GodotHTTPRequest.requests[xhrId].abort();
GodotHTTPRequest.requests[xhrId] = null;
},
- godot_xhr_open: function(xhrId, method, url, user, password) {
- user = user > 0 ? UTF8ToString(user) : null;
- password = password > 0 ? UTF8ToString(password) : null;
- GodotHTTPRequest.requests[xhrId].open(UTF8ToString(method), UTF8ToString(url), true, user, password);
+ 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: function(xhrId, header, value) {
- GodotHTTPRequest.requests[xhrId].setRequestHeader(UTF8ToString(header), UTF8ToString(value));
+ godot_xhr_set_request_header: function (xhrId, header, value) {
+ GodotHTTPRequest.requests[xhrId].setRequestHeader(GodotRuntime.parseString(header), GodotRuntime.parseString(value));
},
- godot_xhr_send_null: function(xhrId) {
+ godot_xhr_send_null: function (xhrId) {
GodotHTTPRequest.requests[xhrId].send();
},
- godot_xhr_send_string: function(xhrId, strPtr) {
+ godot_xhr_send_string: function (xhrId, strPtr) {
if (!strPtr) {
- err("Failed to send string per XHR: null pointer");
+ GodotRuntime.error('Failed to send string per XHR: null pointer');
return;
}
- GodotHTTPRequest.requests[xhrId].send(UTF8ToString(strPtr));
+ GodotHTTPRequest.requests[xhrId].send(GodotRuntime.parseString(strPtr));
},
- godot_xhr_send_data: function(xhrId, ptr, len) {
+ godot_xhr_send_data: function (xhrId, ptr, len) {
if (!ptr) {
- err("Failed to send data per XHR: null pointer");
+ GodotRuntime.error('Failed to send data per XHR: null pointer');
return;
}
if (len < 0) {
- err("Failed to send data per XHR: buffer length less than 0");
+ GodotRuntime.error('Failed to send data per XHR: buffer length less than 0');
return;
}
GodotHTTPRequest.requests[xhrId].send(HEAPU8.subarray(ptr, ptr + len));
},
- godot_xhr_abort: function(xhrId) {
+ godot_xhr_abort: function (xhrId) {
GodotHTTPRequest.requests[xhrId].abort();
},
- godot_xhr_get_status: function(xhrId) {
+ godot_xhr_get_status: function (xhrId) {
return GodotHTTPRequest.requests[xhrId].status;
},
- godot_xhr_get_ready_state: function(xhrId) {
+ godot_xhr_get_ready_state: function (xhrId) {
return GodotHTTPRequest.requests[xhrId].readyState;
},
- godot_xhr_get_response_headers_length: function(xhrId) {
- var headers = GodotHTTPRequest.requests[xhrId].getAllResponseHeaders();
- return headers === null ? 0 : lengthBytesUTF8(headers);
+ 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: function(xhrId, dst, len) {
- var str = GodotHTTPRequest.requests[xhrId].getAllResponseHeaders();
- if (str === null)
+ godot_xhr_get_response_headers: function (xhrId, dst, len) {
+ const str = GodotHTTPRequest.requests[xhrId].getAllResponseHeaders();
+ if (str === null) {
return;
- var buf = new Uint8Array(len + 1);
- stringToUTF8Array(str, buf, 0, buf.length);
- buf = buf.subarray(0, -1);
- HEAPU8.set(buf, dst);
+ }
+ GodotRuntime.stringToHeap(str, dst, len);
},
- godot_xhr_get_response_length: function(xhrId) {
- var body = GodotHTTPRequest.requests[xhrId].response;
+ godot_xhr_get_response_length: function (xhrId) {
+ const body = GodotHTTPRequest.requests[xhrId].response;
return body === null ? 0 : body.byteLength;
},
- godot_xhr_get_response: function(xhrId, dst, len) {
- var buf = GodotHTTPRequest.requests[xhrId].response;
- if (buf === null)
+ 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");
+autoAddDeps(GodotHTTPRequest, '$GodotHTTPRequest');
mergeInto(LibraryManager.library, GodotHTTPRequest);
diff --git a/platform/javascript/native/library_godot_os.js b/platform/javascript/js/libs/library_godot_os.js
index ed48280674..488753d704 100644
--- a/platform/javascript/native/library_godot_os.js
+++ b/platform/javascript/js/libs/library_godot_os.js
@@ -33,53 +33,57 @@ const IDHandler = {
_last_id: 0,
_references: {},
- get: function(p_id) {
+ get: function (p_id) {
return IDHandler._references[p_id];
},
- add: function(p_data) {
+ add: function (p_data) {
const id = ++IDHandler._last_id;
IDHandler._references[id] = p_data;
return id;
},
- remove: function(p_id) {
+ remove: function (p_id) {
delete IDHandler._references[p_id];
},
},
};
-autoAddDeps(IDHandler, "$IDHandler");
+autoAddDeps(IDHandler, '$IDHandler');
mergeInto(LibraryManager.library, IDHandler);
const GodotConfig = {
-
$GodotConfig__postset: 'Module["initConfig"] = GodotConfig.init_config;',
+ $GodotConfig__deps: ['$GodotRuntime'],
$GodotConfig: {
canvas: null,
- locale: "en",
+ locale: 'en',
resize_on_start: false,
on_execute: null,
- init_config: function(p_opts) {
- GodotConfig.resize_on_start = p_opts['resizeCanvasOnStart'] ? true : false;
+ init_config: function (p_opts) {
+ GodotConfig.resize_on_start = !!p_opts['resizeCanvasOnStart'];
GodotConfig.canvas = p_opts['canvas'];
GodotConfig.locale = p_opts['locale'] || GodotConfig.locale;
GodotConfig.on_execute = p_opts['onExecute'];
// This is called by emscripten, even if undocumented.
- Module['onExit'] = p_opts['onExit'];
+ Module['onExit'] = p_opts['onExit']; // eslint-disable-line no-undef
+ },
+
+ locate_file: function (file) {
+ return Module['locateFile'](file); // eslint-disable-line no-undef
},
},
- godot_js_config_canvas_id_get: function(p_ptr, p_ptr_max) {
- stringToUTF8('#' + GodotConfig.canvas.id, p_ptr, p_ptr_max);
+ godot_js_config_canvas_id_get: function (p_ptr, p_ptr_max) {
+ GodotRuntime.stringToHeap(`#${GodotConfig.canvas.id}`, p_ptr, p_ptr_max);
},
- godot_js_config_locale_get: function(p_ptr, p_ptr_max) {
- stringToUTF8(GodotConfig.locale, p_ptr, p_ptr_max);
+ godot_js_config_locale_get: function (p_ptr, p_ptr_max) {
+ GodotRuntime.stringToHeap(GodotConfig.locale, p_ptr, p_ptr_max);
},
- godot_js_config_is_resize_on_start: function() {
+ godot_js_config_is_resize_on_start: function () {
return GodotConfig.resize_on_start ? 1 : 0;
},
};
@@ -88,7 +92,7 @@ autoAddDeps(GodotConfig, '$GodotConfig');
mergeInto(LibraryManager.library, GodotConfig);
const GodotFS = {
- $GodotFS__deps: ['$FS', '$IDBFS'],
+ $GodotFS__deps: ['$FS', '$IDBFS', '$GodotRuntime'],
$GodotFS__postset: [
'Module["initFS"] = GodotFS.init;',
'Module["deinitFS"] = GodotFS.deinit;',
@@ -99,7 +103,7 @@ const GodotFS = {
_syncing: false,
_mount_points: [],
- is_persistent: function() {
+ is_persistent: function () {
return GodotFS._idbfs ? 1 : 0;
},
@@ -107,7 +111,7 @@ const GodotFS = {
// Returns a promise that resolves when the FS is ready.
// We keep track of mount_points, so that we can properly close the IDBFS
// since emscripten is not doing it by itself. (emscripten GH#12516).
- init: function(persistentPaths) {
+ init: function (persistentPaths) {
GodotFS._idbfs = false;
if (!Array.isArray(persistentPaths)) {
return Promise.reject(new Error('Persistent paths must be an array'));
@@ -128,16 +132,16 @@ const GodotFS = {
}
}
- GodotFS._mount_points.forEach(function(path) {
+ GodotFS._mount_points.forEach(function (path) {
createRecursive(path);
FS.mount(IDBFS, {}, path);
});
- return new Promise(function(resolve, reject) {
- FS.syncfs(true, function(err) {
+ return new Promise(function (resolve, reject) {
+ FS.syncfs(true, function (err) {
if (err) {
GodotFS._mount_points = [];
GodotFS._idbfs = false;
- console.log("IndexedDB not available: " + err.message);
+ GodotRuntime.print(`IndexedDB not available: ${err.message}`);
} else {
GodotFS._idbfs = true;
}
@@ -147,12 +151,12 @@ const GodotFS = {
},
// Deinit godot file system, making sure to unmount file systems, and close IDBFS(s).
- deinit: function() {
- GodotFS._mount_points.forEach(function(path) {
+ deinit: function () {
+ GodotFS._mount_points.forEach(function (path) {
try {
FS.unmount(path);
} catch (e) {
- console.log("Already unmounted", e);
+ GodotRuntime.print('Already unmounted', e);
}
if (GodotFS._idbfs && IDBFS.dbs[path]) {
IDBFS.dbs[path].close();
@@ -164,16 +168,16 @@ const GodotFS = {
GodotFS._syncing = false;
},
- sync: function() {
+ sync: function () {
if (GodotFS._syncing) {
- err('Already syncing!');
+ GodotRuntime.error('Already syncing!');
return Promise.resolve();
}
GodotFS._syncing = true;
return new Promise(function (resolve, reject) {
- FS.syncfs(false, function(error) {
+ FS.syncfs(false, function (error) {
if (error) {
- err('Failed to save IDB file system: ' + error.message);
+ GodotRuntime.error(`Failed to save IDB file system: ${error.message}`);
}
GodotFS._syncing = false;
resolve(error);
@@ -182,9 +186,9 @@ const GodotFS = {
},
// Copies a buffer to the internal file system. Creating directories recursively.
- copy_to_fs: function(path, buffer) {
- const idx = path.lastIndexOf("/");
- let dir = "/";
+ copy_to_fs: function (path, buffer) {
+ const idx = path.lastIndexOf('/');
+ let dir = '/';
if (idx > 0) {
dir = path.slice(0, idx);
}
@@ -196,106 +200,68 @@ const GodotFS = {
}
FS.mkdirTree(dir);
}
- FS.writeFile(path, new Uint8Array(buffer), {'flags': 'wx+'});
+ FS.writeFile(path, new Uint8Array(buffer), { 'flags': 'wx+' });
},
},
};
mergeInto(LibraryManager.library, GodotFS);
const GodotOS = {
- $GodotOS__deps: ['$GodotFS'],
+ $GodotOS__deps: ['$GodotFS', '$GodotRuntime'],
$GodotOS__postset: [
'Module["request_quit"] = function() { GodotOS.request_quit() };',
'GodotOS._fs_sync_promise = Promise.resolve();',
].join(''),
$GodotOS: {
-
- request_quit: function() {},
+ request_quit: function () {},
_async_cbs: [],
_fs_sync_promise: null,
- get_func: function(ptr) {
- return wasmTable.get(ptr);
- },
-
- atexit: function(p_promise_cb) {
+ atexit: function (p_promise_cb) {
GodotOS._async_cbs.push(p_promise_cb);
},
- finish_async: function(callback) {
- GodotOS._fs_sync_promise.then(function(err) {
+ finish_async: function (callback) {
+ GodotOS._fs_sync_promise.then(function (err) {
const promises = [];
- GodotOS._async_cbs.forEach(function(cb) {
+ GodotOS._async_cbs.forEach(function (cb) {
promises.push(new Promise(cb));
});
return Promise.all(promises);
- }).then(function() {
+ }).then(function () {
return GodotFS.sync(); // Final FS sync.
- }).then(function(err) {
+ }).then(function (err) {
// Always deferred.
- setTimeout(function() {
+ setTimeout(function () {
callback();
}, 0);
});
},
-
- allocString: function(p_str) {
- const length = lengthBytesUTF8(p_str)+1;
- const c_str = _malloc(length);
- stringToUTF8(p_str, c_str, length);
- return c_str;
- },
-
- allocStringArray: function(strings) {
- const size = strings.length;
- const c_ptr = _malloc(size * 4);
- for (let i = 0; i < size; i++) {
- HEAP32[(c_ptr >> 2) + i] = GodotOS.allocString(strings[i]);
- }
- return c_ptr;
- },
-
- freeStringArray: function(c_ptr, size) {
- for (let i = 0; i < size; i++) {
- _free(HEAP32[(c_ptr >> 2) + i]);
- }
- _free(c_ptr);
- },
-
- heapSub: function(heap, ptr, size) {
- const bytes = heap.BYTES_PER_ELEMENT;
- return heap.subarray(ptr / bytes, ptr / bytes + size);
- },
-
- heapCopy: function(heap, ptr, size) {
- const bytes = heap.BYTES_PER_ELEMENT;
- return heap.slice(ptr / bytes, ptr / bytes + size);
- },
},
- godot_js_os_finish_async: function(p_callback) {
- const func = GodotOS.get_func(p_callback);
+ godot_js_os_finish_async: function (p_callback) {
+ const func = GodotRuntime.get_func(p_callback);
GodotOS.finish_async(func);
},
- godot_js_os_request_quit_cb: function(p_callback) {
- GodotOS.request_quit = GodotOS.get_func(p_callback);
+ godot_js_os_request_quit_cb: function (p_callback) {
+ GodotOS.request_quit = GodotRuntime.get_func(p_callback);
},
- godot_js_os_fs_is_persistent: function() {
+ godot_js_os_fs_is_persistent: function () {
return GodotFS.is_persistent();
},
- godot_js_os_fs_sync: function(callback) {
- const func = GodotOS.get_func(callback);
+ godot_js_os_fs_sync: function (callback) {
+ const func = GodotRuntime.get_func(callback);
GodotOS._fs_sync_promise = GodotFS.sync();
- GodotOS._fs_sync_promise.then(function(err) {
+ GodotOS._fs_sync_promise.then(function (err) {
func();
});
},
- godot_js_os_execute: function(p_json) {
- const json_args = UTF8ToString(p_json);
+ godot_js_os_execute: function (p_json) {
+ const json_args = GodotRuntime.parseString(p_json);
const args = JSON.parse(json_args);
if (GodotConfig.on_execute) {
GodotConfig.on_execute(args);
@@ -304,8 +270,8 @@ const GodotOS = {
return 1;
},
- godot_js_os_shell_open: function(p_uri) {
- window.open(UTF8ToString(p_uri), '_blank');
+ godot_js_os_shell_open: function (p_uri) {
+ window.open(GodotRuntime.parseString(p_uri), '_blank');
},
};
diff --git a/platform/javascript/js/libs/library_godot_runtime.js b/platform/javascript/js/libs/library_godot_runtime.js
new file mode 100644
index 0000000000..04f29ad681
--- /dev/null
+++ b/platform/javascript/js/libs/library_godot_runtime.js
@@ -0,0 +1,120 @@
+/*************************************************************************/
+/* library_godot_runtime.js */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+const GodotRuntime = {
+ $GodotRuntime: {
+ /*
+ * Functions
+ */
+ get_func: function (ptr) {
+ return wasmTable.get(ptr); // eslint-disable-line no-undef
+ },
+
+ /*
+ * Prints
+ */
+ error: function () {
+ err.apply(null, Array.from(arguments)); // eslint-disable-line no-undef
+ },
+
+ print: function () {
+ out.apply(null, Array.from(arguments)); // eslint-disable-line no-undef
+ },
+
+ /*
+ * Memory
+ */
+ malloc: function (p_size) {
+ return _malloc(p_size); // eslint-disable-line no-undef
+ },
+
+ free: function (p_ptr) {
+ _free(p_ptr); // eslint-disable-line no-undef
+ },
+
+ getHeapValue: function (p_ptr, p_type) {
+ return getValue(p_ptr, p_type); // eslint-disable-line no-undef
+ },
+
+ setHeapValue: function (p_ptr, p_value, p_type) {
+ setValue(p_ptr, p_value, p_type); // eslint-disable-line no-undef
+ },
+
+ heapSub: function (p_heap, p_ptr, p_len) {
+ const bytes = p_heap.BYTES_PER_ELEMENT;
+ return p_heap.subarray(p_ptr / bytes, p_ptr / bytes + p_len);
+ },
+
+ heapCopy: 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);
+ },
+
+ /*
+ * Strings
+ */
+ parseString: function (p_ptr) {
+ return UTF8ToString(p_ptr); // eslint-disable-line no-undef
+ },
+
+ strlen: function (p_str) {
+ return lengthBytesUTF8(p_str); // eslint-disable-line no-undef
+ },
+
+ allocString: function (p_str) {
+ const length = GodotRuntime.strlen(p_str) + 1;
+ const c_str = GodotRuntime.malloc(length);
+ stringToUTF8(p_str, c_str, length); // eslint-disable-line no-undef
+ return c_str;
+ },
+
+ allocStringArray: function (p_strings) {
+ const size = p_strings.length;
+ const c_ptr = GodotRuntime.malloc(size * 4);
+ for (let i = 0; i < size; i++) {
+ HEAP32[(c_ptr >> 2) + i] = GodotRuntime.allocString(p_strings[i]);
+ }
+ return c_ptr;
+ },
+
+ freeStringArray: function (p_ptr, p_len) {
+ for (let i = 0; i < p_len; i++) {
+ GodotRuntime.free(HEAP32[(p_ptr >> 2) + i]);
+ }
+ GodotRuntime.free(p_ptr);
+ },
+
+ stringToHeap: function (p_str, p_ptr, p_len) {
+ return stringToUTF8Array(p_str, HEAP8, p_ptr, p_len); // eslint-disable-line no-undef
+ },
+ },
+};
+autoAddDeps(GodotRuntime, '$GodotRuntime');
+mergeInto(LibraryManager.library, GodotRuntime);
diff --git a/platform/javascript/package-lock.json b/platform/javascript/package-lock.json
new file mode 100644
index 0000000000..8e298a495e
--- /dev/null
+++ b/platform/javascript/package-lock.json
@@ -0,0 +1,1605 @@
+{
+ "name": "godot",
+ "version": "1.0.0",
+ "lockfileVersion": 1,
+ "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==",
+ "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==",
+ "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==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-validator-identifier": "^7.10.4",
+ "chalk": "^2.0.0",
+ "js-tokens": "^4.0.0"
+ },
+ "dependencies": {
+ "chalk": {
+ "version": "2.4.2",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
+ "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
+ "dev": true,
+ "requires": {
+ "ansi-styles": "^3.2.1",
+ "escape-string-regexp": "^1.0.5",
+ "supports-color": "^5.3.0"
+ }
+ }
+ }
+ },
+ "@eslint/eslintrc": {
+ "version": "0.1.3",
+ "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.1.3.tgz",
+ "integrity": "sha512-4YVwPkANLeNtRjMekzux1ci8hIaH5eGKktGqR0d3LWsKNn5B2X/1Z6Trxy7jQXl9EBGE6Yj02O+t09FMeRllaA==",
+ "dev": true,
+ "requires": {
+ "ajv": "^6.12.4",
+ "debug": "^4.1.1",
+ "espree": "^7.3.0",
+ "globals": "^12.1.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",
+ "integrity": "sha1-7ihweulOEdK4J7y+UnC86n8+ce4=",
+ "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==",
+ "dev": true
+ },
+ "acorn-jsx": {
+ "version": "5.3.1",
+ "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.1.tgz",
+ "integrity": "sha512-K0Ptm/47OKfQRpNQ2J/oIN/3QYiK6FwW+eJbILhsdxh2WTLdl+30o8aGdTbm5JbffpFFAg/g+zi1E+jvJha5ng==",
+ "dev": true
+ },
+ "ajv": {
+ "version": "6.12.5",
+ "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.5.tgz",
+ "integrity": "sha512-lRF8RORchjpKG50/WFf8xmg7sgCLFiYNNnqdKflk63whMQcWR5ngGjiSXkL9bjxy6B2npOK2HSMN49jEBMSkag==",
+ "dev": true,
+ "requires": {
+ "fast-deep-equal": "^3.1.1",
+ "fast-json-stable-stringify": "^2.0.0",
+ "json-schema-traverse": "^0.4.1",
+ "uri-js": "^4.2.2"
+ }
+ },
+ "ansi-colors": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz",
+ "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==",
+ "dev": true
+ },
+ "ansi-regex": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz",
+ "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==",
+ "dev": true
+ },
+ "ansi-styles": {
+ "version": "3.2.1",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
+ "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
+ "dev": true,
+ "requires": {
+ "color-convert": "^1.9.0"
+ }
+ },
+ "argparse": {
+ "version": "1.0.10",
+ "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz",
+ "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==",
+ "dev": true,
+ "requires": {
+ "sprintf-js": "~1.0.2"
+ }
+ },
+ "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==",
+ "dev": true,
+ "requires": {
+ "define-properties": "^1.1.3",
+ "es-abstract": "^1.17.0",
+ "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==",
+ "dev": true,
+ "requires": {
+ "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"
+ }
+ }
+ }
+ },
+ "astral-regex": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-1.0.0.tgz",
+ "integrity": "sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg==",
+ "dev": true
+ },
+ "balanced-match": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz",
+ "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=",
+ "dev": true
+ },
+ "brace-expansion": {
+ "version": "1.1.11",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
+ "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
+ "dev": true,
+ "requires": {
+ "balanced-match": "^1.0.0",
+ "concat-map": "0.0.1"
+ }
+ },
+ "callsites": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
+ "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==",
+ "dev": true
+ },
+ "chalk": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz",
+ "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==",
+ "dev": true,
+ "requires": {
+ "ansi-styles": "^4.1.0",
+ "supports-color": "^7.1.0"
+ },
+ "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==",
+ "dev": true,
+ "requires": {
+ "@types/color-name": "^1.1.1",
+ "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
+ },
+ "has-flag": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+ "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+ "dev": true
+ },
+ "supports-color": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+ "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+ "dev": true,
+ "requires": {
+ "has-flag": "^4.0.0"
+ }
+ }
+ }
+ },
+ "color-convert": {
+ "version": "1.9.3",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
+ "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
+ "dev": true,
+ "requires": {
+ "color-name": "1.1.3"
+ }
+ },
+ "color-name": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
+ "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=",
+ "dev": true
+ },
+ "concat-map": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
+ "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=",
+ "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=",
+ "dev": true
+ },
+ "cross-spawn": {
+ "version": "7.0.3",
+ "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz",
+ "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==",
+ "dev": true,
+ "requires": {
+ "path-key": "^3.1.0",
+ "shebang-command": "^2.0.0",
+ "which": "^2.0.1"
+ }
+ },
+ "debug": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
+ "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
+ "dev": true,
+ "requires": {
+ "ms": "^2.1.1"
+ }
+ },
+ "deep-is": {
+ "version": "0.1.3",
+ "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz",
+ "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=",
+ "dev": true
+ },
+ "define-properties": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz",
+ "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==",
+ "dev": true,
+ "requires": {
+ "object-keys": "^1.0.12"
+ }
+ },
+ "doctrine": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz",
+ "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==",
+ "dev": true,
+ "requires": {
+ "esutils": "^2.0.2"
+ }
+ },
+ "emoji-regex": {
+ "version": "7.0.3",
+ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz",
+ "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==",
+ "dev": true
+ },
+ "enquirer": {
+ "version": "2.3.6",
+ "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz",
+ "integrity": "sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==",
+ "dev": true,
+ "requires": {
+ "ansi-colors": "^4.1.1"
+ }
+ },
+ "error-ex": {
+ "version": "1.3.2",
+ "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz",
+ "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==",
+ "dev": true,
+ "requires": {
+ "is-arrayish": "^0.2.1"
+ }
+ },
+ "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==",
+ "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-negative-zero": "^2.0.0",
+ "is-regex": "^1.1.1",
+ "object-inspect": "^1.8.0",
+ "object-keys": "^1.1.1",
+ "object.assign": "^4.1.0",
+ "string.prototype.trimend": "^1.0.1",
+ "string.prototype.trimstart": "^1.0.1"
+ }
+ },
+ "es-to-primitive": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz",
+ "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==",
+ "dev": true,
+ "requires": {
+ "is-callable": "^1.1.4",
+ "is-date-object": "^1.0.1",
+ "is-symbol": "^1.0.2"
+ }
+ },
+ "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
+ },
+ "eslint": {
+ "version": "7.9.0",
+ "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.9.0.tgz",
+ "integrity": "sha512-V6QyhX21+uXp4T+3nrNfI3hQNBDa/P8ga7LoQOenwrlEFXrEnUEE+ok1dMtaS3b6rmLXhT1TkTIsG75HMLbknA==",
+ "dev": true,
+ "requires": {
+ "@babel/code-frame": "^7.0.0",
+ "@eslint/eslintrc": "^0.1.3",
+ "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",
+ "eslint-utils": "^2.1.0",
+ "eslint-visitor-keys": "^1.3.0",
+ "espree": "^7.3.0",
+ "esquery": "^1.2.0",
+ "esutils": "^2.0.2",
+ "file-entry-cache": "^5.0.1",
+ "functional-red-black-tree": "^1.0.1",
+ "glob-parent": "^5.0.0",
+ "globals": "^12.1.0",
+ "ignore": "^4.0.6",
+ "import-fresh": "^3.0.0",
+ "imurmurhash": "^0.1.4",
+ "is-glob": "^4.0.0",
+ "js-yaml": "^3.13.1",
+ "json-stable-stringify-without-jsonify": "^1.0.1",
+ "levn": "^0.4.1",
+ "lodash": "^4.17.19",
+ "minimatch": "^3.0.4",
+ "natural-compare": "^1.4.0",
+ "optionator": "^0.9.1",
+ "progress": "^2.0.0",
+ "regexpp": "^3.1.0",
+ "semver": "^7.2.1",
+ "strip-ansi": "^6.0.0",
+ "strip-json-comments": "^3.1.0",
+ "table": "^5.2.3",
+ "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==",
+ "dev": true,
+ "requires": {
+ "confusing-browser-globals": "^1.0.9",
+ "object.assign": "^4.1.0",
+ "object.entries": "^1.1.2"
+ }
+ },
+ "eslint-import-resolver-node": {
+ "version": "0.3.4",
+ "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.4.tgz",
+ "integrity": "sha512-ogtf+5AB/O+nM6DIeBUNr2fuT7ot9Qg/1harBfBtaP13ekEWFQEEMP94BCB7zaNW3gyY+8SHYF00rnqYwXKWOA==",
+ "dev": true,
+ "requires": {
+ "debug": "^2.6.9",
+ "resolve": "^1.13.1"
+ },
+ "dependencies": {
+ "debug": {
+ "version": "2.6.9",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+ "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+ "dev": true,
+ "requires": {
+ "ms": "2.0.0"
+ }
+ },
+ "ms": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+ "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
+ "dev": true
+ }
+ }
+ },
+ "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==",
+ "dev": true,
+ "requires": {
+ "debug": "^2.6.9",
+ "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==",
+ "dev": true,
+ "requires": {
+ "ms": "2.0.0"
+ }
+ },
+ "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==",
+ "dev": true,
+ "requires": {
+ "array-includes": "^3.1.1",
+ "array.prototype.flat": "^1.2.3",
+ "contains-path": "^0.1.0",
+ "debug": "^2.6.9",
+ "doctrine": "1.5.0",
+ "eslint-import-resolver-node": "^0.3.3",
+ "eslint-module-utils": "^2.6.0",
+ "has": "^1.0.3",
+ "minimatch": "^3.0.4",
+ "object.values": "^1.1.1",
+ "read-pkg-up": "^2.0.0",
+ "resolve": "^1.17.0",
+ "tsconfig-paths": "^3.9.0"
+ },
+ "dependencies": {
+ "debug": {
+ "version": "2.6.9",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+ "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+ "dev": true,
+ "requires": {
+ "ms": "2.0.0"
+ }
+ },
+ "doctrine": {
+ "version": "1.5.0",
+ "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-1.5.0.tgz",
+ "integrity": "sha1-N53Ocw9hZvds76TmcHoVmwLFpvo=",
+ "dev": true,
+ "requires": {
+ "esutils": "^2.0.2",
+ "isarray": "^1.0.0"
+ }
+ },
+ "ms": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+ "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
+ "dev": true
+ }
+ }
+ },
+ "eslint-scope": {
+ "version": "5.1.1",
+ "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz",
+ "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==",
+ "dev": true,
+ "requires": {
+ "esrecurse": "^4.3.0",
+ "estraverse": "^4.1.1"
+ }
+ },
+ "eslint-utils": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.1.0.tgz",
+ "integrity": "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==",
+ "dev": true,
+ "requires": {
+ "eslint-visitor-keys": "^1.1.0"
+ }
+ },
+ "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
+ },
+ "espree": {
+ "version": "7.3.0",
+ "resolved": "https://registry.npmjs.org/espree/-/espree-7.3.0.tgz",
+ "integrity": "sha512-dksIWsvKCixn1yrEXO8UosNSxaDoSYpq9reEjZSbHLpT5hpaCAKTLBwq0RHtLrIr+c0ByiYzWT8KTMRzoRCNlw==",
+ "dev": true,
+ "requires": {
+ "acorn": "^7.4.0",
+ "acorn-jsx": "^5.2.0",
+ "eslint-visitor-keys": "^1.3.0"
+ }
+ },
+ "esprima": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz",
+ "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==",
+ "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==",
+ "dev": true,
+ "requires": {
+ "estraverse": "^5.1.0"
+ },
+ "dependencies": {
+ "estraverse": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz",
+ "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==",
+ "dev": true
+ }
+ }
+ },
+ "esrecurse": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz",
+ "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==",
+ "dev": true,
+ "requires": {
+ "estraverse": "^5.2.0"
+ },
+ "dependencies": {
+ "estraverse": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz",
+ "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==",
+ "dev": true
+ }
+ }
+ },
+ "estraverse": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz",
+ "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==",
+ "dev": true
+ },
+ "esutils": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz",
+ "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==",
+ "dev": true
+ },
+ "fast-deep-equal": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
+ "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
+ "dev": true
+ },
+ "fast-json-stable-stringify": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz",
+ "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==",
+ "dev": true
+ },
+ "fast-levenshtein": {
+ "version": "2.0.6",
+ "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz",
+ "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=",
+ "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==",
+ "dev": true,
+ "requires": {
+ "flat-cache": "^2.0.1"
+ }
+ },
+ "find-up": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz",
+ "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=",
+ "dev": true,
+ "requires": {
+ "locate-path": "^2.0.0"
+ }
+ },
+ "flat-cache": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-2.0.1.tgz",
+ "integrity": "sha512-LoQe6yDuUMDzQAEH8sgmh4Md6oZnc/7PjtwjNFSzveXqSHt6ka9fPBuso7IGf9Rz4uqnSnWiFH2B/zj24a5ReA==",
+ "dev": true,
+ "requires": {
+ "flatted": "^2.0.0",
+ "rimraf": "2.6.3",
+ "write": "1.0.3"
+ }
+ },
+ "flatted": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/flatted/-/flatted-2.0.2.tgz",
+ "integrity": "sha512-r5wGx7YeOwNWNlCA0wQ86zKyDLMQr+/RB8xy74M4hTphfmjlijTSSXGuH8rnvKZnfT9i+75zmd8jcKdMR4O6jA==",
+ "dev": true
+ },
+ "fs.realpath": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
+ "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=",
+ "dev": true
+ },
+ "function-bind": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
+ "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==",
+ "dev": true
+ },
+ "functional-red-black-tree": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz",
+ "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=",
+ "dev": true
+ },
+ "glob": {
+ "version": "7.1.6",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz",
+ "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==",
+ "dev": true,
+ "requires": {
+ "fs.realpath": "^1.0.0",
+ "inflight": "^1.0.4",
+ "inherits": "2",
+ "minimatch": "^3.0.4",
+ "once": "^1.3.0",
+ "path-is-absolute": "^1.0.0"
+ }
+ },
+ "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==",
+ "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==",
+ "dev": true,
+ "requires": {
+ "type-fest": "^0.8.1"
+ }
+ },
+ "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==",
+ "dev": true
+ },
+ "has": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
+ "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
+ "dev": true,
+ "requires": {
+ "function-bind": "^1.1.1"
+ }
+ },
+ "has-flag": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
+ "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=",
+ "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==",
+ "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==",
+ "dev": true
+ },
+ "ignore": {
+ "version": "4.0.6",
+ "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz",
+ "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==",
+ "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==",
+ "dev": true,
+ "requires": {
+ "parent-module": "^1.0.0",
+ "resolve-from": "^4.0.0"
+ }
+ },
+ "imurmurhash": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz",
+ "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=",
+ "dev": true
+ },
+ "inflight": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
+ "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=",
+ "dev": true,
+ "requires": {
+ "once": "^1.3.0",
+ "wrappy": "1"
+ }
+ },
+ "inherits": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
+ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
+ "dev": true
+ },
+ "is-arrayish": {
+ "version": "0.2.1",
+ "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz",
+ "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=",
+ "dev": true
+ },
+ "is-callable": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.1.tgz",
+ "integrity": "sha512-wliAfSzx6V+6WfMOmus1xy0XvSgf/dlStkvTfq7F0g4bOIW0PSUbnyse3NhDwdyYS1ozfUtAAySqTws3z9Eqgg==",
+ "dev": true
+ },
+ "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==",
+ "dev": true
+ },
+ "is-extglob": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
+ "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=",
+ "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=",
+ "dev": true
+ },
+ "is-glob": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz",
+ "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==",
+ "dev": true,
+ "requires": {
+ "is-extglob": "^2.1.1"
+ }
+ },
+ "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=",
+ "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==",
+ "dev": true,
+ "requires": {
+ "has-symbols": "^1.0.1"
+ }
+ },
+ "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==",
+ "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==",
+ "dev": true,
+ "requires": {
+ "has-symbols": "^1.0.1"
+ }
+ },
+ "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",
+ "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=",
+ "dev": true
+ },
+ "js-tokens": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
+ "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
+ "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==",
+ "dev": true,
+ "requires": {
+ "argparse": "^1.0.7",
+ "esprima": "^4.0.0"
+ }
+ },
+ "json-schema-traverse": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
+ "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
+ "dev": true
+ },
+ "json-stable-stringify-without-jsonify": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz",
+ "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=",
+ "dev": true
+ },
+ "json5": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz",
+ "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==",
+ "dev": true,
+ "requires": {
+ "minimist": "^1.2.0"
+ }
+ },
+ "levn": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz",
+ "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==",
+ "dev": true,
+ "requires": {
+ "prelude-ls": "^1.2.1",
+ "type-check": "~0.4.0"
+ }
+ },
+ "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=",
+ "dev": true,
+ "requires": {
+ "graceful-fs": "^4.1.2",
+ "parse-json": "^2.2.0",
+ "pify": "^2.0.0",
+ "strip-bom": "^3.0.0"
+ }
+ },
+ "locate-path": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz",
+ "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=",
+ "dev": true,
+ "requires": {
+ "p-locate": "^2.0.0",
+ "path-exists": "^3.0.0"
+ }
+ },
+ "lodash": {
+ "version": "4.17.20",
+ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz",
+ "integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==",
+ "dev": true
+ },
+ "minimatch": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
+ "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
+ "dev": true,
+ "requires": {
+ "brace-expansion": "^1.1.7"
+ }
+ },
+ "minimist": {
+ "version": "1.2.5",
+ "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz",
+ "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==",
+ "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"
+ }
+ },
+ "ms": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
+ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
+ "dev": true
+ },
+ "natural-compare": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz",
+ "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=",
+ "dev": true
+ },
+ "normalize-package-data": {
+ "version": "2.5.0",
+ "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz",
+ "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==",
+ "dev": true,
+ "requires": {
+ "hosted-git-info": "^2.1.4",
+ "resolve": "^1.10.0",
+ "semver": "2 || 3 || 4 || 5",
+ "validate-npm-package-license": "^3.0.1"
+ },
+ "dependencies": {
+ "semver": {
+ "version": "5.7.1",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
+ "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
+ "dev": true
+ }
+ }
+ },
+ "object-inspect": {
+ "version": "1.8.0",
+ "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.8.0.tgz",
+ "integrity": "sha512-jLdtEOB112fORuypAyl/50VRVIBIdVQOSUUGQHzJ4xBSbit81zRarz7GThkEFZy1RceYrWYcPcBFPQwHyAc1gA==",
+ "dev": true
+ },
+ "object-keys": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz",
+ "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==",
+ "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==",
+ "dev": true,
+ "requires": {
+ "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==",
+ "dev": true,
+ "requires": {
+ "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"
+ }
+ }
+ }
+ },
+ "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==",
+ "dev": true,
+ "requires": {
+ "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"
+ }
+ }
+ }
+ },
+ "once": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
+ "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
+ "dev": true,
+ "requires": {
+ "wrappy": "1"
+ }
+ },
+ "optionator": {
+ "version": "0.9.1",
+ "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz",
+ "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==",
+ "dev": true,
+ "requires": {
+ "deep-is": "^0.1.3",
+ "fast-levenshtein": "^2.0.6",
+ "levn": "^0.4.1",
+ "prelude-ls": "^1.2.1",
+ "type-check": "^0.4.0",
+ "word-wrap": "^1.2.3"
+ }
+ },
+ "p-limit": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz",
+ "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==",
+ "dev": true,
+ "requires": {
+ "p-try": "^1.0.0"
+ }
+ },
+ "p-locate": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz",
+ "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=",
+ "dev": true,
+ "requires": {
+ "p-limit": "^1.1.0"
+ }
+ },
+ "p-try": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz",
+ "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=",
+ "dev": true
+ },
+ "parent-module": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
+ "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==",
+ "dev": true,
+ "requires": {
+ "callsites": "^3.0.0"
+ }
+ },
+ "parse-json": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz",
+ "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=",
+ "dev": true,
+ "requires": {
+ "error-ex": "^1.2.0"
+ }
+ },
+ "path-exists": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz",
+ "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=",
+ "dev": true
+ },
+ "path-is-absolute": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
+ "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=",
+ "dev": true
+ },
+ "path-key": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
+ "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==",
+ "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==",
+ "dev": true
+ },
+ "path-type": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/path-type/-/path-type-2.0.0.tgz",
+ "integrity": "sha1-8BLMuEFbcJb8LaoQVMPXI4lZTHM=",
+ "dev": true,
+ "requires": {
+ "pify": "^2.0.0"
+ }
+ },
+ "pify": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
+ "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=",
+ "dev": true
+ },
+ "pkg-dir": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-2.0.0.tgz",
+ "integrity": "sha1-9tXREJ4Z1j7fQo4L1X4Sd3YVM0s=",
+ "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",
+ "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==",
+ "dev": true
+ },
+ "progress": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz",
+ "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==",
+ "dev": true
+ },
+ "punycode": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz",
+ "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==",
+ "dev": true
+ },
+ "read-pkg": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-2.0.0.tgz",
+ "integrity": "sha1-jvHAYjxqbbDcZxPEv6xGMysjaPg=",
+ "dev": true,
+ "requires": {
+ "load-json-file": "^2.0.0",
+ "normalize-package-data": "^2.3.2",
+ "path-type": "^2.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=",
+ "dev": true,
+ "requires": {
+ "find-up": "^2.0.0",
+ "read-pkg": "^2.0.0"
+ }
+ },
+ "regexpp": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.1.0.tgz",
+ "integrity": "sha512-ZOIzd8yVsQQA7j8GCSlPGXwg5PfmA1mrq0JP4nGhh54LaKN3xdai/vHUDu74pKwV8OxseMS65u2NImosQcSD0Q==",
+ "dev": true
+ },
+ "resolve": {
+ "version": "1.17.0",
+ "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.17.0.tgz",
+ "integrity": "sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w==",
+ "dev": true,
+ "requires": {
+ "path-parse": "^1.0.6"
+ }
+ },
+ "resolve-from": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",
+ "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==",
+ "dev": true
+ },
+ "rimraf": {
+ "version": "2.6.3",
+ "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz",
+ "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==",
+ "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
+ },
+ "shebang-command": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
+ "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
+ "dev": true,
+ "requires": {
+ "shebang-regex": "^3.0.0"
+ }
+ },
+ "shebang-regex": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz",
+ "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
+ "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==",
+ "dev": true,
+ "requires": {
+ "ansi-styles": "^3.2.0",
+ "astral-regex": "^1.0.0",
+ "is-fullwidth-code-point": "^2.0.0"
+ }
+ },
+ "spdx-correct": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.1.tgz",
+ "integrity": "sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w==",
+ "dev": true,
+ "requires": {
+ "spdx-expression-parse": "^3.0.0",
+ "spdx-license-ids": "^3.0.0"
+ }
+ },
+ "spdx-exceptions": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz",
+ "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==",
+ "dev": true
+ },
+ "spdx-expression-parse": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz",
+ "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==",
+ "dev": true,
+ "requires": {
+ "spdx-exceptions": "^2.1.0",
+ "spdx-license-ids": "^3.0.0"
+ }
+ },
+ "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==",
+ "dev": true
+ },
+ "sprintf-js": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
+ "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=",
+ "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==",
+ "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"
+ }
+ }
+ }
+ },
+ "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==",
+ "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"
+ }
+ }
+ }
+ },
+ "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==",
+ "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"
+ }
+ }
+ }
+ },
+ "strip-ansi": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz",
+ "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==",
+ "dev": true,
+ "requires": {
+ "ansi-regex": "^5.0.0"
+ }
+ },
+ "strip-bom": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz",
+ "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=",
+ "dev": true
+ },
+ "strip-json-comments": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz",
+ "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==",
+ "dev": true
+ },
+ "supports-color": {
+ "version": "5.5.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
+ "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
+ "dev": true,
+ "requires": {
+ "has-flag": "^3.0.0"
+ }
+ },
+ "table": {
+ "version": "5.4.6",
+ "resolved": "https://registry.npmjs.org/table/-/table-5.4.6.tgz",
+ "integrity": "sha512-wmEc8m4fjnob4gt5riFRtTu/6+4rSe12TpAELNSqHMfF3IqnA+CH37USM6/YR3qRZv7e56kAEAtd6nKZaxe0Ug==",
+ "dev": true,
+ "requires": {
+ "ajv": "^6.10.2",
+ "lodash": "^4.17.14",
+ "slice-ansi": "^2.1.0",
+ "string-width": "^3.0.0"
+ }
+ },
+ "text-table": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz",
+ "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=",
+ "dev": true
+ },
+ "tsconfig-paths": {
+ "version": "3.9.0",
+ "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.9.0.tgz",
+ "integrity": "sha512-dRcuzokWhajtZWkQsDVKbWyY+jgcLC5sqJhg2PSgf4ZkH2aHPvaOY8YWGhmjb68b5qqTfasSsDO9k7RUiEmZAw==",
+ "dev": true,
+ "requires": {
+ "@types/json5": "^0.0.29",
+ "json5": "^1.0.1",
+ "minimist": "^1.2.0",
+ "strip-bom": "^3.0.0"
+ }
+ },
+ "type-check": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz",
+ "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==",
+ "dev": true,
+ "requires": {
+ "prelude-ls": "^1.2.1"
+ }
+ },
+ "type-fest": {
+ "version": "0.8.1",
+ "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz",
+ "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==",
+ "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==",
+ "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==",
+ "dev": true
+ },
+ "validate-npm-package-license": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz",
+ "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==",
+ "dev": true,
+ "requires": {
+ "spdx-correct": "^3.0.0",
+ "spdx-expression-parse": "^3.0.0"
+ }
+ },
+ "which": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
+ "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
+ "dev": true,
+ "requires": {
+ "isexe": "^2.0.0"
+ }
+ },
+ "word-wrap": {
+ "version": "1.2.3",
+ "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz",
+ "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==",
+ "dev": true
+ },
+ "wrappy": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
+ "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"
+ }
+ }
+ }
+}
diff --git a/platform/javascript/package.json b/platform/javascript/package.json
new file mode 100644
index 0000000000..630b584f5b
--- /dev/null
+++ b/platform/javascript/package.json
@@ -0,0 +1,24 @@
+{
+ "name": "godot",
+ "private": true,
+ "version": "1.0.0",
+ "description": "Linting setup for Godot's HTML5 platform code",
+ "scripts": {
+ "test": "echo \"Error: no test specified\" && exit 1",
+ "lint": "npm run lint:engine && npm run lint:libs && npm run lint:modules",
+ "lint:engine": "eslint \"js/engine/*.js\" --no-eslintrc -c .eslintrc.engine.js",
+ "lint:libs": "eslint \"js/libs/*.js\" --no-eslintrc -c .eslintrc.libs.js",
+ "lint:modules": "eslint \"../../modules/**/*.js\" --no-eslintrc -c .eslintrc.libs.js",
+ "format": "npm run format:engine && npm run format:libs && npm run format:modules",
+ "format:engine": "npm run lint:engine -- --fix",
+ "format:libs": "npm run lint:libs -- --fix",
+ "format:modules": "npm run lint:modules -- --fix"
+ },
+ "author": "Godot Engine contributors",
+ "license": "MIT",
+ "devDependencies": {
+ "eslint": "^7.9.0",
+ "eslint-config-airbnb-base": "^14.2.0",
+ "eslint-plugin-import": "^2.22.0"
+ }
+}
diff --git a/platform/linuxbsd/detect.py b/platform/linuxbsd/detect.py
index ea101e24dd..a8bc3a09f6 100644
--- a/platform/linuxbsd/detect.py
+++ b/platform/linuxbsd/detect.py
@@ -129,8 +129,6 @@ def configure(env):
if "clang++" not in os.path.basename(env["CXX"]):
env["CC"] = "clang"
env["CXX"] = "clang++"
- env["LINK"] = "clang++"
- env.Append(CPPDEFINES=["TYPED_METHOD_BIND"])
env.extra_suffix = ".llvm" + env.extra_suffix
if env["use_lld"]:
diff --git a/platform/linuxbsd/display_server_x11.cpp b/platform/linuxbsd/display_server_x11.cpp
index e04dde7969..74c69c0823 100644
--- a/platform/linuxbsd/display_server_x11.cpp
+++ b/platform/linuxbsd/display_server_x11.cpp
@@ -552,6 +552,60 @@ String DisplayServerX11::clipboard_get() const {
return ret;
}
+Bool DisplayServerX11::_predicate_clipboard_save_targets(Display *display, XEvent *event, XPointer arg) {
+ if (event->xany.window == *(Window *)arg) {
+ return (event->type == SelectionRequest) ||
+ (event->type == SelectionNotify);
+ } else {
+ return False;
+ }
+}
+
+void DisplayServerX11::_clipboard_transfer_ownership(Atom p_source, Window x11_window) const {
+ _THREAD_SAFE_METHOD_
+
+ Window selection_owner = XGetSelectionOwner(x11_display, p_source);
+
+ if (selection_owner != x11_window) {
+ return;
+ }
+
+ // Block events polling while processing selection events.
+ MutexLock mutex_lock(events_mutex);
+
+ Atom clipboard_manager = XInternAtom(x11_display, "CLIPBOARD_MANAGER", False);
+ Atom save_targets = XInternAtom(x11_display, "SAVE_TARGETS", False);
+ XConvertSelection(x11_display, clipboard_manager, save_targets, None,
+ x11_window, CurrentTime);
+
+ // Process events from the queue.
+ while (true) {
+ if (!_wait_for_events()) {
+ // Error or timeout, abort.
+ break;
+ }
+
+ // Non-blocking wait for next event and remove it from the queue.
+ XEvent ev;
+ while (XCheckIfEvent(x11_display, &ev, _predicate_clipboard_save_targets, (XPointer)&x11_window)) {
+ switch (ev.type) {
+ case SelectionRequest:
+ _handle_selection_request_event(&(ev.xselectionrequest));
+ break;
+
+ case SelectionNotify: {
+ if (ev.xselection.target == save_targets) {
+ // Once SelectionNotify is received, we're done whether it succeeded or not.
+ return;
+ }
+
+ break;
+ }
+ }
+ }
+ }
+}
+
int DisplayServerX11::get_screen_count() const {
_THREAD_SAFE_METHOD_
@@ -794,7 +848,9 @@ void DisplayServerX11::window_set_title(const String &p_title, WindowID p_window
Atom _net_wm_name = XInternAtom(x11_display, "_NET_WM_NAME", false);
Atom utf8_string = XInternAtom(x11_display, "UTF8_STRING", false);
- XChangeProperty(x11_display, wd.x11_window, _net_wm_name, utf8_string, 8, PropModeReplace, (unsigned char *)p_title.utf8().get_data(), p_title.utf8().length());
+ if (_net_wm_name != None && utf8_string != None) {
+ XChangeProperty(x11_display, wd.x11_window, _net_wm_name, utf8_string, 8, PropModeReplace, (unsigned char *)p_title.utf8().get_data(), p_title.utf8().length());
+ }
}
void DisplayServerX11::window_set_mouse_passthrough(const Vector<Vector2> &p_region, WindowID p_window) {
@@ -1276,7 +1332,9 @@ void DisplayServerX11::_set_wm_fullscreen(WindowID p_window, bool p_enabled) {
hints.flags = 2;
hints.decorations = 0;
property = XInternAtom(x11_display, "_MOTIF_WM_HINTS", True);
- XChangeProperty(x11_display, wd.x11_window, property, property, 32, PropModeReplace, (unsigned char *)&hints, 5);
+ if (property != None) {
+ XChangeProperty(x11_display, wd.x11_window, property, property, 32, PropModeReplace, (unsigned char *)&hints, 5);
+ }
}
if (p_enabled) {
@@ -1303,7 +1361,9 @@ void DisplayServerX11::_set_wm_fullscreen(WindowID p_window, bool p_enabled) {
// set bypass compositor hint
Atom bypass_compositor = XInternAtom(x11_display, "_NET_WM_BYPASS_COMPOSITOR", False);
unsigned long compositing_disable_on = p_enabled ? 1 : 0;
- XChangeProperty(x11_display, wd.x11_window, bypass_compositor, XA_CARDINAL, 32, PropModeReplace, (unsigned char *)&compositing_disable_on, 1);
+ if (bypass_compositor != None) {
+ XChangeProperty(x11_display, wd.x11_window, bypass_compositor, XA_CARDINAL, 32, PropModeReplace, (unsigned char *)&compositing_disable_on, 1);
+ }
XFlush(x11_display);
@@ -1317,7 +1377,9 @@ void DisplayServerX11::_set_wm_fullscreen(WindowID p_window, bool p_enabled) {
hints.flags = 2;
hints.decorations = window_get_flag(WINDOW_FLAG_BORDERLESS, p_window) ? 0 : 1;
property = XInternAtom(x11_display, "_MOTIF_WM_HINTS", True);
- XChangeProperty(x11_display, wd.x11_window, property, property, 32, PropModeReplace, (unsigned char *)&hints, 5);
+ if (property != None) {
+ XChangeProperty(x11_display, wd.x11_window, property, property, 32, PropModeReplace, (unsigned char *)&hints, 5);
+ }
}
}
@@ -1511,7 +1573,9 @@ void DisplayServerX11::window_set_flag(WindowFlags p_flag, bool p_enabled, Windo
hints.flags = 2;
hints.decorations = p_enabled ? 0 : 1;
property = XInternAtom(x11_display, "_MOTIF_WM_HINTS", True);
- XChangeProperty(x11_display, wd.x11_window, property, property, 32, PropModeReplace, (unsigned char *)&hints, 5);
+ if (property != None) {
+ XChangeProperty(x11_display, wd.x11_window, property, property, 32, PropModeReplace, (unsigned char *)&hints, 5);
+ }
// Preserve window size
window_set_size(window_get_size(p_window), p_window);
@@ -2281,53 +2345,105 @@ void DisplayServerX11::_handle_key_event(WindowID p_window, XKeyEvent *p_event,
Input::get_singleton()->accumulate_input_event(k);
}
-void DisplayServerX11::_handle_selection_request_event(XSelectionRequestEvent *p_event) {
- XEvent respond;
- if (p_event->target == XInternAtom(x11_display, "UTF8_STRING", 0) ||
- p_event->target == XInternAtom(x11_display, "COMPOUND_TEXT", 0) ||
- p_event->target == XInternAtom(x11_display, "TEXT", 0) ||
- p_event->target == XA_STRING ||
- p_event->target == XInternAtom(x11_display, "text/plain;charset=utf-8", 0) ||
- p_event->target == XInternAtom(x11_display, "text/plain", 0)) {
- // Directly using internal clipboard because we know our window
- // is the owner during a selection request.
- CharString clip = internal_clipboard.utf8();
- XChangeProperty(x11_display,
- p_event->requestor,
- p_event->property,
- p_event->target,
- 8,
- PropModeReplace,
- (unsigned char *)clip.get_data(),
- clip.length());
- respond.xselection.property = p_event->property;
- } else if (p_event->target == XInternAtom(x11_display, "TARGETS", 0)) {
- Atom data[7];
+Atom DisplayServerX11::_process_selection_request_target(Atom p_target, Window p_requestor, Atom p_property) const {
+ if (p_target == XInternAtom(x11_display, "TARGETS", 0)) {
+ // Request to list all supported targets.
+ Atom data[9];
data[0] = XInternAtom(x11_display, "TARGETS", 0);
- data[1] = XInternAtom(x11_display, "UTF8_STRING", 0);
- data[2] = XInternAtom(x11_display, "COMPOUND_TEXT", 0);
- data[3] = XInternAtom(x11_display, "TEXT", 0);
- data[4] = XA_STRING;
- data[5] = XInternAtom(x11_display, "text/plain;charset=utf-8", 0);
- data[6] = XInternAtom(x11_display, "text/plain", 0);
+ data[1] = XInternAtom(x11_display, "SAVE_TARGETS", 0);
+ data[2] = XInternAtom(x11_display, "MULTIPLE", 0);
+ data[3] = XInternAtom(x11_display, "UTF8_STRING", 0);
+ data[4] = XInternAtom(x11_display, "COMPOUND_TEXT", 0);
+ data[5] = XInternAtom(x11_display, "TEXT", 0);
+ data[6] = XA_STRING;
+ data[7] = XInternAtom(x11_display, "text/plain;charset=utf-8", 0);
+ data[8] = XInternAtom(x11_display, "text/plain", 0);
XChangeProperty(x11_display,
- p_event->requestor,
- p_event->property,
+ p_requestor,
+ p_property,
XA_ATOM,
32,
PropModeReplace,
(unsigned char *)&data,
sizeof(data) / sizeof(data[0]));
- respond.xselection.property = p_event->property;
-
+ return p_property;
+ } else if (p_target == XInternAtom(x11_display, "SAVE_TARGETS", 0)) {
+ // Request to check if SAVE_TARGETS is supported, nothing special to do.
+ XChangeProperty(x11_display,
+ p_requestor,
+ p_property,
+ XInternAtom(x11_display, "NULL", False),
+ 32,
+ PropModeReplace,
+ nullptr,
+ 0);
+ return p_property;
+ } else if (p_target == XInternAtom(x11_display, "UTF8_STRING", 0) ||
+ p_target == XInternAtom(x11_display, "COMPOUND_TEXT", 0) ||
+ p_target == XInternAtom(x11_display, "TEXT", 0) ||
+ p_target == XA_STRING ||
+ p_target == XInternAtom(x11_display, "text/plain;charset=utf-8", 0) ||
+ p_target == XInternAtom(x11_display, "text/plain", 0)) {
+ // Directly using internal clipboard because we know our window
+ // is the owner during a selection request.
+ CharString clip = internal_clipboard.utf8();
+ XChangeProperty(x11_display,
+ p_requestor,
+ p_property,
+ p_target,
+ 8,
+ PropModeReplace,
+ (unsigned char *)clip.get_data(),
+ clip.length());
+ return p_property;
} else {
- char *targetname = XGetAtomName(x11_display, p_event->target);
- printf("No Target '%s'\n", targetname);
- if (targetname) {
- XFree(targetname);
+ char *target_name = XGetAtomName(x11_display, p_target);
+ printf("Target '%s' not supported.\n", target_name);
+ if (target_name) {
+ XFree(target_name);
}
+ return None;
+ }
+}
+
+void DisplayServerX11::_handle_selection_request_event(XSelectionRequestEvent *p_event) const {
+ XEvent respond;
+ if (p_event->target == XInternAtom(x11_display, "MULTIPLE", 0)) {
+ // Request for multiple target conversions at once.
+ Atom atom_pair = XInternAtom(x11_display, "ATOM_PAIR", False);
respond.xselection.property = None;
+
+ Atom type;
+ int format;
+ unsigned long len;
+ unsigned long remaining;
+ unsigned char *data = nullptr;
+ if (XGetWindowProperty(x11_display, p_event->requestor, p_event->property, 0, LONG_MAX, False, atom_pair, &type, &format, &len, &remaining, &data) == Success) {
+ if ((len >= 2) && data) {
+ Atom *targets = (Atom *)data;
+ for (uint64_t i = 0; i < len; i += 2) {
+ Atom target = targets[i];
+ Atom &property = targets[i + 1];
+ property = _process_selection_request_target(target, p_event->requestor, property);
+ }
+
+ XChangeProperty(x11_display,
+ p_event->requestor,
+ p_event->property,
+ atom_pair,
+ 32,
+ PropModeReplace,
+ (unsigned char *)targets,
+ len);
+
+ respond.xselection.property = p_event->property;
+ }
+ XFree(data);
+ }
+ } else {
+ // Request for target conversion.
+ respond.xselection.property = _process_selection_request_target(p_event->target, p_event->requestor, p_event->property);
}
respond.xselection.type = SelectionNotify;
@@ -2463,32 +2579,43 @@ Bool DisplayServerX11::_predicate_all_events(Display *display, XEvent *event, XP
return True;
}
-void DisplayServerX11::_poll_events() {
+bool DisplayServerX11::_wait_for_events() const {
int x11_fd = ConnectionNumber(x11_display);
fd_set in_fds;
- while (!events_thread_done) {
- XFlush(x11_display);
+ XFlush(x11_display);
+
+ FD_ZERO(&in_fds);
+ FD_SET(x11_fd, &in_fds);
- FD_ZERO(&in_fds);
- FD_SET(x11_fd, &in_fds);
+ struct timeval tv;
+ tv.tv_usec = 0;
+ tv.tv_sec = 1;
- struct timeval tv;
- tv.tv_usec = 0;
- tv.tv_sec = 1;
+ // Wait for next event or timeout.
+ int num_ready_fds = select(x11_fd + 1, &in_fds, NULL, NULL, &tv);
- // Wait for next event or timeout.
- int num_ready_fds = select(x11_fd + 1, &in_fds, NULL, NULL, &tv);
+ if (num_ready_fds > 0) {
+ // Event received.
+ return true;
+ } else {
+ // Error or timeout.
if (num_ready_fds < 0) {
- ERR_PRINT("_poll_events: select error: " + itos(errno));
+ ERR_PRINT("_wait_for_events: select error: " + itos(errno));
}
+ return false;
+ }
+}
+
+void DisplayServerX11::_poll_events() {
+ while (!events_thread_done) {
+ _wait_for_events();
// Process events from the queue.
{
MutexLock mutex_lock(events_mutex);
- // Non-blocking wait for next event
- // and remove it from the queue.
+ // Non-blocking wait for next event and remove it from the queue.
XEvent ev;
while (XCheckIfEvent(x11_display, &ev, _predicate_all_events, nullptr)) {
// Check if the input manager wants to process the event.
@@ -3385,7 +3512,9 @@ void DisplayServerX11::set_icon(const Ref<Image> &p_icon) {
pr += 4;
}
- XChangeProperty(x11_display, wd.x11_window, net_wm_icon, XA_CARDINAL, 32, PropModeReplace, (unsigned char *)pd.ptr(), pd.size());
+ if (net_wm_icon != None) {
+ XChangeProperty(x11_display, wd.x11_window, net_wm_icon, XA_CARDINAL, 32, PropModeReplace, (unsigned char *)pd.ptr(), pd.size());
+ }
if (!g_set_icon_error) {
break;
@@ -3478,7 +3607,9 @@ DisplayServerX11::WindowID DisplayServerX11::_create_window(WindowMode p_mode, u
{
const long pid = OS::get_singleton()->get_process_id();
Atom net_wm_pid = XInternAtom(x11_display, "_NET_WM_PID", False);
- XChangeProperty(x11_display, wd.x11_window, net_wm_pid, XA_CARDINAL, 32, PropModeReplace, (unsigned char *)&pid, 1);
+ if (net_wm_pid != None) {
+ XChangeProperty(x11_display, wd.x11_window, net_wm_pid, XA_CARDINAL, 32, PropModeReplace, (unsigned char *)&pid, 1);
+ }
}
long im_event_mask = 0;
@@ -3526,7 +3657,9 @@ DisplayServerX11::WindowID DisplayServerX11::_create_window(WindowMode p_mode, u
/* set the titlebar name */
XStoreName(x11_display, wd.x11_window, "Godot");
XSetWMProtocols(x11_display, wd.x11_window, &wm_delete, 1);
- XChangeProperty(x11_display, wd.x11_window, xdnd_aware, XA_ATOM, 32, PropModeReplace, (unsigned char *)&xdnd_version, 1);
+ if (xdnd_aware != None) {
+ XChangeProperty(x11_display, wd.x11_window, xdnd_aware, XA_ATOM, 32, PropModeReplace, (unsigned char *)&xdnd_version, 1);
+ }
if (xim && xim_style) {
// Block events polling while changing input focus
@@ -3557,20 +3690,25 @@ DisplayServerX11::WindowID DisplayServerX11::_create_window(WindowMode p_mode, u
hints.flags = 2;
hints.decorations = 0;
property = XInternAtom(x11_display, "_MOTIF_WM_HINTS", True);
- XChangeProperty(x11_display, wd.x11_window, property, property, 32, PropModeReplace, (unsigned char *)&hints, 5);
+ if (property != None) {
+ XChangeProperty(x11_display, wd.x11_window, property, property, 32, PropModeReplace, (unsigned char *)&hints, 5);
+ }
}
if (wd.menu_type) {
// Set Utility type to disable fade animations.
Atom type_atom = XInternAtom(x11_display, "_NET_WM_WINDOW_TYPE_UTILITY", False);
Atom wt_atom = XInternAtom(x11_display, "_NET_WM_WINDOW_TYPE", False);
-
- XChangeProperty(x11_display, wd.x11_window, wt_atom, XA_ATOM, 32, PropModeReplace, (unsigned char *)&type_atom, 1);
+ if (wt_atom != None && type_atom != None) {
+ XChangeProperty(x11_display, wd.x11_window, wt_atom, XA_ATOM, 32, PropModeReplace, (unsigned char *)&type_atom, 1);
+ }
} else {
Atom type_atom = XInternAtom(x11_display, "_NET_WM_WINDOW_TYPE_NORMAL", False);
Atom wt_atom = XInternAtom(x11_display, "_NET_WM_WINDOW_TYPE", False);
- XChangeProperty(x11_display, wd.x11_window, wt_atom, XA_ATOM, 32, PropModeReplace, (unsigned char *)&type_atom, 1);
+ if (wt_atom != None && type_atom != None) {
+ XChangeProperty(x11_display, wd.x11_window, wt_atom, XA_ATOM, 32, PropModeReplace, (unsigned char *)&type_atom, 1);
+ }
}
_update_size_hints(id);
@@ -3852,6 +3990,10 @@ DisplayServerX11::DisplayServerX11(const String &p_rendering_driver, WindowMode
(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));
+ if (main_window == INVALID_WINDOW_ID) {
+ r_error = ERR_CANT_CREATE;
+ return;
+ }
for (int i = 0; i < WINDOW_FLAG_MAX; i++) {
if (p_flags & (1 << i)) {
window_set_flag(WindowFlags(i), true, main_window);
@@ -4043,6 +4185,11 @@ DisplayServerX11::DisplayServerX11(const String &p_rendering_driver, WindowMode
}
DisplayServerX11::~DisplayServerX11() {
+ // Send owned clipboard data to clipboard manager before exit.
+ Window x11_main_window = windows[MAIN_WINDOW_ID].x11_window;
+ _clipboard_transfer_ownership(XA_PRIMARY, x11_main_window);
+ _clipboard_transfer_ownership(XInternAtom(x11_display, "CLIPBOARD", 0), x11_main_window);
+
events_thread_done = true;
Thread::wait_to_finish(events_thread);
memdelete(events_thread);
diff --git a/platform/linuxbsd/display_server_x11.h b/platform/linuxbsd/display_server_x11.h
index 9ff28b9ed2..682f1c8ef3 100644
--- a/platform/linuxbsd/display_server_x11.h
+++ b/platform/linuxbsd/display_server_x11.h
@@ -204,10 +204,13 @@ class DisplayServerX11 : public DisplayServer {
Point2i center;
void _handle_key_event(WindowID p_window, XKeyEvent *p_event, LocalVector<XEvent> &p_events, uint32_t &p_event_index, bool p_echo = false);
- void _handle_selection_request_event(XSelectionRequestEvent *p_event);
+
+ Atom _process_selection_request_target(Atom p_target, Window p_requestor, Atom p_property) const;
+ void _handle_selection_request_event(XSelectionRequestEvent *p_event) const;
String _clipboard_get_impl(Atom p_source, Window x11_window, Atom target) const;
String _clipboard_get(Atom p_source, Window x11_window) const;
+ void _clipboard_transfer_ownership(Atom p_source, Window x11_window) const;
//bool minimized;
//bool window_has_focus;
@@ -262,10 +265,12 @@ class DisplayServerX11 : public DisplayServer {
bool events_thread_done = false;
LocalVector<XEvent> polled_events;
static void _poll_events_thread(void *ud);
+ bool _wait_for_events() const;
void _poll_events();
static Bool _predicate_all_events(Display *display, XEvent *event, XPointer arg);
static Bool _predicate_clipboard_selection(Display *display, XEvent *event, XPointer arg);
+ static Bool _predicate_clipboard_save_targets(Display *display, XEvent *event, XPointer arg);
protected:
void _window_changed(XEvent *event);
diff --git a/platform/linuxbsd/joypad_linux.cpp b/platform/linuxbsd/joypad_linux.cpp
index fda1358dfd..4a9d0a8181 100644
--- a/platform/linuxbsd/joypad_linux.cpp
+++ b/platform/linuxbsd/joypad_linux.cpp
@@ -459,9 +459,9 @@ void JoypadLinux::process_joypads() {
case ABS_HAT0X:
if (ev.value != 0) {
if (ev.value < 0) {
- joy->dpad |= Input::HAT_MASK_LEFT;
+ joy->dpad = (joy->dpad | Input::HAT_MASK_LEFT) & ~Input::HAT_MASK_RIGHT;
} else {
- joy->dpad |= Input::HAT_MASK_RIGHT;
+ joy->dpad = (joy->dpad | Input::HAT_MASK_RIGHT) & ~Input::HAT_MASK_LEFT;
}
} else {
joy->dpad &= ~(Input::HAT_MASK_LEFT | Input::HAT_MASK_RIGHT);
@@ -473,9 +473,9 @@ void JoypadLinux::process_joypads() {
case ABS_HAT0Y:
if (ev.value != 0) {
if (ev.value < 0) {
- joy->dpad |= Input::HAT_MASK_UP;
+ joy->dpad = (joy->dpad | Input::HAT_MASK_UP) & ~Input::HAT_MASK_DOWN;
} else {
- joy->dpad |= Input::HAT_MASK_DOWN;
+ joy->dpad = (joy->dpad | Input::HAT_MASK_DOWN) & ~Input::HAT_MASK_UP;
}
} else {
joy->dpad &= ~(Input::HAT_MASK_UP | Input::HAT_MASK_DOWN);
diff --git a/platform/linuxbsd/key_mapping_x11.cpp b/platform/linuxbsd/key_mapping_x11.cpp
index 77512b1a9e..78049f2dfc 100644
--- a/platform/linuxbsd/key_mapping_x11.cpp
+++ b/platform/linuxbsd/key_mapping_x11.cpp
@@ -185,7 +185,6 @@ struct _TranslatePair {
};
static _TranslatePair _scancode_to_keycode[] = {
-
{ KEY_ESCAPE, 0x09 },
{ KEY_1, 0x0A },
{ KEY_2, 0x0B },
@@ -354,7 +353,6 @@ struct _XTranslateUnicodePair {
};
enum {
-
_KEYSYM_MAX = 759
};
@@ -1160,7 +1158,6 @@ struct _XTranslateUnicodePairReverse {
};
enum {
-
_UNICODE_MAX = 750
};
diff --git a/platform/osx/detect.py b/platform/osx/detect.py
index 64e1d94f39..ea41479bb0 100644
--- a/platform/osx/detect.py
+++ b/platform/osx/detect.py
@@ -87,8 +87,8 @@ def configure(env):
if env["arch"] == "arm64":
print("Building for macOS 10.15+, platform arm64.")
- env.Append(CCFLAGS=["-arch", "arm64", "-mmacosx-version-min=10.15", "-target", "arm64-apple-macos10.15"])
- env.Append(LINKFLAGS=["-arch", "arm64", "-mmacosx-version-min=10.15", "-target", "arm64-apple-macos10.15"])
+ env.Append(CCFLAGS=["-arch", "arm64", "-mmacosx-version-min=10.15"])
+ env.Append(LINKFLAGS=["-arch", "arm64", "-mmacosx-version-min=10.15"])
else:
print("Building for macOS 10.12+, platform x86-64.")
env.Append(CCFLAGS=["-arch", "x86_64", "-mmacosx-version-min=10.12"])
@@ -99,7 +99,6 @@ def configure(env):
mpprefix = os.environ.get("MACPORTS_PREFIX", "/opt/local")
mpclangver = env["macports_clang"]
env["CC"] = mpprefix + "/libexec/llvm-" + mpclangver + "/bin/clang"
- env["LINK"] = mpprefix + "/libexec/llvm-" + mpclangver + "/bin/clang++"
env["CXX"] = mpprefix + "/libexec/llvm-" + mpclangver + "/bin/clang++"
env["AR"] = mpprefix + "/libexec/llvm-" + mpclangver + "/bin/llvm-ar"
env["RANLIB"] = mpprefix + "/libexec/llvm-" + mpclangver + "/bin/llvm-ranlib"
@@ -134,12 +133,6 @@ def configure(env):
env["AS"] = basecmd + "as"
env.Append(CPPDEFINES=["__MACPORTS__"]) # hack to fix libvpx MM256_BROADCASTSI128_SI256 define
- if env["CXX"] == "clang++":
- # This should now work with clang++, re-enable if there are issues
- # env.Append(CPPDEFINES=["TYPED_METHOD_BIND"])
- env["CC"] = "clang"
- env["LINK"] = "clang++"
-
if env["use_ubsan"] or env["use_asan"] or env["use_tsan"]:
env.extra_suffix += "s"
diff --git a/platform/server/detect.py b/platform/server/detect.py
index f9e151f956..d9ac357679 100644
--- a/platform/server/detect.py
+++ b/platform/server/detect.py
@@ -94,8 +94,6 @@ def configure(env):
if "clang++" not in os.path.basename(env["CXX"]):
env["CC"] = "clang"
env["CXX"] = "clang++"
- env["LINK"] = "clang++"
- env.Append(CPPDEFINES=["TYPED_METHOD_BIND"])
env.extra_suffix = ".llvm" + env.extra_suffix
if env["use_coverage"]:
diff --git a/platform/uwp/export/export.cpp b/platform/uwp/export/export.cpp
index e0c03df48e..30568241a9 100644
--- a/platform/uwp/export/export.cpp
+++ b/platform/uwp/export/export.cpp
@@ -1004,6 +1004,9 @@ public:
}
virtual void get_export_options(List<ExportOption> *r_options) override {
+ r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "custom_template/debug", PROPERTY_HINT_GLOBAL_FILE, "*.zip"), ""));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "custom_template/release", PROPERTY_HINT_GLOBAL_FILE, "*.zip"), ""));
+
r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "architecture/target", PROPERTY_HINT_ENUM, "arm,x86,x64"), 1));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "command_line/extra_args"), ""));
@@ -1045,9 +1048,6 @@ public:
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "tiles/show_name_on_wide310x150"), false));
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "tiles/show_name_on_square310x310"), false));
- r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "custom_template/debug", PROPERTY_HINT_GLOBAL_FILE, "*.zip"), ""));
- r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "custom_template/release", PROPERTY_HINT_GLOBAL_FILE, "*.zip"), ""));
-
// Capabilities
const char **basic = uwp_capabilities;
while (*basic) {
diff --git a/platform/windows/detect.py b/platform/windows/detect.py
index 934314b2f2..e0b2a52014 100644
--- a/platform/windows/detect.py
+++ b/platform/windows/detect.py
@@ -383,18 +383,17 @@ def configure_mingw(env):
if env["use_llvm"]:
env["CC"] = mingw_prefix + "clang"
- env["AS"] = mingw_prefix + "as"
env["CXX"] = mingw_prefix + "clang++"
+ env["AS"] = mingw_prefix + "as"
env["AR"] = mingw_prefix + "ar"
env["RANLIB"] = mingw_prefix + "ranlib"
- env["LINK"] = mingw_prefix + "clang++"
else:
env["CC"] = mingw_prefix + "gcc"
- env["AS"] = mingw_prefix + "as"
env["CXX"] = mingw_prefix + "g++"
+ env["AS"] = mingw_prefix + "as"
env["AR"] = mingw_prefix + "gcc-ar"
env["RANLIB"] = mingw_prefix + "gcc-ranlib"
- env["LINK"] = mingw_prefix + "g++"
+
env["x86_libtheora_opt_gcc"] = True
if env["use_lto"]:
diff --git a/platform/windows/key_mapping_windows.cpp b/platform/windows/key_mapping_windows.cpp
index d8d0b13068..25eff7df57 100644
--- a/platform/windows/key_mapping_windows.cpp
+++ b/platform/windows/key_mapping_windows.cpp
@@ -38,7 +38,6 @@ struct _WinTranslatePair {
};
static _WinTranslatePair _vk_to_keycode[] = {
-
{ KEY_BACKSPACE, VK_BACK }, // (0x08) // backspace
{ KEY_TAB, VK_TAB }, //(0x09)
@@ -238,7 +237,6 @@ VK_OEM_CLEAR (0xFE)
*/
static _WinTranslatePair _scancode_to_keycode[] = {
-
{ KEY_ESCAPE, 0x01 },
{ KEY_1, 0x02 },
{ KEY_2, 0x03 },
diff --git a/platform/windows/os_windows.cpp b/platform/windows/os_windows.cpp
index b108d74b2e..646bc3aa4c 100644
--- a/platform/windows/os_windows.cpp
+++ b/platform/windows/os_windows.cpp
@@ -466,8 +466,10 @@ Error OS_Windows::execute(const String &p_path, const List<String> &p_arguments,
ERR_FAIL_COND_V(ret == 0, ERR_CANT_FORK);
if (p_blocking) {
- DWORD ret2 = WaitForSingleObject(pi.pi.hProcess, INFINITE);
+ WaitForSingleObject(pi.pi.hProcess, INFINITE);
if (r_exitcode) {
+ DWORD ret2;
+ GetExitCodeProcess(pi.pi.hProcess, &ret2);
*r_exitcode = ret2;
}