diff options
Diffstat (limited to 'platform')
131 files changed, 2226 insertions, 2380 deletions
diff --git a/platform/android/SCsub b/platform/android/SCsub index 344ca036de..e4d04f1df9 100644 --- a/platform/android/SCsub +++ b/platform/android/SCsub @@ -53,14 +53,14 @@ else: print("WARN: Architecture not suitable for embedding into APK; keeping .so at \\bin") if lib_arch_dir != "": - if env["target"] == "release": - lib_type_dir = "release" - elif env["target"] == "release_debug": - lib_type_dir = "debug" - else: # debug + if env.dev_build: lib_type_dir = "dev" + elif env.debug_features: + lib_type_dir = "debug" + else: # Release + lib_type_dir = "release" - if env["tools"]: + if env.editor_build: lib_tools_dir = "tools/" else: lib_tools_dir = "" diff --git a/platform/android/android_input_handler.cpp b/platform/android/android_input_handler.cpp index 454bcd2eda..c0b098cd7f 100644 --- a/platform/android/android_input_handler.cpp +++ b/platform/android/android_input_handler.cpp @@ -118,7 +118,7 @@ void AndroidInputHandler::process_key_event(int p_keycode, int p_physical_keycod Input::get_singleton()->parse_input_event(ev); } -void AndroidInputHandler::_parse_all_touch(bool p_pressed) { +void AndroidInputHandler::_parse_all_touch(bool p_pressed, bool p_double_tap) { if (touch.size()) { //end all if exist for (int i = 0; i < touch.size(); i++) { @@ -127,17 +127,18 @@ void AndroidInputHandler::_parse_all_touch(bool p_pressed) { ev->set_index(touch[i].id); ev->set_pressed(p_pressed); ev->set_position(touch[i].pos); + ev->set_double_tap(p_double_tap); Input::get_singleton()->parse_input_event(ev); } } } void AndroidInputHandler::_release_all_touch() { - _parse_all_touch(false); + _parse_all_touch(false, false); touch.clear(); } -void AndroidInputHandler::process_touch_event(int p_event, int p_pointer, const Vector<TouchPos> &p_points) { +void AndroidInputHandler::process_touch_event(int p_event, int p_pointer, const Vector<TouchPos> &p_points, bool p_double_tap) { switch (p_event) { case AMOTION_EVENT_ACTION_DOWN: { //gesture begin // Release any remaining touches or mouse event @@ -151,7 +152,7 @@ void AndroidInputHandler::process_touch_event(int p_event, int p_pointer, const } //send touch - _parse_all_touch(true); + _parse_all_touch(true, p_double_tap); } break; case AMOTION_EVENT_ACTION_MOVE: { //motion diff --git a/platform/android/android_input_handler.h b/platform/android/android_input_handler.h index 88490f0407..4da8a910c0 100644 --- a/platform/android/android_input_handler.h +++ b/platform/android/android_input_handler.h @@ -87,13 +87,13 @@ private: void _release_mouse_event_info(bool p_source_mouse_relative = false); - void _parse_all_touch(bool p_pressed); + void _parse_all_touch(bool p_pressed, bool p_double_tap); void _release_all_touch(); public: void process_mouse_event(int p_event_action, int p_event_android_buttons_mask, Point2 p_event_pos, Vector2 p_delta, bool p_double_click, bool p_source_mouse_relative); - void process_touch_event(int p_event, int p_pointer, const Vector<TouchPos> &p_points); + void process_touch_event(int p_event, int p_pointer, const Vector<TouchPos> &p_points, bool p_double_tap); void process_magnify(Point2 p_pos, float p_factor); void process_pan(Point2 p_pos, Vector2 p_delta); void process_joy_event(JoypadEvent p_event); diff --git a/platform/android/android_keys_utils.h b/platform/android/android_keys_utils.h index 5ec3ee17aa..bc633aeade 100644 --- a/platform/android/android_keys_utils.h +++ b/platform/android/android_keys_utils.h @@ -43,7 +43,6 @@ struct AndroidGodotCodePair { static AndroidGodotCodePair android_godot_code_pairs[] = { { AKEYCODE_UNKNOWN, Key::UNKNOWN }, // (0) Unknown key code. - { AKEYCODE_HOME, Key::HOME }, // (3) Home key. { AKEYCODE_BACK, Key::BACK }, // (4) Back key. { AKEYCODE_0, Key::KEY_0 }, // (7) '0' key. { AKEYCODE_1, Key::KEY_1 }, // (8) '1' key. @@ -63,6 +62,7 @@ static AndroidGodotCodePair android_godot_code_pairs[] = { { AKEYCODE_DPAD_RIGHT, Key::RIGHT }, // (22) Directional Pad Right key. { AKEYCODE_VOLUME_UP, Key::VOLUMEUP }, // (24) Volume Up key. { AKEYCODE_VOLUME_DOWN, Key::VOLUMEDOWN }, // (25) Volume Down key. + { AKEYCODE_POWER, Key::STANDBY }, // (26) Power key. { AKEYCODE_CLEAR, Key::CLEAR }, // (28) Clear key. { AKEYCODE_A, Key::A }, // (29) 'A' key. { AKEYCODE_B, Key::B }, // (30) 'B' key. @@ -98,6 +98,7 @@ static AndroidGodotCodePair android_godot_code_pairs[] = { { AKEYCODE_SHIFT_RIGHT, Key::SHIFT }, // (60) Right Shift modifier key. { AKEYCODE_TAB, Key::TAB }, // (61) Tab key. { AKEYCODE_SPACE, Key::SPACE }, // (62) Space key. + { AKEYCODE_ENVELOPE, Key::LAUNCHMAIL }, // (65) Envelope special function key. { AKEYCODE_ENTER, Key::ENTER }, // (66) Enter key. { AKEYCODE_DEL, Key::BACKSPACE }, // (67) Backspace key. { AKEYCODE_GRAVE, Key::QUOTELEFT }, // (68) '`' (backtick) key. @@ -114,6 +115,7 @@ static AndroidGodotCodePair android_godot_code_pairs[] = { { AKEYCODE_MENU, Key::MENU }, // (82) Menu key. { AKEYCODE_SEARCH, Key::SEARCH }, // (84) Search key. { AKEYCODE_MEDIA_STOP, Key::MEDIASTOP }, // (86) Stop media key. + { AKEYCODE_MEDIA_NEXT, Key::MEDIANEXT }, // (87) Play Next media key. { AKEYCODE_MEDIA_PREVIOUS, Key::MEDIAPREVIOUS }, // (88) Play Previous media key. { AKEYCODE_PAGE_UP, Key::PAGEUP }, // (92) Page Up key. { AKEYCODE_PAGE_DOWN, Key::PAGEDOWN }, // (93) Page Down key. @@ -127,6 +129,8 @@ static AndroidGodotCodePair android_godot_code_pairs[] = { { AKEYCODE_META_RIGHT, Key::META }, // (118) Right Meta modifier key. { AKEYCODE_SYSRQ, Key::PRINT }, // (120) System Request / Print Screen key. { AKEYCODE_BREAK, Key::PAUSE }, // (121) Break / Pause key. + { AKEYCODE_MOVE_HOME, Key::HOME }, // (122) Home Movement key. + { AKEYCODE_MOVE_END, Key::END }, // (123) End Movement key. { AKEYCODE_INSERT, Key::INSERT }, // (124) Insert key. { AKEYCODE_FORWARD, Key::FORWARD }, // (125) Forward key. { AKEYCODE_MEDIA_PLAY, Key::MEDIAPLAY }, // (126) Play media key. diff --git a/platform/android/api/jni_singleton.h b/platform/android/api/jni_singleton.h index 690fddae21..895bc70103 100644 --- a/platform/android/api/jni_singleton.h +++ b/platform/android/api/jni_singleton.h @@ -150,9 +150,8 @@ public: env->DeleteLocalRef(arr); } break; -#ifndef _MSC_VER -#warning This is missing 64 bits arrays, I have no idea how to do it in JNI -#endif + // TODO: This is missing 64 bits arrays, I have no idea how to do it in JNI. + case Variant::DICTIONARY: { jobject obj = env->CallObjectMethodA(instance, E->get().method, v); ret = _jobject_to_variant(env, obj); diff --git a/platform/android/detect.py b/platform/android/detect.py index 2ff9cd3dc5..6eb8ba34ed 100644 --- a/platform/android/detect.py +++ b/platform/android/detect.py @@ -3,6 +3,11 @@ import sys import platform import subprocess +from typing import TYPE_CHECKING + +if TYPE_CHECKING: + from SCons import Environment + def is_active(): return True @@ -17,8 +22,6 @@ def can_build(): def get_opts(): - from SCons.Variables import BoolVariable, EnumVariable - return [ ("ANDROID_SDK_ROOT", "Path to the Android SDK", get_env_android_sdk_root()), ("ndk_platform", 'Target platform (android-<api>, e.g. "android-24")', "android-24"), @@ -46,7 +49,7 @@ def get_ndk_version(): def get_flags(): return [ ("arch", "arm64"), # Default for convenience. - ("tools", False), + ("target", "template_debug"), ] @@ -74,7 +77,7 @@ def install_ndk_if_needed(env): env["ANDROID_NDK_ROOT"] = get_android_ndk_root(env) -def configure(env): +def configure(env: "Environment"): # Validate arch. supported_arches = ["x86_32", "x86_64", "arm32", "arm64"] if env["arch"] not in supported_arches: @@ -114,20 +117,6 @@ def configure(env): env.Append(CCFLAGS=target_option) env.Append(LINKFLAGS=target_option) - # Build type - - if env["target"].startswith("release"): - if env["optimize"] == "speed": # optimize for speed (default) - # `-O2` is more friendly to debuggers than `-O3`, leading to better crash backtraces - # when using `target=release_debug`. - opt = "-O3" if env["target"] == "release" else "-O2" - env.Append(CCFLAGS=[opt]) - elif env["optimize"] == "size": # optimize for size - env.Append(CCFLAGS=["-Oz"]) - elif env["target"] == "debug": - env.Append(LINKFLAGS=["-O0"]) - env.Append(CCFLAGS=["-O0", "-g"]) - # LTO if env["lto"] == "auto": # LTO benefits for Android (size, performance) haven't been clearly established yet. @@ -167,22 +156,16 @@ def configure(env): env["RANLIB"] = compiler_path + "/llvm-ranlib" env["AS"] = compiler_path + "/clang" - # Disable exceptions and rtti on non-tools (template) builds - if env["tools"]: - env.Append(CXXFLAGS=["-frtti"]) - elif env["builtin_icu"]: - env.Append(CXXFLAGS=["-frtti", "-fno-exceptions"]) - else: - env.Append(CXXFLAGS=["-fno-rtti", "-fno-exceptions"]) - # Don't use dynamic_cast, necessary with no-rtti. - env.Append(CPPDEFINES=["NO_SAFE_CAST"]) + # Disable exceptions on template builds + if not env.editor_build: + env.Append(CXXFLAGS=["-fno-exceptions"]) env.Append( CCFLAGS=( "-fpic -ffunction-sections -funwind-tables -fstack-protector-strong -fvisibility=hidden -fno-strict-aliasing".split() ) ) - env.Append(CPPDEFINES=["NO_STATVFS", "GLES_ENABLED"]) + env.Append(CPPDEFINES=["GLES_ENABLED"]) if get_min_sdk_version(env["ndk_platform"]) >= 24: env.Append(CPPDEFINES=[("_FILE_OFFSET_BITS", 64)]) @@ -204,7 +187,7 @@ def configure(env): env.Append(LINKFLAGS="-Wl,-soname,libgodot_android.so") env.Prepend(CPPPATH=["#platform/android"]) - env.Append(CPPDEFINES=["ANDROID_ENABLED", "UNIX_ENABLED", "NO_FCNTL"]) + env.Append(CPPDEFINES=["ANDROID_ENABLED", "UNIX_ENABLED"]) env.Append(LIBS=["OpenSLES", "EGL", "GLESv2", "android", "log", "z", "dl"]) if env["vulkan"]: diff --git a/platform/android/display_server_android.cpp b/platform/android/display_server_android.cpp index d3bce12de1..967f5c7dae 100644 --- a/platform/android/display_server_android.cpp +++ b/platform/android/display_server_android.cpp @@ -321,6 +321,11 @@ int64_t DisplayServerAndroid::window_get_native_handle(HandleType p_handle_type, case WINDOW_VIEW: { return 0; // Not supported. } +#ifdef GLES3_ENABLED + case OPENGL_CONTEXT: { + return eglGetCurrentContext(); + } +#endif default: { return 0; } @@ -440,8 +445,8 @@ Vector<String> DisplayServerAndroid::get_rendering_drivers_func() { return drivers; } -DisplayServer *DisplayServerAndroid::create_func(const String &p_rendering_driver, DisplayServer::WindowMode p_mode, DisplayServer::VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i &p_resolution, Error &r_error) { - DisplayServer *ds = memnew(DisplayServerAndroid(p_rendering_driver, p_mode, p_vsync_mode, p_flags, p_resolution, r_error)); +DisplayServer *DisplayServerAndroid::create_func(const String &p_rendering_driver, DisplayServer::WindowMode p_mode, DisplayServer::VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i *p_position, const Vector2i &p_resolution, Error &r_error) { + DisplayServer *ds = memnew(DisplayServerAndroid(p_rendering_driver, p_mode, p_vsync_mode, p_flags, p_position, p_resolution, r_error)); if (r_error != OK) { OS::get_singleton()->alert("Your video card driver does not support any of the supported Vulkan versions.", "Unable to initialize Video driver"); } @@ -463,7 +468,7 @@ void DisplayServerAndroid::reset_window() { context_vulkan->window_destroy(MAIN_WINDOW_ID); Size2i display_size = OS_Android::get_singleton()->get_display_size(); - if (context_vulkan->window_create(native_window, last_vsync_mode, display_size.width, display_size.height) == -1) { + if (context_vulkan->window_create(native_window, last_vsync_mode, display_size.width, display_size.height) != OK) { memdelete(context_vulkan); context_vulkan = nullptr; ERR_FAIL_MSG("Failed to reset Vulkan window."); @@ -485,7 +490,7 @@ void DisplayServerAndroid::notify_surface_changed(int p_width, int p_height) { rect_changed_callback.callp(reinterpret_cast<const Variant **>(&sizep), 1, ret, ce); } -DisplayServerAndroid::DisplayServerAndroid(const String &p_rendering_driver, DisplayServer::WindowMode p_mode, DisplayServer::VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i &p_resolution, Error &r_error) { +DisplayServerAndroid::DisplayServerAndroid(const String &p_rendering_driver, DisplayServer::WindowMode p_mode, DisplayServer::VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i *p_position, const Vector2i &p_resolution, Error &r_error) { rendering_driver = p_rendering_driver; // TODO: rendering_driver is broken, change when different drivers are supported again @@ -529,7 +534,7 @@ DisplayServerAndroid::DisplayServerAndroid(const String &p_rendering_driver, Dis } Size2i display_size = OS_Android::get_singleton()->get_display_size(); - if (context_vulkan->window_create(native_window, p_vsync_mode, display_size.width, display_size.height) == -1) { + if (context_vulkan->window_create(native_window, p_vsync_mode, display_size.width, display_size.height) != OK) { memdelete(context_vulkan); context_vulkan = nullptr; ERR_FAIL_MSG("Failed to create Vulkan window."); @@ -580,6 +585,9 @@ void DisplayServerAndroid::process_gyroscope(const Vector3 &p_gyroscope) { } void DisplayServerAndroid::mouse_set_mode(MouseMode p_mode) { + if (!OS_Android::get_singleton()->get_godot_java()->get_godot_view()->can_update_pointer_icon() || !OS_Android::get_singleton()->get_godot_java()->get_godot_view()->can_capture_pointer()) { + return; + } if (mouse_mode == p_mode) { return; } @@ -612,6 +620,9 @@ MouseButton DisplayServerAndroid::mouse_get_button_state() const { } void DisplayServerAndroid::cursor_set_shape(DisplayServer::CursorShape p_shape) { + if (!OS_Android::get_singleton()->get_godot_java()->get_godot_view()->can_update_pointer_icon()) { + return; + } if (cursor_shape == p_shape) { return; } diff --git a/platform/android/display_server_android.h b/platform/android/display_server_android.h index 6e14ba3e23..a6bc88e048 100644 --- a/platform/android/display_server_android.h +++ b/platform/android/display_server_android.h @@ -194,7 +194,7 @@ public: virtual void mouse_set_mode(MouseMode p_mode) override; virtual MouseMode mouse_get_mode() const override; - static DisplayServer *create_func(const String &p_rendering_driver, WindowMode p_mode, DisplayServer::VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i &p_resolution, Error &r_error); + static DisplayServer *create_func(const String &p_rendering_driver, WindowMode p_mode, DisplayServer::VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i *p_position, const Vector2i &p_resolution, Error &r_error); static Vector<String> get_rendering_drivers_func(); static void register_android_driver(); @@ -204,7 +204,7 @@ public: virtual Point2i mouse_get_position() const override; virtual MouseButton mouse_get_button_state() const override; - DisplayServerAndroid(const String &p_rendering_driver, WindowMode p_mode, DisplayServer::VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i &p_resolution, Error &r_error); + DisplayServerAndroid(const String &p_rendering_driver, WindowMode p_mode, DisplayServer::VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i *p_position, const Vector2i &p_resolution, Error &r_error); ~DisplayServerAndroid(); }; diff --git a/platform/android/export/export_plugin.cpp b/platform/android/export/export_plugin.cpp index 366bd1c48c..c3fba625c6 100644 --- a/platform/android/export/export_plugin.cpp +++ b/platform/android/export/export_plugin.cpp @@ -401,7 +401,7 @@ void EditorExportPlatformAndroid::_check_for_changes_poll_thread(void *ud) { } } - if (EditorSettings::get_singleton()->get("export/android/shutdown_adb_on_exit")) { + if (EDITOR_GET("export/android/shutdown_adb_on_exit")) { String adb = get_adb_path(); if (!FileAccess::exists(adb)) { return; //adb not configured @@ -419,7 +419,7 @@ String EditorExportPlatformAndroid::get_project_name(const String &p_name) const if (!p_name.is_empty()) { aname = p_name; } else { - aname = ProjectSettings::get_singleton()->get("application/config/name"); + aname = GLOBAL_GET("application/config/name"); } if (aname.is_empty()) { @@ -431,7 +431,7 @@ String EditorExportPlatformAndroid::get_project_name(const String &p_name) const String EditorExportPlatformAndroid::get_package_name(const String &p_package) const { String pname = p_package; - String basename = ProjectSettings::get_singleton()->get("application/config/name"); + String basename = GLOBAL_GET("application/config/name"); basename = basename.to_lower(); String name; @@ -585,12 +585,13 @@ zip_fileinfo EditorExportPlatformAndroid::get_zip_fileinfo() { return zipfi; } -Vector<String> EditorExportPlatformAndroid::get_abis() { - Vector<String> abis; - abis.push_back("armeabi-v7a"); - abis.push_back("arm64-v8a"); - abis.push_back("x86"); - abis.push_back("x86_64"); +Vector<EditorExportPlatformAndroid::ABI> EditorExportPlatformAndroid::get_abis() { + // Should have the same order and size as get_archs. + Vector<ABI> abis; + abis.push_back(ABI("armeabi-v7a", "arm32")); + abis.push_back(ABI("arm64-v8a", "arm64")); + abis.push_back(ABI("x86", "x86_32")); + abis.push_back(ABI("x86_64", "x86_64")); return abis; } @@ -687,14 +688,20 @@ Error EditorExportPlatformAndroid::save_apk_so(void *p_userdata, const SharedObj return FAILED; } APKExportData *ed = static_cast<APKExportData *>(p_userdata); - Vector<String> abis = get_abis(); + Vector<ABI> abis = get_abis(); bool exported = false; for (int i = 0; i < p_so.tags.size(); ++i) { // shared objects can be fat (compatible with multiple ABIs) - int abi_index = abis.find(p_so.tags[i]); + int abi_index = -1; + for (int j = 0; j < abis.size(); ++j) { + if (abis[j].abi == p_so.tags[i] || abis[j].arch == p_so.tags[i]) { + abi_index = j; + break; + } + } if (abi_index != -1) { exported = true; - String abi = abis[abi_index]; + String abi = abis[abi_index].abi; String dst_path = String("lib").path_join(abi).path_join(p_so.path.get_file()); Vector<uint8_t> array = FileAccess::get_file_as_array(p_so.path); Error store_err = store_in_apk(ed, dst_path, array); @@ -702,9 +709,7 @@ Error EditorExportPlatformAndroid::save_apk_so(void *p_userdata, const SharedObj } } if (!exported) { - String abis_string = String(" ").join(abis); - String err = "Cannot determine ABI for library \"" + p_so.path + "\". One of the supported ABIs must be used as a tag: " + abis_string; - ERR_PRINT(err); + ERR_PRINT("Cannot determine architecture for library \"" + p_so.path + "\". One of the supported architectures must be used as a tag: " + join_abis(abis, " ", true)); return FAILED; } return OK; @@ -725,16 +730,22 @@ Error EditorExportPlatformAndroid::ignore_apk_file(void *p_userdata, const Strin Error EditorExportPlatformAndroid::copy_gradle_so(void *p_userdata, const SharedObject &p_so) { ERR_FAIL_COND_V_MSG(!p_so.path.get_file().begins_with("lib"), FAILED, "Android .so file names must start with \"lib\", but got: " + p_so.path); - Vector<String> abis = get_abis(); + Vector<ABI> abis = get_abis(); CustomExportData *export_data = static_cast<CustomExportData *>(p_userdata); bool exported = false; for (int i = 0; i < p_so.tags.size(); ++i) { - int abi_index = abis.find(p_so.tags[i]); + int abi_index = -1; + for (int j = 0; j < abis.size(); ++j) { + if (abis[j].abi == p_so.tags[i] || abis[j].arch == p_so.tags[i]) { + abi_index = j; + break; + } + } if (abi_index != -1) { exported = true; String base = "res://android/build/libs"; String type = export_data->debug ? "debug" : "release"; - String abi = abis[abi_index]; + String abi = abis[abi_index].abi; String filename = p_so.path.get_file(); String dst_path = base.path_join(type).path_join(abi).path_join(filename); Vector<uint8_t> data = FileAccess::get_file_as_array(p_so.path); @@ -745,7 +756,7 @@ Error EditorExportPlatformAndroid::copy_gradle_so(void *p_userdata, const Shared } } ERR_FAIL_COND_V_MSG(!exported, FAILED, - "Cannot determine ABI for library \"" + p_so.path + "\". One of the supported ABIs must be used as a tag: " + String(" ").join(abis)); + "Cannot determine architecture for library \"" + p_so.path + "\". One of the supported architectures must be used as a tag:" + join_abis(abis, " ", true)); return OK; } @@ -791,7 +802,7 @@ void EditorExportPlatformAndroid::_get_permissions(const Ref<EditorExportPreset> } void EditorExportPlatformAndroid::_write_tmp_manifest(const Ref<EditorExportPreset> &p_preset, bool p_give_internet, bool p_debug) { - print_verbose("Building temporary manifest.."); + print_verbose("Building temporary manifest..."); String manifest_text = "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" "<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n" @@ -1395,7 +1406,7 @@ void EditorExportPlatformAndroid::_fix_resources(const Ref<EditorExportPreset> & Vector<String> string_table; String package_name = p_preset->get("package/name"); - Dictionary appnames = ProjectSettings::get_singleton()->get("application/config/name_localized"); + Dictionary appnames = GLOBAL_GET("application/config/name_localized"); for (uint32_t i = 0; i < string_count; i++) { uint32_t offset = decode_uint32(&r_manifest[string_table_begins + i * 4]); @@ -1505,9 +1516,9 @@ void EditorExportPlatformAndroid::_process_launcher_icons(const String &p_file_n } String EditorExportPlatformAndroid::load_splash_refs(Ref<Image> &splash_image, Ref<Image> &splash_bg_color_image) { - bool scale_splash = ProjectSettings::get_singleton()->get("application/boot_splash/fullsize"); - bool apply_filter = ProjectSettings::get_singleton()->get("application/boot_splash/use_filter"); - String project_splash_path = ProjectSettings::get_singleton()->get("application/boot_splash/image"); + bool scale_splash = GLOBAL_GET("application/boot_splash/fullsize"); + bool apply_filter = GLOBAL_GET("application/boot_splash/use_filter"); + String project_splash_path = GLOBAL_GET("application/boot_splash/image"); if (!project_splash_path.is_empty()) { splash_image.instantiate(); @@ -1528,7 +1539,7 @@ String EditorExportPlatformAndroid::load_splash_refs(Ref<Image> &splash_image, R } if (scale_splash) { - Size2 screen_size = Size2(ProjectSettings::get_singleton()->get("display/window/size/viewport_width"), ProjectSettings::get_singleton()->get("display/window/size/viewport_height")); + Size2 screen_size = Size2(GLOBAL_GET("display/window/size/viewport_width"), GLOBAL_GET("display/window/size/viewport_height")); int width, height; if (screen_size.width > screen_size.height) { // scale horizontally @@ -1551,7 +1562,7 @@ String EditorExportPlatformAndroid::load_splash_refs(Ref<Image> &splash_image, R print_verbose("Creating splash background color image."); splash_bg_color_image.instantiate(); - splash_bg_color_image->create(splash_image->get_width(), splash_image->get_height(), false, splash_image->get_format()); + splash_bg_color_image->initialize_data(splash_image->get_width(), splash_image->get_height(), false, splash_image->get_format()); splash_bg_color_image->fill(bg_color); String processed_splash_config_xml = vformat(SPLASH_CONFIG_XML_CONTENT, bool_to_string(apply_filter)); @@ -1559,7 +1570,7 @@ String EditorExportPlatformAndroid::load_splash_refs(Ref<Image> &splash_image, R } void EditorExportPlatformAndroid::load_icon_refs(const Ref<EditorExportPreset> &p_preset, Ref<Image> &icon, Ref<Image> &foreground, Ref<Image> &background) { - String project_icon_path = ProjectSettings::get_singleton()->get("application/config/icon"); + String project_icon_path = GLOBAL_GET("application/config/icon"); icon.instantiate(); foreground.instantiate(); @@ -1656,11 +1667,11 @@ void EditorExportPlatformAndroid::_copy_icons_to_gradle_project(const Ref<Editor } } -Vector<String> EditorExportPlatformAndroid::get_enabled_abis(const Ref<EditorExportPreset> &p_preset) { - Vector<String> abis = get_abis(); - Vector<String> enabled_abis; +Vector<EditorExportPlatformAndroid::ABI> EditorExportPlatformAndroid::get_enabled_abis(const Ref<EditorExportPreset> &p_preset) { + Vector<ABI> abis = get_abis(); + Vector<ABI> enabled_abis; for (int i = 0; i < abis.size(); ++i) { - bool is_enabled = p_preset->get("architectures/" + abis[i]); + bool is_enabled = p_preset->get("architectures/" + abis[i].abi); if (is_enabled) { enabled_abis.push_back(abis[i]); } @@ -1671,9 +1682,10 @@ Vector<String> EditorExportPlatformAndroid::get_enabled_abis(const Ref<EditorExp void EditorExportPlatformAndroid::get_preset_features(const Ref<EditorExportPreset> &p_preset, List<String> *r_features) const { r_features->push_back("etc2"); - Vector<String> abis = get_enabled_abis(p_preset); + Vector<ABI> abis = get_enabled_abis(p_preset); for (int i = 0; i < abis.size(); ++i) { - r_features->push_back(abis[i]); + r_features->push_back(abis[i].arch); + r_features->push_back(abis[i].abi); } } @@ -1697,9 +1709,9 @@ void EditorExportPlatformAndroid::get_export_options(List<ExportOption> *r_optio // Android supports multiple architectures in an app bundle, so // we expose each option as a checkbox in the export dialog. - const Vector<String> abis = get_abis(); + const Vector<ABI> abis = get_abis(); for (int i = 0; i < abis.size(); ++i) { - const String abi = abis[i]; + const String abi = abis[i].abi; // All Android devices supporting Vulkan run 64-bit Android, // so there is usually no point in exporting for 32-bit Android. const bool is_default = abi == "arm64-v8a"; @@ -1920,7 +1932,7 @@ Error EditorExportPlatformAndroid::run(const Ref<EditorExportPreset> &p_preset, print_verbose(output); if (p_debug_flags & DEBUG_FLAG_REMOTE_DEBUG) { - int dbg_port = EditorSettings::get_singleton()->get("network/debug/remote_port"); + int dbg_port = EDITOR_GET("network/debug/remote_port"); args.clear(); args.push_back("-s"); args.push_back(devices[p_device].id); @@ -1935,7 +1947,7 @@ Error EditorExportPlatformAndroid::run(const Ref<EditorExportPreset> &p_preset, } if (p_debug_flags & DEBUG_FLAG_DUMB_CLIENT) { - int fs_port = EditorSettings::get_singleton()->get("filesystem/file_server/port"); + int fs_port = EDITOR_GET("filesystem/file_server/port"); args.clear(); args.push_back("-s"); @@ -1965,7 +1977,7 @@ Error EditorExportPlatformAndroid::run(const Ref<EditorExportPreset> &p_preset, args.push_back("shell"); args.push_back("am"); args.push_back("start"); - if ((bool)EditorSettings::get_singleton()->get("export/android/force_system_user") && devices[p_device].api_level >= 17) { // Multi-user introduced in Android 17 + if ((bool)EDITOR_GET("export/android/force_system_user") && devices[p_device].api_level >= 17) { // Multi-user introduced in Android 17 args.push_back("--user"); args.push_back("0"); } @@ -1995,7 +2007,7 @@ String EditorExportPlatformAndroid::get_adb_path() { if (OS::get_singleton()->get_name() == "Windows") { exe_ext = ".exe"; } - String sdk_path = EditorSettings::get_singleton()->get("export/android/android_sdk_path"); + String sdk_path = EDITOR_GET("export/android/android_sdk_path"); return sdk_path.path_join("platform-tools/adb" + exe_ext); } @@ -2005,7 +2017,7 @@ String EditorExportPlatformAndroid::get_apksigner_path() { exe_ext = ".bat"; } String apksigner_command_name = "apksigner" + exe_ext; - String sdk_path = EditorSettings::get_singleton()->get("export/android/android_sdk_path"); + String sdk_path = EDITOR_GET("export/android/android_sdk_path"); String apksigner_path = ""; Error errn; @@ -2099,7 +2111,7 @@ bool EditorExportPlatformAndroid::has_valid_export_configuration(const Ref<Edito } if (!FileAccess::exists(dk)) { - dk = EditorSettings::get_singleton()->get("export/android/debug_keystore"); + dk = EDITOR_GET("export/android/debug_keystore"); if (!FileAccess::exists(dk)) { valid = false; err += TTR("Debug keystore not configured in the Editor Settings nor in the preset.") + "\n"; @@ -2120,7 +2132,7 @@ bool EditorExportPlatformAndroid::has_valid_export_configuration(const Ref<Edito err += TTR("Release keystore incorrectly configured in the export preset.") + "\n"; } - String sdk_path = EditorSettings::get_singleton()->get("export/android/android_sdk_path"); + String sdk_path = EDITOR_GET("export/android/android_sdk_path"); if (sdk_path.is_empty()) { err += TTR("A valid Android SDK path is required in Editor Settings.") + "\n"; valid = false; @@ -2394,9 +2406,9 @@ Error EditorExportPlatformAndroid::sign_apk(const Ref<EditorExportPreset> &p_pre user = p_preset->get("keystore/debug_user"); if (keystore.is_empty()) { - keystore = EditorSettings::get_singleton()->get("export/android/debug_keystore"); - password = EditorSettings::get_singleton()->get("export/android/debug_keystore_pass"); - user = EditorSettings::get_singleton()->get("export/android/debug_keystore_user"); + keystore = EDITOR_GET("export/android/debug_keystore"); + password = EDITOR_GET("export/android/debug_keystore_pass"); + user = EDITOR_GET("export/android/debug_keystore_user"); } if (ep.step(vformat(TTR("Signing debug %s..."), export_label), 104)) { @@ -2479,7 +2491,7 @@ void EditorExportPlatformAndroid::_clear_assets_directory() { // Clear the APK assets directory if (da_res->dir_exists(APK_ASSETS_DIRECTORY)) { - print_verbose("Clearing APK assets directory.."); + print_verbose("Clearing APK assets directory..."); Ref<DirAccess> da_assets = DirAccess::open(APK_ASSETS_DIRECTORY); da_assets->erase_contents_recursive(); da_res->remove(APK_ASSETS_DIRECTORY); @@ -2487,7 +2499,7 @@ void EditorExportPlatformAndroid::_clear_assets_directory() { // Clear the AAB assets directory if (da_res->dir_exists(AAB_ASSETS_DIRECTORY)) { - print_verbose("Clearing AAB assets directory.."); + print_verbose("Clearing AAB assets directory..."); Ref<DirAccess> da_assets = DirAccess::open(AAB_ASSETS_DIRECTORY); da_assets->erase_contents_recursive(); da_res->remove(AAB_ASSETS_DIRECTORY); @@ -2516,13 +2528,24 @@ void EditorExportPlatformAndroid::_remove_copied_libs() { da->remove(GDNATIVE_LIBS_PATH); } -String EditorExportPlatformAndroid::join_list(List<String> parts, const String &separator) const { +String EditorExportPlatformAndroid::join_list(const List<String> &p_parts, const String &p_separator) { String ret; - for (int i = 0; i < parts.size(); ++i) { + for (int i = 0; i < p_parts.size(); ++i) { if (i > 0) { - ret += separator; + ret += p_separator; } - ret += parts[i]; + ret += p_parts[i]; + } + return ret; +} + +String EditorExportPlatformAndroid::join_abis(const Vector<EditorExportPlatformAndroid::ABI> &p_parts, const String &p_separator, bool p_use_arch) { + String ret; + for (int i = 0; i < p_parts.size(); ++i) { + if (i > 0) { + ret += p_separator; + } + ret += (p_use_arch) ? p_parts[i].arch : p_parts[i].abi; } return ret; } @@ -2544,7 +2567,7 @@ Error EditorExportPlatformAndroid::export_project_helper(const Ref<EditorExportP bool use_custom_build = bool(p_preset->get("custom_build/use_custom_build")); bool p_give_internet = p_flags & (DEBUG_FLAG_DUMB_CLIENT | DEBUG_FLAG_REMOTE_DEBUG); bool apk_expansion = p_preset->get("apk_expansion/enable"); - Vector<String> enabled_abis = get_enabled_abis(p_preset); + Vector<ABI> enabled_abis = get_enabled_abis(p_preset); print_verbose("Exporting for Android..."); print_verbose("- debug build: " + bool_to_string(p_debug)); @@ -2553,7 +2576,7 @@ Error EditorExportPlatformAndroid::export_project_helper(const Ref<EditorExportP print_verbose("- sign build: " + bool_to_string(should_sign)); print_verbose("- custom build enabled: " + bool_to_string(use_custom_build)); print_verbose("- apk expansion enabled: " + bool_to_string(apk_expansion)); - print_verbose("- enabled abis: " + String(",").join(enabled_abis)); + print_verbose("- enabled abis: " + join_abis(enabled_abis, ",", false)); print_verbose("- export filter: " + itos(p_preset->get_export_filter())); print_verbose("- include filter: " + p_preset->get_include_filter()); print_verbose("- exclude filter: " + p_preset->get_exclude_filter()); @@ -2592,10 +2615,10 @@ Error EditorExportPlatformAndroid::export_project_helper(const Ref<EditorExportP } if (use_custom_build) { - print_verbose("Starting custom build.."); + print_verbose("Starting custom build..."); //test that installed build version is alright { - print_verbose("Checking build version.."); + print_verbose("Checking build version..."); Ref<FileAccess> f = FileAccess::open("res://android/.build_version", FileAccess::READ); if (f.is_null()) { add_message(EXPORT_MESSAGE_ERROR, TTR("Export"), TTR("Trying to build from a custom built template, but no version info for it exists. Please reinstall from the 'Project' menu.")); @@ -2628,7 +2651,7 @@ Error EditorExportPlatformAndroid::export_project_helper(const Ref<EditorExportP _clear_assets_directory(); _remove_copied_libs(); if (!apk_expansion) { - print_verbose("Exporting project files.."); + print_verbose("Exporting project files..."); CustomExportData user_data; user_data.assets_directory = assets_directory; user_data.debug = p_debug; @@ -2639,18 +2662,17 @@ Error EditorExportPlatformAndroid::export_project_helper(const Ref<EditorExportP } if (user_data.libs.size() > 0) { Ref<FileAccess> fa = FileAccess::open(GDNATIVE_LIBS_PATH, FileAccess::WRITE); - JSON json; - fa->store_string(json.stringify(user_data.libs, "\t")); + fa->store_string(JSON::stringify(user_data.libs, "\t")); } } else { - print_verbose("Saving apk expansion file.."); + print_verbose("Saving apk expansion file..."); err = save_apk_expansion_file(p_preset, p_debug, p_path); if (err != OK) { add_message(EXPORT_MESSAGE_ERROR, TTR("Export"), TTR("Could not write expansion package file!")); return err; } } - print_verbose("Storing command line flags.."); + print_verbose("Storing command line flags..."); store_file_at_path(assets_directory + "/_cl_", command_line_flags); print_verbose("Updating ANDROID_HOME environment to " + sdk_path); @@ -2677,7 +2699,7 @@ Error EditorExportPlatformAndroid::export_project_helper(const Ref<EditorExportP if (!target_sdk_version.is_valid_int()) { target_sdk_version = itos(DEFAULT_TARGET_SDK_VERSION); } - String enabled_abi_string = String("|").join(enabled_abis); + String enabled_abi_string = join_abis(enabled_abis, "|", false); String sign_flag = should_sign ? "true" : "false"; String zipalign_flag = "true"; @@ -2729,9 +2751,9 @@ Error EditorExportPlatformAndroid::export_project_helper(const Ref<EditorExportP String debug_user = p_preset->get("keystore/debug_user"); if (debug_keystore.is_empty()) { - debug_keystore = EditorSettings::get_singleton()->get("export/android/debug_keystore"); - debug_password = EditorSettings::get_singleton()->get("export/android/debug_keystore_pass"); - debug_user = EditorSettings::get_singleton()->get("export/android/debug_keystore_user"); + debug_keystore = EDITOR_GET("export/android/debug_keystore"); + debug_password = EDITOR_GET("export/android/debug_keystore_pass"); + debug_user = EDITOR_GET("export/android/debug_keystore_user"); } if (debug_keystore.is_relative_path()) { debug_keystore = OS::get_singleton()->get_resource_dir().path_join(debug_keystore).simplify_path(); @@ -2803,7 +2825,7 @@ Error EditorExportPlatformAndroid::export_project_helper(const Ref<EditorExportP return OK; } // This is the start of the Legacy build system - print_verbose("Starting legacy build system.."); + print_verbose("Starting legacy build system..."); if (p_debug) { src_apk = p_preset->get("custom_template/debug"); } else { @@ -2862,7 +2884,7 @@ Error EditorExportPlatformAndroid::export_project_helper(const Ref<EditorExportP String apk_expansion_pkey = p_preset->get("apk_expansion/public_key"); - Vector<String> invalid_abis(enabled_abis); + Vector<ABI> invalid_abis(enabled_abis); while (ret == UNZ_OK) { //get filename unz_file_info info; @@ -2902,20 +2924,22 @@ Error EditorExportPlatformAndroid::export_project_helper(const Ref<EditorExportP _load_image_data(splash_bg_color_image, data); } - for (int i = 0; i < icon_densities_count; ++i) { - if (main_image.is_valid() && !main_image->is_empty()) { - if (file == launcher_icons[i].export_path) { - _process_launcher_icons(file, main_image, launcher_icons[i].dimensions, data); + if (file.ends_with(".png") && file.contains("mipmap")) { + for (int i = 0; i < icon_densities_count; ++i) { + if (main_image.is_valid() && !main_image->is_empty()) { + if (file == launcher_icons[i].export_path) { + _process_launcher_icons(file, main_image, launcher_icons[i].dimensions, data); + } } - } - if (foreground.is_valid() && !foreground->is_empty()) { - if (file == launcher_adaptive_icon_foregrounds[i].export_path) { - _process_launcher_icons(file, foreground, launcher_adaptive_icon_foregrounds[i].dimensions, data); + if (foreground.is_valid() && !foreground->is_empty()) { + if (file == launcher_adaptive_icon_foregrounds[i].export_path) { + _process_launcher_icons(file, foreground, launcher_adaptive_icon_foregrounds[i].dimensions, data); + } } - } - if (background.is_valid() && !background->is_empty()) { - if (file == launcher_adaptive_icon_backgrounds[i].export_path) { - _process_launcher_icons(file, background, launcher_adaptive_icon_backgrounds[i].dimensions, data); + if (background.is_valid() && !background->is_empty()) { + if (file == launcher_adaptive_icon_backgrounds[i].export_path) { + _process_launcher_icons(file, background, launcher_adaptive_icon_backgrounds[i].dimensions, data); + } } } } @@ -2923,7 +2947,7 @@ Error EditorExportPlatformAndroid::export_project_helper(const Ref<EditorExportP if (file.ends_with(".so")) { bool enabled = false; for (int i = 0; i < enabled_abis.size(); ++i) { - if (file.begins_with("lib/" + enabled_abis[i] + "/")) { + if (file.begins_with("lib/" + enabled_abis[i].abi + "/")) { invalid_abis.erase(enabled_abis[i]); enabled = true; break; @@ -2965,8 +2989,7 @@ Error EditorExportPlatformAndroid::export_project_helper(const Ref<EditorExportP } if (!invalid_abis.is_empty()) { - String unsupported_arch = String(", ").join(invalid_abis); - add_message(EXPORT_MESSAGE_ERROR, TTR("Export"), vformat(TTR("Missing libraries in the export template for the selected architectures: %s. Please build a template with all required libraries, or uncheck the missing architectures in the export preset."), unsupported_arch)); + add_message(EXPORT_MESSAGE_ERROR, TTR("Export"), vformat(TTR("Missing libraries in the export template for the selected architectures: %s. Please build a template with all required libraries, or uncheck the missing architectures in the export preset."), join_abis(invalid_abis, ", ", false))); CLEANUP_AND_RETURN(ERR_FILE_NOT_FOUND); } diff --git a/platform/android/export/export_plugin.h b/platform/android/export/export_plugin.h index 46012bd46c..c8fcb761fe 100644 --- a/platform/android/export/export_plugin.h +++ b/platform/android/export/export_plugin.h @@ -99,7 +99,22 @@ class EditorExportPlatformAndroid : public EditorExportPlatform { static zip_fileinfo get_zip_fileinfo(); - static Vector<String> get_abis(); + struct ABI { + String abi; + String arch; + + bool operator==(const ABI &p_a) const { + return p_a.abi == abi; + } + + ABI(const String &p_abi, const String &p_arch) { + abi = p_abi; + arch = p_arch; + } + ABI() {} + }; + + static Vector<ABI> get_abis(); /// List the gdap files in the directory specified by the p_path parameter. static Vector<String> list_gdap_files(const String &p_path); @@ -152,7 +167,7 @@ class EditorExportPlatformAndroid : public EditorExportPlatform { const Ref<Image> &foreground, const Ref<Image> &background); - static Vector<String> get_enabled_abis(const Ref<EditorExportPreset> &p_preset); + static Vector<ABI> get_enabled_abis(const Ref<EditorExportPreset> &p_preset); public: typedef Error (*EditorExportSaveFunction)(void *p_userdata, const String &p_path, const Vector<uint8_t> &p_data, int p_file, int p_total, const Vector<String> &p_enc_in_filters, const Vector<String> &p_enc_ex_filters, const Vector<uint8_t> &p_key); @@ -228,7 +243,8 @@ public: void _remove_copied_libs(); - String join_list(List<String> parts, const String &separator) const; + static String join_list(const List<String> &p_parts, const String &p_separator); + static String join_abis(const Vector<ABI> &p_parts, const String &p_separator, bool p_use_arch); virtual Error export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags = 0) override; diff --git a/platform/android/export/gradle_export_util.cpp b/platform/android/export/gradle_export_util.cpp index 2f53942f76..8d016d3fac 100644 --- a/platform/android/export/gradle_export_util.cpp +++ b/platform/android/export/gradle_export_util.cpp @@ -158,7 +158,7 @@ Error _create_project_name_strings_files(const Ref<EditorExportPreset> &p_preset return ERR_CANT_OPEN; } da->list_dir_begin(); - Dictionary appnames = ProjectSettings::get_singleton()->get("application/config/name_localized"); + Dictionary appnames = GLOBAL_GET("application/config/name_localized"); while (true) { String file = da->get_next(); if (file.is_empty()) { diff --git a/platform/android/java/app/config.gradle b/platform/android/java/app/config.gradle index 0346625e4b..e1d9dc4cde 100644 --- a/platform/android/java/app/config.gradle +++ b/platform/android/java/app/config.gradle @@ -1,10 +1,10 @@ ext.versions = [ - androidGradlePlugin: '7.0.3', + androidGradlePlugin: '7.2.1', compileSdk : 32, - minSdk : 19, // Also update 'platform/android/java/lib/AndroidManifest.xml#minSdkVersion' & 'platform/android/export/export_plugin.cpp#DEFAULT_MIN_SDK_VERSION' - targetSdk : 32, // Also update 'platform/android/java/lib/AndroidManifest.xml#targetSdkVersion' & 'platform/android/export/export_plugin.cpp#DEFAULT_TARGET_SDK_VERSION' + minSdk : 19, // Also update 'platform/android/export/export_plugin.cpp#DEFAULT_MIN_SDK_VERSION' + targetSdk : 32, // Also update 'platform/android/export/export_plugin.cpp#DEFAULT_TARGET_SDK_VERSION' buildTools : '32.0.0', - kotlinVersion : '1.6.21', + kotlinVersion : '1.7.0', fragmentVersion : '1.3.6', nexusPublishVersion: '1.1.0', javaVersion : 11, diff --git a/platform/android/java/build.gradle b/platform/android/java/build.gradle index 81c7130c03..5a91e5ce32 100644 --- a/platform/android/java/build.gradle +++ b/platform/android/java/build.gradle @@ -29,8 +29,13 @@ allprojects { ext { supportedAbis = ["arm32", "arm64", "x86_32", "x86_64"] - supportedTargetsMap = [release: "release", dev: "debug", debug: "release_debug"] supportedFlavors = ["editor", "template"] + supportedFlavorsBuildTypes = [ + // The editor can't be used with target=release as debugging tools are then not + // included, and it would crash on errors instead of reporting them. + "editor": ["dev", "debug"], + "template": ["dev", "debug", "release"] + ] // Used by gradle to specify which architecture to build for by default when running // `./gradlew build` (this command is usually used by Android Studio). @@ -88,7 +93,7 @@ task copyDebugAARToAppModule(type: Copy) { dependsOn ':lib:assembleTemplateDebug' from('lib/build/outputs/aar') into('app/libs/debug') - include('godot-lib.debug.aar') + include('godot-lib.template_debug.aar') } /** @@ -99,7 +104,7 @@ task copyDebugAARToBin(type: Copy) { dependsOn ':lib:assembleTemplateDebug' from('lib/build/outputs/aar') into(binDir) - include('godot-lib.debug.aar') + include('godot-lib.template_debug.aar') } /** @@ -110,7 +115,7 @@ task copyDevAARToAppModule(type: Copy) { dependsOn ':lib:assembleTemplateDev' from('lib/build/outputs/aar') into('app/libs/dev') - include('godot-lib.dev.aar') + include('godot-lib.template_debug.dev.aar') } /** @@ -121,7 +126,7 @@ task copyDevAARToBin(type: Copy) { dependsOn ':lib:assembleTemplateDev' from('lib/build/outputs/aar') into(binDir) - include('godot-lib.dev.aar') + include('godot-lib.template_debug.dev.aar') } /** @@ -132,7 +137,7 @@ task copyReleaseAARToAppModule(type: Copy) { dependsOn ':lib:assembleTemplateRelease' from('lib/build/outputs/aar') into('app/libs/release') - include('godot-lib.release.aar') + include('godot-lib.template_release.aar') } /** @@ -143,7 +148,7 @@ task copyReleaseAARToBin(type: Copy) { dependsOn ':lib:assembleTemplateRelease' from('lib/build/outputs/aar') into(binDir) - include('godot-lib.release.aar') + include('godot-lib.template_release.aar') } /** @@ -168,13 +173,8 @@ def templateExcludedBuildTask() { if (!isAndroidStudio()) { logger.lifecycle("Excluding Android studio build tasks") for (String flavor : supportedFlavors) { - for (String buildType : supportedTargetsMap.keySet()) { - if (buildType == "release" && flavor == "editor") { - // The editor can't be used with target=release as debugging tools are then not - // included, and it would crash on errors instead of reporting them. - continue - } - + String[] supportedBuildTypes = supportedFlavorsBuildTypes[flavor] + for (String buildType : supportedBuildTypes) { for (String abi : selectedAbis) { excludedTasks += ":lib:" + getSconsTaskName(flavor, buildType, abi) } @@ -188,7 +188,7 @@ def templateBuildTasks() { def tasks = [] // Only build the apks and aar files for which we have native shared libraries. - for (String target : supportedTargetsMap.keySet()) { + for (String target : supportedFlavorsBuildTypes["template"]) { File targetLibs = new File("lib/libs/" + target) if (targetLibs != null && targetLibs.isDirectory() @@ -240,12 +240,7 @@ task generateGodotEditor { def tasks = [] - for (String target : supportedTargetsMap.keySet()) { - if (target == "release") { - // The editor can't be used with target=release as debugging tools are then not - // included, and it would crash on errors instead of reporting them. - continue - } + for (String target : supportedFlavorsBuildTypes["editor"]) { File targetLibs = new File("lib/libs/tools/" + target) if (targetLibs != null && targetLibs.isDirectory() @@ -281,6 +276,11 @@ task generateDevTemplate { finalizedBy 'zipCustomBuild' } +task clean(type: Delete) { + dependsOn 'cleanGodotEditor' + dependsOn 'cleanGodotTemplates' +} + /** * Clean the generated editor artifacts. */ @@ -297,8 +297,6 @@ task cleanGodotEditor(type: Delete) { // Delete the Godot editor apks in the Godot bin directory delete("$binDir/android_editor.apk") delete("$binDir/android_editor_dev.apk") - - finalizedBy getTasksByName("clean", true) } /** @@ -322,9 +320,12 @@ task cleanGodotTemplates(type: Delete) { delete("$binDir/android_dev.apk") delete("$binDir/android_release.apk") delete("$binDir/android_source.zip") + delete("$binDir/godot-lib.template_debug.aar") + delete("$binDir/godot-lib.template_debug.dev.aar") + delete("$binDir/godot-lib.template_release.aar") + + // Cover deletion for the libs using the previous naming scheme delete("$binDir/godot-lib.debug.aar") delete("$binDir/godot-lib.dev.aar") delete("$binDir/godot-lib.release.aar") - - finalizedBy getTasksByName("clean", true) } diff --git a/platform/android/java/gradle/wrapper/gradle-wrapper.properties b/platform/android/java/gradle/wrapper/gradle-wrapper.properties index ffed3a254e..41dfb87909 100644 --- a/platform/android/java/gradle/wrapper/gradle-wrapper.properties +++ b/platform/android/java/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.4-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/platform/android/java/lib/AndroidManifest.xml b/platform/android/java/lib/AndroidManifest.xml index 79b5aadf2a..1f77e2fc34 100644 --- a/platform/android/java/lib/AndroidManifest.xml +++ b/platform/android/java/lib/AndroidManifest.xml @@ -4,9 +4,6 @@ android:versionCode="1" android:versionName="1.0"> - <!-- Should match the mindSdk and targetSdk values in platform/android/java/app/config.gradle --> - <uses-sdk android:minSdkVersion="19" android:targetSdkVersion="32" /> - <application> <!-- Records the version of the Godot library --> diff --git a/platform/android/java/lib/build.gradle b/platform/android/java/lib/build.gradle index 318ae1143f..841656a240 100644 --- a/platform/android/java/lib/build.gradle +++ b/platform/android/java/lib/build.gradle @@ -100,25 +100,34 @@ android { throw new GradleException("Invalid product flavor: $flavorName") } - boolean toolsFlag = flavorName == "editor" - def buildType = variant.buildType.name - if (buildType == null || buildType == "" || !supportedTargetsMap.containsKey(buildType)) { + if (buildType == null || buildType == "" || !supportedFlavorsBuildTypes[flavorName].contains(buildType)) { throw new GradleException("Invalid build type: $buildType") } - def sconsTarget = supportedTargetsMap[buildType] - if (sconsTarget == null || sconsTarget == "") { - throw new GradleException("Invalid scons target: $sconsTarget") + boolean devBuild = buildType == "dev" + + def sconsTarget = flavorName + if (sconsTarget == "template") { + switch (buildType) { + case "release": + sconsTarget += "_release" + break + case "debug": + case "dev": + default: + sconsTarget += "_debug" + break; + } } // Update the name of the generated library - def outputSuffix = "${buildType}.aar" - if (toolsFlag) { - outputSuffix = "tools.$outputSuffix" + def outputSuffix = "${sconsTarget}" + if (devBuild) { + outputSuffix = "${outputSuffix}.dev" } variant.outputs.all { output -> - output.outputFileName = "godot-lib.${outputSuffix}" + output.outputFileName = "godot-lib.${outputSuffix}.aar" } // Find scons' executable path @@ -159,7 +168,7 @@ android { def taskName = getSconsTaskName(flavorName, buildType, selectedAbi) tasks.create(name: taskName, type: Exec) { executable sconsExecutableFile.absolutePath - args "--directory=${pathToRootDir}", "platform=android", "tools=${toolsFlag}", "target=${sconsTarget}", "arch=${selectedAbi}", "-j" + Runtime.runtime.availableProcessors() + args "--directory=${pathToRootDir}", "platform=android", "dev_mode=${devBuild}", "dev_build=${devBuild}", "target=${sconsTarget}", "arch=${selectedAbi}", "-j" + Runtime.runtime.availableProcessors() } // Schedule the tasks so the generated libs are present before the aar file is packaged. @@ -167,11 +176,10 @@ android { } } - // TODO: Enable when issues with AGP 7.1+ are resolved (https://github.com/GodotVR/godot_openxr/issues/187). -// publishing { -// singleVariant("templateRelease") { -// withSourcesJar() -// withJavadocJar() -// } -// } + publishing { + singleVariant("templateRelease") { + withSourcesJar() + withJavadocJar() + } + } } diff --git a/platform/android/java/lib/src/org/godotengine/godot/GodotGLRenderView.java b/platform/android/java/lib/src/org/godotengine/godot/GodotGLRenderView.java index 513021f1d1..3dfc37f6b0 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/GodotGLRenderView.java +++ b/platform/android/java/lib/src/org/godotengine/godot/GodotGLRenderView.java @@ -74,7 +74,6 @@ public class GodotGLRenderView extends GLSurfaceView implements GodotRenderView private final Godot godot; private final GodotInputHandler inputHandler; private final GodotRenderer godotRenderer; - private PointerIcon pointerIcon; public GodotGLRenderView(Context context, Godot godot, XRMode xrMode, boolean p_use_debug_opengl) { super(context); @@ -84,7 +83,7 @@ public class GodotGLRenderView extends GLSurfaceView implements GodotRenderView this.inputHandler = new GodotInputHandler(this); this.godotRenderer = new GodotRenderer(); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { - pointerIcon = PointerIcon.getSystemIcon(getContext(), PointerIcon.TYPE_DEFAULT); + setPointerIcon(PointerIcon.getSystemIcon(getContext(), PointerIcon.TYPE_DEFAULT)); } init(xrMode, false); } @@ -175,13 +174,16 @@ public class GodotGLRenderView extends GLSurfaceView implements GodotRenderView @Keep public void setPointerIcon(int pointerType) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { - pointerIcon = PointerIcon.getSystemIcon(getContext(), pointerType); + setPointerIcon(PointerIcon.getSystemIcon(getContext(), pointerType)); } } @Override public PointerIcon onResolvePointerIcon(MotionEvent me, int pointerIndex) { - return pointerIcon; + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { + return getPointerIcon(); + } + return super.onResolvePointerIcon(me, pointerIndex); } private void init(XRMode xrMode, boolean translucent) { diff --git a/platform/android/java/lib/src/org/godotengine/godot/GodotLib.java b/platform/android/java/lib/src/org/godotengine/godot/GodotLib.java index 26aad867b1..33896ecb95 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/GodotLib.java +++ b/platform/android/java/lib/src/org/godotengine/godot/GodotLib.java @@ -110,7 +110,7 @@ public class GodotLib { /** * Forward touch events. */ - public static native void dispatchTouchEvent(int event, int pointer, int pointerCount, float[] positions); + public static native void dispatchTouchEvent(int event, int pointer, int pointerCount, float[] positions, boolean doubleTap); /** * Dispatch mouse events diff --git a/platform/android/java/lib/src/org/godotengine/godot/GodotVulkanRenderView.java b/platform/android/java/lib/src/org/godotengine/godot/GodotVulkanRenderView.java index fa6c3280b9..0becf00d93 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/GodotVulkanRenderView.java +++ b/platform/android/java/lib/src/org/godotengine/godot/GodotVulkanRenderView.java @@ -48,7 +48,6 @@ public class GodotVulkanRenderView extends VkSurfaceView implements GodotRenderV private final Godot godot; private final GodotInputHandler mInputHandler; private final VkRenderer mRenderer; - private PointerIcon pointerIcon; public GodotVulkanRenderView(Context context, Godot godot) { super(context); @@ -57,7 +56,7 @@ public class GodotVulkanRenderView extends VkSurfaceView implements GodotRenderV mInputHandler = new GodotInputHandler(this); mRenderer = new VkRenderer(); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { - pointerIcon = PointerIcon.getSystemIcon(getContext(), PointerIcon.TYPE_DEFAULT); + setPointerIcon(PointerIcon.getSystemIcon(getContext(), PointerIcon.TYPE_DEFAULT)); } setFocusableInTouchMode(true); startRenderer(mRenderer); @@ -149,13 +148,16 @@ public class GodotVulkanRenderView extends VkSurfaceView implements GodotRenderV @Keep public void setPointerIcon(int pointerType) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { - pointerIcon = PointerIcon.getSystemIcon(getContext(), pointerType); + setPointerIcon(PointerIcon.getSystemIcon(getContext(), pointerType)); } } @Override public PointerIcon onResolvePointerIcon(MotionEvent me, int pointerIndex) { - return pointerIcon; + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { + return getPointerIcon(); + } + return super.onResolvePointerIcon(me, pointerIndex); } @Override diff --git a/platform/android/java/lib/src/org/godotengine/godot/input/GodotEditText.java b/platform/android/java/lib/src/org/godotengine/godot/input/GodotEditText.java index 7925b54fc4..804dbaf165 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/input/GodotEditText.java +++ b/platform/android/java/lib/src/org/godotengine/godot/input/GodotEditText.java @@ -127,7 +127,9 @@ public class GodotEditText extends EditText { edit.setText(""); edit.append(text); if (msg.arg2 != -1) { - edit.setSelection(msg.arg1, msg.arg2); + int selectionStart = Math.min(msg.arg1, edit.length()); + int selectionEnd = Math.min(msg.arg2, edit.length()); + edit.setSelection(selectionStart, selectionEnd); edit.mInputWrapper.setSelection(true); } else { edit.mInputWrapper.setSelection(false); diff --git a/platform/android/java/lib/src/org/godotengine/godot/input/GodotGestureHandler.kt b/platform/android/java/lib/src/org/godotengine/godot/input/GodotGestureHandler.kt index 9715c31fc1..cde8d7cdae 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/input/GodotGestureHandler.kt +++ b/platform/android/java/lib/src/org/godotengine/godot/input/GodotGestureHandler.kt @@ -55,18 +55,15 @@ internal class GodotGestureHandler : SimpleOnGestureListener(), OnScaleGestureLi */ var panningAndScalingEnabled = false - private var doubleTapInProgress = false + private var nextDownIsDoubleTap = false private var dragInProgress = false private var scaleInProgress = false private var contextClickInProgress = false private var pointerCaptureInProgress = false override fun onDown(event: MotionEvent): Boolean { - // Don't send / register a down event while we're in the middle of a double-tap - if (!doubleTapInProgress) { - // Send the down event - GodotInputHandler.handleMotionEvent(event) - } + GodotInputHandler.handleMotionEvent(event.source, MotionEvent.ACTION_DOWN, event.buttonState, event.x, event.y, nextDownIsDoubleTap) + nextDownIsDoubleTap = false return true } @@ -80,7 +77,7 @@ internal class GodotGestureHandler : SimpleOnGestureListener(), OnScaleGestureLi } private fun contextClickRouter(event: MotionEvent) { - if (scaleInProgress) { + if (scaleInProgress || nextDownIsDoubleTap) { return } @@ -137,40 +134,24 @@ internal class GodotGestureHandler : SimpleOnGestureListener(), OnScaleGestureLi } private fun onActionUp(event: MotionEvent): Boolean { + if (event.actionMasked == MotionEvent.ACTION_CANCEL && pointerCaptureInProgress) { + // Don't dispatch the ACTION_CANCEL while a capture is in progress + return true + } + val sourceMouseRelative = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { event.isFromSource(InputDevice.SOURCE_MOUSE_RELATIVE) } else { false } - when { - pointerCaptureInProgress -> { - return if (event.actionMasked == MotionEvent.ACTION_CANCEL) { - // Don't dispatch the ACTION_CANCEL while a capture is in progress - true - } else { - GodotInputHandler.handleMouseEvent( - MotionEvent.ACTION_UP, - event.buttonState, - event.x, - event.y, - 0f, - 0f, - false, - sourceMouseRelative - ) - pointerCaptureInProgress = false - true - } - } - dragInProgress -> { - GodotInputHandler.handleMotionEvent(event) - dragInProgress = false - return true - } - contextClickInProgress -> { + + if (pointerCaptureInProgress || dragInProgress || contextClickInProgress) { + if (contextClickInProgress || GodotInputHandler.isMouseEvent(event)) { + // This may be an ACTION_BUTTON_RELEASE event which we don't handle, + // so we convert it to an ACTION_UP event. GodotInputHandler.handleMouseEvent( - event.actionMasked, - 0, + MotionEvent.ACTION_UP, + event.buttonState, event.x, event.y, 0f, @@ -178,11 +159,16 @@ internal class GodotGestureHandler : SimpleOnGestureListener(), OnScaleGestureLi false, sourceMouseRelative ) - contextClickInProgress = false - return true + } else { + GodotInputHandler.handleTouchEvent(event) } - else -> return false + pointerCaptureInProgress = false + dragInProgress = false + contextClickInProgress = false + return true } + + return false } private fun onActionMove(event: MotionEvent): Boolean { @@ -209,24 +195,14 @@ internal class GodotGestureHandler : SimpleOnGestureListener(), OnScaleGestureLi override fun onDoubleTapEvent(event: MotionEvent): Boolean { if (event.actionMasked == MotionEvent.ACTION_UP) { - doubleTapInProgress = false + nextDownIsDoubleTap = false + GodotInputHandler.handleMotionEvent(event) } return true } override fun onDoubleTap(event: MotionEvent): Boolean { - doubleTapInProgress = true - val x = event.x - val y = event.y - val buttonMask = - if (GodotInputHandler.isMouseEvent(event)) { - event.buttonState - } else { - MotionEvent.BUTTON_PRIMARY - } - GodotInputHandler.handleMouseEvent(MotionEvent.ACTION_DOWN, buttonMask, x, y, true) - GodotInputHandler.handleMouseEvent(MotionEvent.ACTION_UP, 0, x, y, false) - + nextDownIsDoubleTap = true return true } @@ -255,7 +231,7 @@ internal class GodotGestureHandler : SimpleOnGestureListener(), OnScaleGestureLi val x = terminusEvent.x val y = terminusEvent.y - if (terminusEvent.pointerCount >= 2 && panningAndScalingEnabled) { + if (terminusEvent.pointerCount >= 2 && panningAndScalingEnabled && !pointerCaptureInProgress) { GodotLib.pan(x, y, distanceX / 5f, distanceY / 5f) } else { GodotInputHandler.handleMotionEvent(terminusEvent) @@ -264,7 +240,7 @@ internal class GodotGestureHandler : SimpleOnGestureListener(), OnScaleGestureLi } override fun onScale(detector: ScaleGestureDetector?): Boolean { - if (detector == null || !panningAndScalingEnabled) { + if (detector == null || !panningAndScalingEnabled || pointerCaptureInProgress) { return false } GodotLib.magnify( @@ -276,7 +252,7 @@ internal class GodotGestureHandler : SimpleOnGestureListener(), OnScaleGestureLi } override fun onScaleBegin(detector: ScaleGestureDetector?): Boolean { - if (detector == null || !panningAndScalingEnabled) { + if (detector == null || !panningAndScalingEnabled || pointerCaptureInProgress) { return false } scaleInProgress = true diff --git a/platform/android/java/lib/src/org/godotengine/godot/input/GodotInputHandler.java b/platform/android/java/lib/src/org/godotengine/godot/input/GodotInputHandler.java index 03cb8034fa..2f26497cc8 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/input/GodotInputHandler.java +++ b/platform/android/java/lib/src/org/godotengine/godot/input/GodotInputHandler.java @@ -245,7 +245,7 @@ public class GodotInputHandler implements InputManager.InputDeviceListener { } return true; } - } else if (isMouseEvent(event)) { + } else { return handleMouseEvent(event); } @@ -438,15 +438,19 @@ public class GodotInputHandler implements InputManager.InputDeviceListener { } static boolean handleMotionEvent(int eventSource, int eventAction, int buttonsMask, float x, float y) { - return handleMotionEvent(eventSource, eventAction, buttonsMask, x, y, 0, 0); + return handleMotionEvent(eventSource, eventAction, buttonsMask, x, y, false); + } + + static boolean handleMotionEvent(int eventSource, int eventAction, int buttonsMask, float x, float y, boolean doubleTap) { + return handleMotionEvent(eventSource, eventAction, buttonsMask, x, y, 0, 0, doubleTap); } - static boolean handleMotionEvent(int eventSource, int eventAction, int buttonsMask, float x, float y, float deltaX, float deltaY) { + static boolean handleMotionEvent(int eventSource, int eventAction, int buttonsMask, float x, float y, float deltaX, float deltaY, boolean doubleTap) { if (isMouseEvent(eventSource)) { - return handleMouseEvent(eventAction, buttonsMask, x, y, deltaX, deltaY, false, false); + return handleMouseEvent(eventAction, buttonsMask, x, y, deltaX, deltaY, doubleTap, false); } - return handleTouchEvent(eventAction, x, y); + return handleTouchEvent(eventAction, x, y, doubleTap); } static boolean handleMouseEvent(final MotionEvent event) { @@ -468,11 +472,10 @@ public class GodotInputHandler implements InputManager.InputDeviceListener { return handleMouseEvent(eventAction, buttonsMask, x, y, 0, 0, false, false); } - static boolean handleMouseEvent(int eventAction, int buttonsMask, float x, float y, boolean doubleClick) { - return handleMouseEvent(eventAction, buttonsMask, x, y, 0, 0, doubleClick, false); - } - static boolean handleMouseEvent(int eventAction, int buttonsMask, float x, float y, float deltaX, float deltaY, boolean doubleClick, boolean sourceMouseRelative) { + // We don't handle ACTION_BUTTON_PRESS and ACTION_BUTTON_RELEASE events as they typically + // follow ACTION_DOWN and ACTION_UP events. As such, handling them would result in duplicate + // stream of events to the engine. switch (eventAction) { case MotionEvent.ACTION_CANCEL: case MotionEvent.ACTION_UP: @@ -508,14 +511,14 @@ public class GodotInputHandler implements InputManager.InputDeviceListener { final int action = event.getActionMasked(); final int actionPointerId = event.getPointerId(event.getActionIndex()); - return handleTouchEvent(action, actionPointerId, pointerCount, positions); + return handleTouchEvent(action, actionPointerId, pointerCount, positions, false); } - static boolean handleTouchEvent(int eventAction, float x, float y) { - return handleTouchEvent(eventAction, 0, 1, new float[] { 0, x, y }); + static boolean handleTouchEvent(int eventAction, float x, float y, boolean doubleTap) { + return handleTouchEvent(eventAction, 0, 1, new float[] { 0, x, y }, doubleTap); } - static boolean handleTouchEvent(int eventAction, int actionPointerId, int pointerCount, float[] positions) { + static boolean handleTouchEvent(int eventAction, int actionPointerId, int pointerCount, float[] positions, boolean doubleTap) { switch (eventAction) { case MotionEvent.ACTION_DOWN: case MotionEvent.ACTION_CANCEL: @@ -523,7 +526,7 @@ public class GodotInputHandler implements InputManager.InputDeviceListener { case MotionEvent.ACTION_MOVE: case MotionEvent.ACTION_POINTER_UP: case MotionEvent.ACTION_POINTER_DOWN: { - GodotLib.dispatchTouchEvent(eventAction, actionPointerId, pointerCount, positions); + GodotLib.dispatchTouchEvent(eventAction, actionPointerId, pointerCount, positions, doubleTap); return true; } } diff --git a/platform/android/java/lib/src/org/godotengine/godot/input/GodotTextInputWrapper.java b/platform/android/java/lib/src/org/godotengine/godot/input/GodotTextInputWrapper.java index c959b5f28c..01ad5ee415 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/input/GodotTextInputWrapper.java +++ b/platform/android/java/lib/src/org/godotengine/godot/input/GodotTextInputWrapper.java @@ -122,7 +122,7 @@ public class GodotTextInputWrapper implements TextWatcher, OnEditorActionListene @Override public boolean onEditorAction(final TextView pTextView, final int pActionID, final KeyEvent pKeyEvent) { - if (mEdit == pTextView && isFullScreenEdit()) { + if (mEdit == pTextView && isFullScreenEdit() && pKeyEvent != null) { final String characters = pKeyEvent.getCharacters(); for (int i = 0; i < characters.length(); i++) { diff --git a/platform/android/java_godot_lib_jni.cpp b/platform/android/java_godot_lib_jni.cpp index 04b69d5b86..b5cb9d341d 100644 --- a/platform/android/java_godot_lib_jni.cpp +++ b/platform/android/java_godot_lib_jni.cpp @@ -265,7 +265,7 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_dispatchMouseEvent(JN } // Called on the UI thread -JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_dispatchTouchEvent(JNIEnv *env, jclass clazz, jint ev, jint pointer, jint pointer_count, jfloatArray position) { +JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_dispatchTouchEvent(JNIEnv *env, jclass clazz, jint ev, jint pointer, jint pointer_count, jfloatArray position, jboolean p_double_tap) { if (step.get() <= 0) { return; } @@ -280,7 +280,7 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_dispatchTouchEvent(JN points.push_back(tp); } - input_handler->process_touch_event(ev, pointer, points); + input_handler->process_touch_event(ev, pointer, points, p_double_tap); } // Called on the UI thread @@ -409,7 +409,7 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_focusout(JNIEnv *env, JNIEXPORT jstring JNICALL Java_org_godotengine_godot_GodotLib_getGlobal(JNIEnv *env, jclass clazz, jstring path) { String js = jstring_to_string(path, env); - return env->NewStringUTF(ProjectSettings::get_singleton()->get(js).operator String().utf8().get_data()); + return env->NewStringUTF(GLOBAL_GET(js).operator String().utf8().get_data()); } JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_callobject(JNIEnv *env, jclass clazz, jlong ID, jstring method, jobjectArray params) { diff --git a/platform/android/java_godot_lib_jni.h b/platform/android/java_godot_lib_jni.h index 09fed15690..f3f2646bfb 100644 --- a/platform/android/java_godot_lib_jni.h +++ b/platform/android/java_godot_lib_jni.h @@ -46,7 +46,7 @@ JNIEXPORT jboolean JNICALL Java_org_godotengine_godot_GodotLib_step(JNIEnv *env, JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_ttsCallback(JNIEnv *env, jclass clazz, jint event, jint id, jint pos); JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_back(JNIEnv *env, jclass clazz); JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_dispatchMouseEvent(JNIEnv *env, jclass clazz, jint p_event_type, jint p_button_mask, jfloat p_x, jfloat p_y, jfloat p_delta_x, jfloat p_delta_y, jboolean p_double_click, jboolean p_source_mouse_relative); -JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_dispatchTouchEvent(JNIEnv *env, jclass clazz, jint ev, jint pointer, jint pointer_count, jfloatArray positions); +JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_dispatchTouchEvent(JNIEnv *env, jclass clazz, jint ev, jint pointer, jint pointer_count, jfloatArray positions, jboolean p_double_tap); JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_magnify(JNIEnv *env, jclass clazz, jfloat p_x, jfloat p_y, jfloat p_factor); JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_pan(JNIEnv *env, jclass clazz, jfloat p_x, jfloat p_y, jfloat p_delta_x, jfloat p_delta_y); JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_key(JNIEnv *env, jclass clazz, jint p_keycode, jint p_physical_keycode, jint p_unicode, jboolean p_pressed); diff --git a/platform/android/java_godot_view_wrapper.cpp b/platform/android/java_godot_view_wrapper.cpp index 0153ba96fc..378a467772 100644 --- a/platform/android/java_godot_view_wrapper.cpp +++ b/platform/android/java_godot_view_wrapper.cpp @@ -40,13 +40,24 @@ GodotJavaViewWrapper::GodotJavaViewWrapper(jobject godot_view) { _cls = (jclass)env->NewGlobalRef(env->GetObjectClass(godot_view)); - if (android_get_device_api_level() >= __ANDROID_API_O__) { + int android_device_api_level = android_get_device_api_level(); + if (android_device_api_level >= __ANDROID_API_N__) { + _set_pointer_icon = env->GetMethodID(_cls, "setPointerIcon", "(I)V"); + } + if (android_device_api_level >= __ANDROID_API_O__) { _request_pointer_capture = env->GetMethodID(_cls, "requestPointerCapture", "()V"); _release_pointer_capture = env->GetMethodID(_cls, "releasePointerCapture", "()V"); - _set_pointer_icon = env->GetMethodID(_cls, "setPointerIcon", "(I)V"); } } +bool GodotJavaViewWrapper::can_update_pointer_icon() const { + return _set_pointer_icon != nullptr; +} + +bool GodotJavaViewWrapper::can_capture_pointer() const { + return _request_pointer_capture != nullptr && _release_pointer_capture != nullptr; +} + void GodotJavaViewWrapper::request_pointer_capture() { if (_request_pointer_capture != nullptr) { JNIEnv *env = get_jni_env(); @@ -57,7 +68,7 @@ void GodotJavaViewWrapper::request_pointer_capture() { } void GodotJavaViewWrapper::release_pointer_capture() { - if (_request_pointer_capture != nullptr) { + if (_release_pointer_capture != nullptr) { JNIEnv *env = get_jni_env(); ERR_FAIL_NULL(env); diff --git a/platform/android/java_godot_view_wrapper.h b/platform/android/java_godot_view_wrapper.h index c52f459d64..b398c73cac 100644 --- a/platform/android/java_godot_view_wrapper.h +++ b/platform/android/java_godot_view_wrapper.h @@ -50,6 +50,9 @@ private: public: GodotJavaViewWrapper(jobject godot_view); + bool can_update_pointer_icon() const; + bool can_capture_pointer() const; + void request_pointer_capture(); void release_pointer_capture(); void set_pointer_icon(int pointer_type); diff --git a/platform/android/java_godot_wrapper.cpp b/platform/android/java_godot_wrapper.cpp index 07b0d75921..416b98c895 100644 --- a/platform/android/java_godot_wrapper.cpp +++ b/platform/android/java_godot_wrapper.cpp @@ -78,13 +78,23 @@ GodotJavaWrapper::GodotJavaWrapper(JNIEnv *p_env, jobject p_activity, jobject p_ _on_godot_setup_completed = p_env->GetMethodID(godot_class, "onGodotSetupCompleted", "()V"); _on_godot_main_loop_started = p_env->GetMethodID(godot_class, "onGodotMainLoopStarted", "()V"); _create_new_godot_instance = p_env->GetMethodID(godot_class, "createNewGodotInstance", "([Ljava/lang/String;)V"); + _get_render_view = p_env->GetMethodID(godot_class, "getRenderView", "()Lorg/godotengine/godot/GodotRenderView;"); // get some Activity method pointers... _get_class_loader = p_env->GetMethodID(activity_class, "getClassLoader", "()Ljava/lang/ClassLoader;"); } GodotJavaWrapper::~GodotJavaWrapper() { - // nothing to do here for now + if (godot_view) { + delete godot_view; + } + + JNIEnv *env = get_jni_env(); + ERR_FAIL_NULL(env); + env->DeleteGlobalRef(godot_instance); + env->DeleteGlobalRef(godot_class); + env->DeleteGlobalRef(activity); + env->DeleteGlobalRef(activity_class); } jobject GodotJavaWrapper::get_activity() { @@ -115,14 +125,18 @@ jobject GodotJavaWrapper::get_class_loader() { } GodotJavaViewWrapper *GodotJavaWrapper::get_godot_view() { - if (_godot_view != nullptr) { - return _godot_view; + if (godot_view != nullptr) { + return godot_view; } - JNIEnv *env = get_jni_env(); - ERR_FAIL_NULL_V(env, nullptr); - jmethodID godot_view_getter = env->GetMethodID(godot_class, "getRenderView", "()Lorg/godotengine/godot/GodotRenderView;"); - _godot_view = new GodotJavaViewWrapper(env->CallObjectMethod(godot_instance, godot_view_getter)); - return _godot_view; + if (_get_render_view) { + JNIEnv *env = get_jni_env(); + ERR_FAIL_NULL_V(env, nullptr); + jobject godot_render_view = env->CallObjectMethod(godot_instance, _get_render_view); + if (!env->IsSameObject(godot_render_view, nullptr)) { + godot_view = new GodotJavaViewWrapper(godot_render_view); + } + } + return godot_view; } bool GodotJavaWrapper::on_video_init(JNIEnv *p_env) { diff --git a/platform/android/java_godot_wrapper.h b/platform/android/java_godot_wrapper.h index 0e96a4e1f3..fb9c4c77fc 100644 --- a/platform/android/java_godot_wrapper.h +++ b/platform/android/java_godot_wrapper.h @@ -46,7 +46,7 @@ private: jclass godot_class; jclass activity_class; - GodotJavaViewWrapper *_godot_view = nullptr; + GodotJavaViewWrapper *godot_view = nullptr; jmethodID _on_video_init = nullptr; jmethodID _restart = nullptr; @@ -69,6 +69,7 @@ private: jmethodID _on_godot_main_loop_started = nullptr; jmethodID _get_class_loader = nullptr; jmethodID _create_new_godot_instance = nullptr; + jmethodID _get_render_view = nullptr; public: GodotJavaWrapper(JNIEnv *p_env, jobject p_activity, jobject p_godot_instance); diff --git a/platform/android/jni_utils.cpp b/platform/android/jni_utils.cpp index 193ef61264..2b0ee50570 100644 --- a/platform/android/jni_utils.cpp +++ b/platform/android/jni_utils.cpp @@ -167,9 +167,8 @@ jvalret _variant_to_jvalue(JNIEnv *env, Variant::Type p_type, const Variant *p_a v.obj = arr; } break; -#ifndef _MSC_VER -#warning This is missing 64 bits arrays, I have no idea how to do it in JNI -#endif + + // TODO: This is missing 64 bits arrays, I have no idea how to do it in JNI. default: { v.val.i = 0; @@ -266,33 +265,33 @@ Variant _jobject_to_variant(JNIEnv *env, jobject obj) { if (name == "[D") { jdoubleArray arr = (jdoubleArray)obj; int fCount = env->GetArrayLength(arr); - PackedFloat32Array sarr; - sarr.resize(fCount); + PackedFloat64Array packed_array; + packed_array.resize(fCount); - real_t *w = sarr.ptrw(); + double *w = packed_array.ptrw(); for (int i = 0; i < fCount; i++) { double n; env->GetDoubleArrayRegion(arr, i, 1, &n); w[i] = n; } - return sarr; + return packed_array; } if (name == "[F") { jfloatArray arr = (jfloatArray)obj; int fCount = env->GetArrayLength(arr); - PackedFloat32Array sarr; - sarr.resize(fCount); + PackedFloat32Array packed_array; + packed_array.resize(fCount); - real_t *w = sarr.ptrw(); + float *w = packed_array.ptrw(); for (int i = 0; i < fCount; i++) { float n; env->GetFloatArrayRegion(arr, i, 1, &n); w[i] = n; } - return sarr; + return packed_array; } if (name == "[Ljava.lang.Object;") { diff --git a/platform/android/os_android.cpp b/platform/android/os_android.cpp index 4469c7a0f7..97fa90b1d2 100644 --- a/platform/android/os_android.cpp +++ b/platform/android/os_android.cpp @@ -162,11 +162,16 @@ Vector<String> OS_Android::get_granted_permissions() const { } Error OS_Android::open_dynamic_library(const String p_path, void *&p_library_handle, bool p_also_set_library_path, String *r_resolved_path) { - p_library_handle = dlopen(p_path.utf8().get_data(), RTLD_NOW); + String path = p_path; + if (!FileAccess::exists(path)) { + path = p_path.get_file(); + } + + p_library_handle = dlopen(path.utf8().get_data(), RTLD_NOW); ERR_FAIL_NULL_V_MSG(p_library_handle, ERR_CANT_OPEN, "Can't open dynamic library: " + p_path + ", error: " + dlerror() + "."); if (r_resolved_path != nullptr) { - *r_resolved_path = p_path; + *r_resolved_path = path; } return OK; diff --git a/platform/android/plugin/godot_plugin_jni.cpp b/platform/android/plugin/godot_plugin_jni.cpp index 7a39e2003d..5a7123b833 100644 --- a/platform/android/plugin/godot_plugin_jni.cpp +++ b/platform/android/plugin/godot_plugin_jni.cpp @@ -135,9 +135,9 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_plugin_GodotPlugin_nativeRegis } // Retrieve the current list of gdnative libraries. - Array singletons = Array(); + Array singletons; if (ProjectSettings::get_singleton()->has_setting("gdnative/singletons")) { - singletons = ProjectSettings::get_singleton()->get("gdnative/singletons"); + singletons = GLOBAL_GET("gdnative/singletons"); } // Insert the libraries provided by the plugin diff --git a/platform/android/thread_jandroid.cpp b/platform/android/thread_jandroid.cpp index 61b471866f..9f87303341 100644 --- a/platform/android/thread_jandroid.cpp +++ b/platform/android/thread_jandroid.cpp @@ -63,7 +63,7 @@ static void term_thread() { void init_thread_jandroid(JavaVM *p_jvm, JNIEnv *p_env) { java_vm = p_jvm; env = p_env; - Thread::_set_platform_funcs(nullptr, nullptr, &init_thread, &term_thread); + Thread::_set_platform_functions({ .init = init_thread, .term = &term_thread }); } void setup_android_thread() { diff --git a/platform/android/vulkan/vulkan_context_android.cpp b/platform/android/vulkan/vulkan_context_android.cpp index 5945421e17..948292c3af 100644 --- a/platform/android/vulkan/vulkan_context_android.cpp +++ b/platform/android/vulkan/vulkan_context_android.cpp @@ -28,6 +28,8 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ +#ifdef VULKAN_ENABLED + #include "vulkan_context_android.h" #ifdef USE_VOLK @@ -40,7 +42,7 @@ const char *VulkanContextAndroid::_get_platform_surface_extension() const { return VK_KHR_ANDROID_SURFACE_EXTENSION_NAME; } -int VulkanContextAndroid::window_create(ANativeWindow *p_window, DisplayServer::VSyncMode p_vsync_mode, int p_width, int p_height) { +Error VulkanContextAndroid::window_create(ANativeWindow *p_window, DisplayServer::VSyncMode p_vsync_mode, int p_width, int p_height) { VkAndroidSurfaceCreateInfoKHR createInfo; createInfo.sType = VK_STRUCTURE_TYPE_ANDROID_SURFACE_CREATE_INFO_KHR; createInfo.pNext = nullptr; @@ -50,7 +52,7 @@ int VulkanContextAndroid::window_create(ANativeWindow *p_window, DisplayServer:: VkSurfaceKHR surface; VkResult err = vkCreateAndroidSurfaceKHR(get_instance(), &createInfo, nullptr, &surface); if (err != VK_SUCCESS) { - ERR_FAIL_V_MSG(-1, "vkCreateAndroidSurfaceKHR failed with error " + itos(err)); + ERR_FAIL_V_MSG(ERR_CANT_CREATE, "vkCreateAndroidSurfaceKHR failed with error " + itos(err)); } return _window_create(DisplayServer::MAIN_WINDOW_ID, p_vsync_mode, surface, p_width, p_height); @@ -63,3 +65,5 @@ bool VulkanContextAndroid::_use_validation_layers() { // On Android, we use validation layers automatically if they were explicitly linked with the app. return count > 0; } + +#endif // VULKAN_ENABLED diff --git a/platform/android/vulkan/vulkan_context_android.h b/platform/android/vulkan/vulkan_context_android.h index 43b5d62598..fe9a033e1c 100644 --- a/platform/android/vulkan/vulkan_context_android.h +++ b/platform/android/vulkan/vulkan_context_android.h @@ -31,6 +31,8 @@ #ifndef VULKAN_CONTEXT_ANDROID_H #define VULKAN_CONTEXT_ANDROID_H +#ifdef VULKAN_ENABLED + #include "drivers/vulkan/vulkan_context.h" struct ANativeWindow; @@ -39,7 +41,7 @@ class VulkanContextAndroid : public VulkanContext { virtual const char *_get_platform_surface_extension() const override; public: - int window_create(ANativeWindow *p_window, DisplayServer::VSyncMode p_vsync_mode, int p_width, int p_height); + Error window_create(ANativeWindow *p_window, DisplayServer::VSyncMode p_vsync_mode, int p_width, int p_height); VulkanContextAndroid() = default; ~VulkanContextAndroid() override = default; @@ -48,4 +50,6 @@ protected: bool _use_validation_layers() override; }; +#endif // VULKAN_ENABLED + #endif // VULKAN_CONTEXT_ANDROID_H diff --git a/platform/ios/detect.py b/platform/ios/detect.py index 0f277d6b3a..38e62134b5 100644 --- a/platform/ios/detect.py +++ b/platform/ios/detect.py @@ -2,6 +2,11 @@ import os import sys from methods import detect_darwin_sdk_path +from typing import TYPE_CHECKING + +if TYPE_CHECKING: + from SCons import Environment + def is_active(): return True @@ -37,12 +42,12 @@ def get_opts(): def get_flags(): return [ ("arch", "arm64"), # Default for convenience. - ("tools", False), + ("target", "template_debug"), ("use_volk", False), ] -def configure(env): +def configure(env: "Environment"): # Validate arch. supported_arches = ["x86_64", "arm64"] if env["arch"] not in supported_arches: @@ -52,23 +57,6 @@ def configure(env): ) sys.exit() - ## Build type - - if env["target"].startswith("release"): - env.Append(CPPDEFINES=[("NS_BLOCK_ASSERTIONS", 1)]) - if env["optimize"] == "speed": # optimize for speed (default) - # `-O2` is more friendly to debuggers than `-O3`, leading to better crash backtraces - # when using `target=release_debug`. - opt = "-O3" if env["target"] == "release" else "-O2" - env.Append(CCFLAGS=[opt]) - env.Append(LINKFLAGS=[opt]) - elif env["optimize"] == "size": # optimize for size - env.Append(CCFLAGS=["-Os"]) - env.Append(LINKFLAGS=["-Os"]) - - elif env["target"] == "debug": - env.Append(CCFLAGS=["-g", "-O0"]) - ## LTO if env["lto"] == "auto": # Disable by default as it makes linking in Xcode very slow. @@ -145,12 +133,10 @@ def configure(env): env.Append(ASFLAGS=["-arch", "arm64"]) env.Append(CPPDEFINES=["NEED_LONG_INT"]) - # Disable exceptions on non-tools (template) builds - if not env["tools"]: - if env["ios_exceptions"]: - env.Append(CCFLAGS=["-fexceptions"]) - else: - env.Append(CCFLAGS=["-fno-exceptions"]) + if env["ios_exceptions"]: + env.Append(CCFLAGS=["-fexceptions"]) + else: + env.Append(CCFLAGS=["-fno-exceptions"]) # Temp fix for ABS/MAX/MIN macros in iOS SDK blocking compilation env.Append(CCFLAGS=["-Wno-ambiguous-macro"]) diff --git a/platform/ios/display_server_ios.h b/platform/ios/display_server_ios.h index f3624f24ab..447f919139 100644 --- a/platform/ios/display_server_ios.h +++ b/platform/ios/display_server_ios.h @@ -40,7 +40,6 @@ #include "vulkan_context_ios.h" -#import <QuartzCore/CAMetalLayer.h> #ifdef USE_VOLK #include <volk.h> #else @@ -48,6 +47,9 @@ #endif #endif +#import <Foundation/Foundation.h> +#import <QuartzCore/CAMetalLayer.h> + class DisplayServerIOS : public DisplayServer { GDCLASS(DisplayServerIOS, DisplayServer) @@ -73,7 +75,7 @@ class DisplayServerIOS : public DisplayServer { void perform_event(const Ref<InputEvent> &p_event); - DisplayServerIOS(const String &p_rendering_driver, DisplayServer::WindowMode p_mode, DisplayServer::VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i &p_resolution, Error &r_error); + DisplayServerIOS(const String &p_rendering_driver, DisplayServer::WindowMode p_mode, DisplayServer::VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i *p_position, const Vector2i &p_resolution, Error &r_error); ~DisplayServerIOS(); public: @@ -82,7 +84,7 @@ public: static DisplayServerIOS *get_singleton(); static void register_ios_driver(); - static DisplayServer *create_func(const String &p_rendering_driver, WindowMode p_mode, DisplayServer::VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i &p_resolution, Error &r_error); + static DisplayServer *create_func(const String &p_rendering_driver, WindowMode p_mode, DisplayServer::VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i *p_position, const Vector2i &p_resolution, Error &r_error); static Vector<String> get_rendering_drivers_func(); // MARK: - Events diff --git a/platform/ios/display_server_ios.mm b/platform/ios/display_server_ios.mm index d3a0f38463..6793b40dd4 100644 --- a/platform/ios/display_server_ios.mm +++ b/platform/ios/display_server_ios.mm @@ -41,7 +41,6 @@ #include "tts_ios.h" #import "view_controller.h" -#import <Foundation/Foundation.h> #import <sys/utsname.h> static const float kDisplayServerIOSAcceleration = 1.f; @@ -50,7 +49,7 @@ DisplayServerIOS *DisplayServerIOS::get_singleton() { return (DisplayServerIOS *)DisplayServer::get_singleton(); } -DisplayServerIOS::DisplayServerIOS(const String &p_rendering_driver, WindowMode p_mode, DisplayServer::VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i &p_resolution, Error &r_error) { +DisplayServerIOS::DisplayServerIOS(const String &p_rendering_driver, WindowMode p_mode, DisplayServer::VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i *p_position, const Vector2i &p_resolution, Error &r_error) { rendering_driver = p_rendering_driver; // Init TTS @@ -152,8 +151,8 @@ DisplayServerIOS::~DisplayServerIOS() { #endif } -DisplayServer *DisplayServerIOS::create_func(const String &p_rendering_driver, WindowMode p_mode, DisplayServer::VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i &p_resolution, Error &r_error) { - return memnew(DisplayServerIOS(p_rendering_driver, p_mode, p_vsync_mode, p_flags, p_resolution, r_error)); +DisplayServer *DisplayServerIOS::create_func(const String &p_rendering_driver, WindowMode p_mode, DisplayServer::VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i *p_position, const Vector2i &p_resolution, Error &r_error) { + return memnew(DisplayServerIOS(p_rendering_driver, p_mode, p_vsync_mode, p_flags, p_position, p_resolution, r_error)); } Vector<String> DisplayServerIOS::get_rendering_drivers_func() { @@ -228,19 +227,20 @@ void DisplayServerIOS::_window_callback(const Callable &p_callable, const Varian // MARK: Touches void DisplayServerIOS::touch_press(int p_idx, int p_x, int p_y, bool p_pressed, bool p_double_click) { - if (!GLOBAL_DEF("debug/disable_touch", false)) { + if (!GLOBAL_GET("debug/disable_touch")) { Ref<InputEventScreenTouch> ev; ev.instantiate(); ev->set_index(p_idx); ev->set_pressed(p_pressed); ev->set_position(Vector2(p_x, p_y)); + ev->set_double_tap(p_double_click); perform_event(ev); } } void DisplayServerIOS::touch_drag(int p_idx, int p_prev_x, int p_prev_y, int p_x, int p_y) { - if (!GLOBAL_DEF("debug/disable_touch", false)) { + if (!GLOBAL_GET("debug/disable_touch")) { Ref<InputEventScreenDrag> ev; ev.instantiate(); ev->set_index(p_idx); diff --git a/platform/ios/export/export_plugin.cpp b/platform/ios/export/export_plugin.cpp index 74a57dc614..8e4d91ac50 100644 --- a/platform/ios/export/export_plugin.cpp +++ b/platform/ios/export/export_plugin.cpp @@ -358,8 +358,8 @@ void EditorExportPlatformIOS::_fix_config_file(const Ref<EditorExportPreset> &p_ switch (image_scale_mode) { case 0: { - String logo_path = ProjectSettings::get_singleton()->get("application/boot_splash/image"); - bool is_on = ProjectSettings::get_singleton()->get("application/boot_splash/fullsize"); + String logo_path = GLOBAL_GET("application/boot_splash/image"); + bool is_on = GLOBAL_GET("application/boot_splash/fullsize"); // If custom logo is not specified, Godot does not scale default one, so we should do the same. value = (is_on && logo_path.length() > 0) ? "scaleAspectFit" : "center"; } break; @@ -371,7 +371,7 @@ void EditorExportPlatformIOS::_fix_config_file(const Ref<EditorExportPreset> &p_ strnew += lines[i].replace("$launch_screen_image_mode", value) + "\n"; } else if (lines[i].find("$launch_screen_background_color") != -1) { bool use_custom = p_preset->get("storyboard/use_custom_bg_color"); - Color color = use_custom ? p_preset->get("storyboard/custom_bg_color") : ProjectSettings::get_singleton()->get("application/boot_splash/bg_color"); + Color color = use_custom ? p_preset->get("storyboard/custom_bg_color") : GLOBAL_GET("application/boot_splash/bg_color"); const String value_format = "red=\"$red\" green=\"$green\" blue=\"$blue\" alpha=\"$alpha\""; Dictionary value_dictionary; @@ -384,7 +384,7 @@ void EditorExportPlatformIOS::_fix_config_file(const Ref<EditorExportPreset> &p_ strnew += lines[i].replace("$launch_screen_background_color", value) + "\n"; } else if (lines[i].find("$pbx_locale_file_reference") != -1) { String locale_files; - Vector<String> translations = ProjectSettings::get_singleton()->get("internationalization/locale/translations"); + Vector<String> translations = GLOBAL_GET("internationalization/locale/translations"); if (translations.size() > 0) { HashSet<String> languages; for (const String &E : translations) { @@ -403,7 +403,7 @@ void EditorExportPlatformIOS::_fix_config_file(const Ref<EditorExportPreset> &p_ strnew += lines[i].replace("$pbx_locale_file_reference", locale_files); } else if (lines[i].find("$pbx_locale_build_reference") != -1) { String locale_files; - Vector<String> translations = ProjectSettings::get_singleton()->get("internationalization/locale/translations"); + Vector<String> translations = GLOBAL_GET("internationalization/locale/translations"); if (translations.size() > 0) { HashSet<String> languages; for (const String &E : translations) { @@ -574,7 +574,7 @@ Error EditorExportPlatformIOS::_export_icons(const Ref<EditorExportPreset> &p_pr String icon_path = p_preset->get(info.preset_key); if (icon_path.length() == 0) { // Resize main app icon - icon_path = ProjectSettings::get_singleton()->get("application/config/icon"); + icon_path = GLOBAL_GET("application/config/icon"); Ref<Image> img = memnew(Image); Error err = ImageLoader::load_image(icon_path, img); if (err != OK) { @@ -677,7 +677,7 @@ Error EditorExportPlatformIOS::_export_loading_screen_file(const Ref<EditorExpor } else { Ref<Image> splash; - const String splash_path = ProjectSettings::get_singleton()->get("application/boot_splash/image"); + const String splash_path = GLOBAL_GET("application/boot_splash/image"); if (!splash_path.is_empty()) { splash.instantiate(); @@ -718,9 +718,9 @@ Error EditorExportPlatformIOS::_export_loading_screen_images(const Ref<EditorExp LoadingScreenInfo info = loading_screen_infos[i]; String loading_screen_file = p_preset->get(info.preset_key); - Color boot_bg_color = ProjectSettings::get_singleton()->get("application/boot_splash/bg_color"); - String boot_logo_path = ProjectSettings::get_singleton()->get("application/boot_splash/image"); - bool boot_logo_scale = ProjectSettings::get_singleton()->get("application/boot_splash/fullsize"); + Color boot_bg_color = GLOBAL_GET("application/boot_splash/bg_color"); + String boot_logo_path = GLOBAL_GET("application/boot_splash/image"); + bool boot_logo_scale = GLOBAL_GET("application/boot_splash/fullsize"); if (loading_screen_file.size() > 0) { // Load custom loading screens, and resize if required. @@ -740,8 +740,7 @@ Error EditorExportPlatformIOS::_export_loading_screen_images(const Ref<EditorExp img->resize(info.width, info.width / aspect_ratio); } } - Ref<Image> new_img = memnew(Image); - new_img->create(info.width, info.height, false, Image::FORMAT_RGBA8); + Ref<Image> new_img = Image::create_empty(info.width, info.height, false, Image::FORMAT_RGBA8); new_img->fill(boot_bg_color); _blend_and_rotate(new_img, img, false); err = new_img->save_png(p_dest_dir + info.export_name); @@ -755,8 +754,7 @@ Error EditorExportPlatformIOS::_export_loading_screen_images(const Ref<EditorExp } } else { // Generate loading screen from the splash screen - Ref<Image> img = memnew(Image); - img->create(info.width, info.height, false, Image::FORMAT_RGBA8); + Ref<Image> img = Image::create_empty(info.width, info.height, false, Image::FORMAT_RGBA8); img->fill(boot_bg_color); Ref<Image> img_bs; @@ -1496,8 +1494,8 @@ Error EditorExportPlatformIOS::export_project(const Ref<EditorExportPreset> &p_p print_line("Static framework: " + library_to_use); String pkg_name; - if (String(ProjectSettings::get_singleton()->get("application/config/name")) != "") { - pkg_name = String(ProjectSettings::get_singleton()->get("application/config/name")); + if (String(GLOBAL_GET("application/config/name")) != "") { + pkg_name = String(GLOBAL_GET("application/config/name")); } else { pkg_name = "Unnamed"; } @@ -1646,12 +1644,12 @@ Error EditorExportPlatformIOS::export_project(const Ref<EditorExportPreset> &p_p return ERR_FILE_NOT_FOUND; } - Dictionary appnames = ProjectSettings::get_singleton()->get("application/config/name_localized"); + Dictionary appnames = GLOBAL_GET("application/config/name_localized"); Dictionary camera_usage_descriptions = p_preset->get("privacy/camera_usage_description_localized"); Dictionary microphone_usage_descriptions = p_preset->get("privacy/microphone_usage_description_localized"); Dictionary photolibrary_usage_descriptions = p_preset->get("privacy/photolibrary_usage_description_localized"); - Vector<String> translations = ProjectSettings::get_singleton()->get("internationalization/locale/translations"); + Vector<String> translations = GLOBAL_GET("internationalization/locale/translations"); if (translations.size() > 0) { { String fname = dest_dir + binary_name + "/en.lproj"; @@ -1659,7 +1657,7 @@ Error EditorExportPlatformIOS::export_project(const Ref<EditorExportPreset> &p_p Ref<FileAccess> f = FileAccess::open(fname + "/InfoPlist.strings", FileAccess::WRITE); f->store_line("/* Localized versions of Info.plist keys */"); f->store_line(""); - f->store_line("CFBundleDisplayName = \"" + ProjectSettings::get_singleton()->get("application/config/name").operator String() + "\";"); + f->store_line("CFBundleDisplayName = \"" + GLOBAL_GET("application/config/name").operator String() + "\";"); f->store_line("NSCameraUsageDescription = \"" + p_preset->get("privacy/camera_usage_description").operator String() + "\";"); f->store_line("NSMicrophoneUsageDescription = \"" + p_preset->get("privacy/microphone_usage_description").operator String() + "\";"); f->store_line("NSPhotoLibraryUsageDescription = \"" + p_preset->get("privacy/photolibrary_usage_description").operator String() + "\";"); diff --git a/platform/ios/godot_view.mm b/platform/ios/godot_view.mm index ff90c05b1d..4537dc2985 100644 --- a/platform/ios/godot_view.mm +++ b/platform/ios/godot_view.mm @@ -30,6 +30,7 @@ #import "godot_view.h" +#include "core/config/project_settings.h" #include "core/os/keyboard.h" #include "core/string/ustring.h" #import "display_layer.h" @@ -205,16 +206,16 @@ static const float earth_gravity = 9.80665; if (self.useCADisplayLink) { self.displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(drawView)]; - // Approximate frame rate - // assumes device refreshes at 60 fps - int displayFPS = (NSInteger)(1.0 / self.renderingInterval); - - self.displayLink.preferredFramesPerSecond = displayFPS; + if (GLOBAL_GET("display/window/ios/allow_high_refresh_rate")) { + self.displayLink.preferredFramesPerSecond = 120; + } else { + self.displayLink.preferredFramesPerSecond = 60; + } // Setup DisplayLink in main thread [self.displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes]; } else { - self.animationTimer = [NSTimer scheduledTimerWithTimeInterval:self.renderingInterval target:self selector:@selector(drawView) userInfo:nil repeats:YES]; + self.animationTimer = [NSTimer scheduledTimerWithTimeInterval:(1.0 / 60) target:self selector:@selector(drawView) userInfo:nil repeats:YES]; } } diff --git a/platform/ios/os_ios.mm b/platform/ios/os_ios.mm index a674498620..b6b94d2f5e 100644 --- a/platform/ios/os_ios.mm +++ b/platform/ios/os_ios.mm @@ -396,7 +396,14 @@ void OS_IOS::vibrate_handheld(int p_duration_ms) { } bool OS_IOS::_check_internal_feature_support(const String &p_feature) { - return p_feature == "mobile"; + if (p_feature == "system_fonts") { + return true; + } + if (p_feature == "mobile") { + return true; + } + + return false; } void OS_IOS::on_focus_out() { diff --git a/platform/ios/view_controller.mm b/platform/ios/view_controller.mm index 43669d3f94..53c5d3d0b4 100644 --- a/platform/ios/view_controller.mm +++ b/platform/ios/view_controller.mm @@ -164,7 +164,11 @@ // MARK: Orientation - (UIRectEdge)preferredScreenEdgesDeferringSystemGestures { - return UIRectEdgeAll; + if (GLOBAL_GET("display/window/ios/suppress_ui_gesture")) { + return UIRectEdgeAll; + } else { + return UIRectEdgeNone; + } } - (BOOL)shouldAutorotate { @@ -206,7 +210,11 @@ } - (BOOL)prefersStatusBarHidden { - return YES; + if (GLOBAL_GET("display/window/ios/hide_status_bar")) { + return YES; + } else { + return NO; + } } - (BOOL)prefersHomeIndicatorAutoHidden { diff --git a/platform/ios/vulkan_context_ios.h b/platform/ios/vulkan_context_ios.h index e9c09e087a..3849c8ba8a 100644 --- a/platform/ios/vulkan_context_ios.h +++ b/platform/ios/vulkan_context_ios.h @@ -31,6 +31,8 @@ #ifndef VULKAN_CONTEXT_IOS_H #define VULKAN_CONTEXT_IOS_H +#ifdef VULKAN_ENABLED + #include "drivers/vulkan/vulkan_context.h" #import <UIKit/UIKit.h> @@ -45,4 +47,6 @@ public: ~VulkanContextIOS(); }; +#endif // VULKAN_ENABLED + #endif // VULKAN_CONTEXT_IOS_H diff --git a/platform/ios/vulkan_context_ios.mm b/platform/ios/vulkan_context_ios.mm index 09cd369aa5..81b021e758 100644 --- a/platform/ios/vulkan_context_ios.mm +++ b/platform/ios/vulkan_context_ios.mm @@ -28,6 +28,8 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ +#ifdef VULKAN_ENABLED + #include "vulkan_context_ios.h" #ifdef USE_VOLK #include <volk.h> @@ -57,3 +59,5 @@ Error VulkanContextIOS::window_create(DisplayServer::WindowID p_window_id, Displ VulkanContextIOS::VulkanContextIOS() {} VulkanContextIOS::~VulkanContextIOS() {} + +#endif // VULKAN_ENABLED diff --git a/platform/linuxbsd/SCsub b/platform/linuxbsd/SCsub index 91d45627b9..fcd739cdc9 100644 --- a/platform/linuxbsd/SCsub +++ b/platform/linuxbsd/SCsub @@ -14,15 +14,7 @@ common_linuxbsd = [ ] if env["x11"]: - common_linuxbsd += [ - "gl_manager_x11.cpp", - "detect_prime_x11.cpp", - "display_server_x11.cpp", - "key_mapping_x11.cpp", - ] - - if env["vulkan"]: - common_linuxbsd.append("vulkan_context_x11.cpp") + common_linuxbsd += SConscript("x11/SCsub") if env["speechd"]: common_linuxbsd.append(["speechd-so_wrap.c", "tts_linux.cpp"]) diff --git a/platform/linuxbsd/crash_handler_linuxbsd.cpp b/platform/linuxbsd/crash_handler_linuxbsd.cpp index 33da094860..8c8c8588b8 100644 --- a/platform/linuxbsd/crash_handler_linuxbsd.cpp +++ b/platform/linuxbsd/crash_handler_linuxbsd.cpp @@ -89,7 +89,7 @@ static void handle_crash(int sig) { // Try to demangle the function name to provide a more readable one if (dladdr(bt_buffer[i], &info) && info.dli_sname) { if (info.dli_sname[0] == '_') { - int status; + int status = 0; char *demangled = abi::__cxa_demangle(info.dli_sname, nullptr, nullptr, &status); if (status == 0 && demangled) { diff --git a/platform/linuxbsd/detect.py b/platform/linuxbsd/detect.py index 12d2432eea..004bcb8674 100644 --- a/platform/linuxbsd/detect.py +++ b/platform/linuxbsd/detect.py @@ -4,6 +4,11 @@ import sys from methods import get_compiler_version, using_gcc from platform_methods import detect_arch +from typing import TYPE_CHECKING + +if TYPE_CHECKING: + from SCons import Environment + def is_active(): return True @@ -44,8 +49,6 @@ def get_opts(): BoolVariable("fontconfig", "Detect and use fontconfig for system fonts support", True), BoolVariable("udev", "Use udev for gamepad connection callbacks", True), BoolVariable("x11", "Enable X11 display", True), - BoolVariable("debug_symbols", "Add debugging symbols to release/release_debug builds", True), - BoolVariable("separate_debug_symbols", "Create a separate file containing debugging symbols", False), BoolVariable("touch", "Enable touch events", True), BoolVariable("execinfo", "Use libexecinfo on systems where glibc is not available", False), ] @@ -57,7 +60,7 @@ def get_flags(): ] -def configure(env): +def configure(env: "Environment"): # Validate arch. supported_arches = ["x86_32", "x86_64", "arm32", "arm64", "rv64", "ppc32", "ppc64"] if env["arch"] not in supported_arches: @@ -69,26 +72,9 @@ def configure(env): ## Build type - if env["target"] == "release": - if env["optimize"] == "speed": # optimize for speed (default) - env.Prepend(CCFLAGS=["-O3"]) - elif env["optimize"] == "size": # optimize for size - env.Prepend(CCFLAGS=["-Os"]) - - if env["debug_symbols"]: - env.Prepend(CCFLAGS=["-g2"]) - - elif env["target"] == "release_debug": - if env["optimize"] == "speed": # optimize for speed (default) - env.Prepend(CCFLAGS=["-O2"]) - elif env["optimize"] == "size": # optimize for size - env.Prepend(CCFLAGS=["-Os"]) - - if env["debug_symbols"]: - env.Prepend(CCFLAGS=["-g2"]) - - elif env["target"] == "debug": - env.Prepend(CCFLAGS=["-g3"]) + if env.dev_build: + # This is needed for our crash handler to work properly. + # gdb works fine without it though, so maybe our crash handler could too. env.Append(LINKFLAGS=["-rdynamic"]) # CPU architecture flags. @@ -370,20 +356,22 @@ def configure(env): if env["opengl3"]: env.Append(CPPDEFINES=["GLES3_ENABLED"]) - env.ParseConfig("pkg-config gl --cflags --libs") env.Append(LIBS=["pthread"]) if platform.system() == "Linux": env.Append(LIBS=["dl"]) - if platform.system().find("BSD") >= 0: + if not env["execinfo"] and platform.libc_ver()[0] != "glibc": + # The default crash handler depends on glibc, so if the host uses + # a different libc (BSD libc, musl), fall back to libexecinfo. + print("Note: Using `execinfo=yes` for the crash handler as required on platforms where glibc is missing.") env["execinfo"] = True if env["execinfo"]: env.Append(LIBS=["execinfo"]) - if not env["tools"]: + if not env.editor_build: import subprocess import re diff --git a/platform/linuxbsd/export/export_plugin.cpp b/platform/linuxbsd/export/export_plugin.cpp index 4d45d3ba12..8277bb1505 100644 --- a/platform/linuxbsd/export/export_plugin.cpp +++ b/platform/linuxbsd/export/export_plugin.cpp @@ -56,8 +56,8 @@ Error EditorExportPlatformLinuxBSD::export_project(const Ref<EditorExportPreset> } String app_name; - if (String(ProjectSettings::get_singleton()->get("application/config/name")) != "") { - app_name = String(ProjectSettings::get_singleton()->get("application/config/name")); + if (String(GLOBAL_GET("application/config/name")) != "") { + app_name = String(GLOBAL_GET("application/config/name")); } else { app_name = "Unnamed"; } diff --git a/platform/linuxbsd/freedesktop_screensaver.cpp b/platform/linuxbsd/freedesktop_screensaver.cpp index fa3f7fbfea..88ec37c456 100644 --- a/platform/linuxbsd/freedesktop_screensaver.cpp +++ b/platform/linuxbsd/freedesktop_screensaver.cpp @@ -55,7 +55,7 @@ void FreeDesktopScreenSaver::inhibit() { return; } - String app_name_string = ProjectSettings::get_singleton()->get("application/config/name"); + String app_name_string = GLOBAL_GET("application/config/name"); CharString app_name_utf8 = app_name_string.utf8(); const char *app_name = app_name_string.is_empty() ? "Godot Engine" : app_name_utf8.get_data(); diff --git a/platform/linuxbsd/godot_linuxbsd.cpp b/platform/linuxbsd/godot_linuxbsd.cpp index 91a260182e..fa5e20891c 100644 --- a/platform/linuxbsd/godot_linuxbsd.cpp +++ b/platform/linuxbsd/godot_linuxbsd.cpp @@ -69,6 +69,7 @@ int main(int argc, char *argv[]) { } if (Main::start()) { + os.set_exit_code(EXIT_SUCCESS); os.run(); // it is actually the OS that decides how to run } Main::cleanup(); diff --git a/platform/linuxbsd/os_linuxbsd.cpp b/platform/linuxbsd/os_linuxbsd.cpp index f4e94f1a91..e14e4fb52d 100644 --- a/platform/linuxbsd/os_linuxbsd.cpp +++ b/platform/linuxbsd/os_linuxbsd.cpp @@ -34,20 +34,23 @@ #include "main/main.h" #include "servers/display_server.h" +#include "modules/modules_enabled.gen.h" // For regex. +#ifdef MODULE_REGEX_ENABLED +#include "modules/regex/regex.h" +#endif + #ifdef X11_ENABLED -#include "display_server_x11.h" +#include "x11/display_server_x11.h" #endif #ifdef HAVE_MNTENT #include <mntent.h> #endif +#include <dlfcn.h> #include <stdio.h> #include <stdlib.h> #include <string.h> - -#include <dlfcn.h> -#include <fcntl.h> #include <sys/stat.h> #include <sys/types.h> #include <sys/utsname.h> @@ -125,6 +128,8 @@ void OS_LinuxBSD::initialize() { crash_handler.initialize(); OS_Unix::initialize_core(); + + system_dir_desktop_cache = get_system_dir(SYSTEM_DIR_DESKTOP); } void OS_LinuxBSD::initialize_joypads() { @@ -242,6 +247,204 @@ String OS_LinuxBSD::get_version() const { return uts.version; } +Vector<String> OS_LinuxBSD::get_video_adapter_driver_info() const { + if (RenderingServer::get_singleton()->get_rendering_device() == nullptr) { + return Vector<String>(); + } + + const String rendering_device_name = RenderingServer::get_singleton()->get_rendering_device()->get_device_name(); // e.g. `NVIDIA GeForce GTX 970` + const String rendering_device_vendor = RenderingServer::get_singleton()->get_rendering_device()->get_device_vendor_name(); // e.g. `NVIDIA` + const String card_name = rendering_device_name.trim_prefix(rendering_device_vendor).strip_edges(); // -> `GeForce GTX 970` + + String vendor_device_id_mappings; + List<String> lspci_args; + lspci_args.push_back("-n"); + Error err = const_cast<OS_LinuxBSD *>(this)->execute("lspci", lspci_args, &vendor_device_id_mappings); + if (err != OK || vendor_device_id_mappings.is_empty()) { + return Vector<String>(); + } + + // Usually found under "VGA", but for example NVIDIA mobile/laptop adapters are often listed under "3D" and some AMD adapters are under "Display". + const String dc_vga = "0300"; // VGA compatible controller + const String dc_display = "0302"; // Display controller + const String dc_3d = "0380"; // 3D controller + + // splitting results by device class allows prioritizing, if multiple devices are found. + Vector<String> class_vga_device_candidates; + Vector<String> class_display_device_candidates; + Vector<String> class_3d_device_candidates; + +#ifdef MODULE_REGEX_ENABLED + RegEx regex_id_format = RegEx(); + regex_id_format.compile("^[a-f0-9]{4}:[a-f0-9]{4}$"); // e.g. `10de:13c2`; IDs are always in hexadecimal +#endif + + Vector<String> value_lines = vendor_device_id_mappings.split("\n", false); // example: `02:00.0 0300: 10de:13c2 (rev a1)` + for (const String &line : value_lines) { + Vector<String> columns = line.split(" ", false); + if (columns.size() < 3) { + continue; + } + String device_class = columns[1].trim_suffix(":"); + String vendor_device_id_mapping = columns[2]; + +#ifdef MODULE_REGEX_ENABLED + if (regex_id_format.search(vendor_device_id_mapping).is_null()) { + continue; + } +#endif + + if (device_class == dc_vga) { + class_vga_device_candidates.push_back(vendor_device_id_mapping); + } else if (device_class == dc_display) { + class_display_device_candidates.push_back(vendor_device_id_mapping); + } else if (device_class == dc_3d) { + class_3d_device_candidates.push_back(vendor_device_id_mapping); + } + } + + // Check results against currently used device (`card_name`), in case the user has multiple graphics cards. + const String device_lit = "Device"; // line of interest + class_vga_device_candidates = OS_LinuxBSD::lspci_device_filter(class_vga_device_candidates, dc_vga, device_lit, card_name); + class_display_device_candidates = OS_LinuxBSD::lspci_device_filter(class_display_device_candidates, dc_display, device_lit, card_name); + class_3d_device_candidates = OS_LinuxBSD::lspci_device_filter(class_3d_device_candidates, dc_3d, device_lit, card_name); + + // Get driver names and filter out invalid ones, because some adapters are dummys used only for passthrough. + // And they have no indicator besides certain driver names. + const String kernel_lit = "Kernel driver in use"; // line of interest + const String dummys = "vfio"; // for e.g. pci passthrough dummy kernel driver `vfio-pci` + Vector<String> class_vga_device_drivers = OS_LinuxBSD::lspci_get_device_value(class_vga_device_candidates, kernel_lit, dummys); + Vector<String> class_display_device_drivers = OS_LinuxBSD::lspci_get_device_value(class_display_device_candidates, kernel_lit, dummys); + Vector<String> class_3d_device_drivers = OS_LinuxBSD::lspci_get_device_value(class_3d_device_candidates, kernel_lit, dummys); + + static String driver_name; + static String driver_version; + + // Use first valid value: + for (const String &driver : class_3d_device_drivers) { + driver_name = driver; + break; + } + if (driver_name.is_empty()) { + for (const String &driver : class_display_device_drivers) { + driver_name = driver; + break; + } + } + if (driver_name.is_empty()) { + for (const String &driver : class_vga_device_drivers) { + driver_name = driver; + break; + } + } + + Vector<String> info; + info.push_back(driver_name); + + String modinfo; + List<String> modinfo_args; + modinfo_args.push_back(driver_name); + err = const_cast<OS_LinuxBSD *>(this)->execute("modinfo", modinfo_args, &modinfo); + if (err != OK || modinfo.is_empty()) { + info.push_back(""); // So that this method always either returns an empty array, or an array of length 2. + return info; + } + Vector<String> lines = modinfo.split("\n", false); + for (const String &line : lines) { + Vector<String> columns = line.split(":", false, 1); + if (columns.size() < 2) { + continue; + } + if (columns[0].strip_edges() == "version") { + driver_version = columns[1].strip_edges(); // example value: `510.85.02` on Linux/BSD + break; + } + } + + info.push_back(driver_version); + + return info; +} + +Vector<String> OS_LinuxBSD::lspci_device_filter(Vector<String> vendor_device_id_mapping, String class_suffix, String check_column, String whitelist) const { + // NOTE: whitelist can be changed to `Vector<String>`, if the need arises. + const String sep = ":"; + Vector<String> devices; + for (const String &mapping : vendor_device_id_mapping) { + String device; + List<String> d_args; + d_args.push_back("-d"); + d_args.push_back(mapping + sep + class_suffix); + d_args.push_back("-vmm"); + Error err = const_cast<OS_LinuxBSD *>(this)->execute("lspci", d_args, &device); // e.g. `lspci -d 10de:13c2:0300 -vmm` + if (err != OK) { + return Vector<String>(); + } else if (device.is_empty()) { + continue; + } + + Vector<String> device_lines = device.split("\n", false); + for (const String &line : device_lines) { + Vector<String> columns = line.split(":", false, 1); + if (columns.size() < 2) { + continue; + } + if (columns[0].strip_edges() == check_column) { + // for `column[0] == "Device"` this may contain `GM204 [GeForce GTX 970]` + bool is_valid = true; + if (!whitelist.is_empty()) { + is_valid = columns[1].strip_edges().contains(whitelist); + } + if (is_valid) { + devices.push_back(mapping); + } + break; + } + } + } + return devices; +} + +Vector<String> OS_LinuxBSD::lspci_get_device_value(Vector<String> vendor_device_id_mapping, String check_column, String blacklist) const { + // NOTE: blacklist can be changed to `Vector<String>`, if the need arises. + const String sep = ":"; + Vector<String> values; + for (const String &mapping : vendor_device_id_mapping) { + String device; + List<String> d_args; + d_args.push_back("-d"); + d_args.push_back(mapping); + d_args.push_back("-k"); + Error err = const_cast<OS_LinuxBSD *>(this)->execute("lspci", d_args, &device); // e.g. `lspci -d 10de:13c2 -k` + if (err != OK) { + return Vector<String>(); + } else if (device.is_empty()) { + continue; + } + + Vector<String> device_lines = device.split("\n", false); + for (const String &line : device_lines) { + Vector<String> columns = line.split(":", false, 1); + if (columns.size() < 2) { + continue; + } + if (columns[0].strip_edges() == check_column) { + // for `column[0] == "Kernel driver in use"` this may contain `nvidia` + bool is_valid = true; + const String value = columns[1].strip_edges(); + if (!blacklist.is_empty()) { + is_valid = !value.contains(blacklist); + } + if (is_valid) { + values.push_back(value); + } + break; + } + } + } + return values; +} + Error OS_LinuxBSD::shell_open(String p_uri) { Error ok; int err_code; @@ -280,7 +483,16 @@ Error OS_LinuxBSD::shell_open(String p_uri) { } bool OS_LinuxBSD::_check_internal_feature_support(const String &p_feature) { - return p_feature == "pc"; +#ifdef FONTCONFIG_ENABLED + if (p_feature == "system_fonts") { + return font_config_initialized; + } +#endif + if (p_feature == "pc") { + return true; + } + + return false; } uint64_t OS_LinuxBSD::get_embedded_pck_offset() const { @@ -422,6 +634,8 @@ String OS_LinuxBSD::get_system_font_path(const String &p_font_name, bool p_bold, ERR_FAIL_V_MSG(String(), "Unable to load fontconfig, system font support is disabled."); } + bool allow_substitutes = (p_font_name.to_lower() == "sans-serif") || (p_font_name.to_lower() == "serif") || (p_font_name.to_lower() == "monospace") || (p_font_name.to_lower() == "cursive") || (p_font_name.to_lower() == "fantasy"); + String ret; FcConfig *config = FcInitLoadConfigAndFonts(); @@ -443,6 +657,19 @@ String OS_LinuxBSD::get_system_font_path(const String &p_font_name, bool p_bold, FcResult result; FcPattern *match = FcFontMatch(0, pattern, &result); if (match) { + if (!allow_substitutes) { + char *family_name = nullptr; + if (FcPatternGetString(match, FC_FAMILY, 0, reinterpret_cast<FcChar8 **>(&family_name)) == FcResultMatch) { + if (family_name && String::utf8(family_name).to_lower() != p_font_name.to_lower()) { + FcPatternDestroy(match); + FcPatternDestroy(pattern); + FcObjectSetDestroy(object_set); + FcConfigDestroy(config); + + return String(); + } + } + } char *file_name = nullptr; if (FcPatternGetString(match, FC_FILE, 0, reinterpret_cast<FcChar8 **>(&file_name)) == FcResultMatch) { if (file_name) { @@ -509,6 +736,10 @@ String OS_LinuxBSD::get_cache_path() const { } String OS_LinuxBSD::get_system_dir(SystemDir p_dir, bool p_shared_storage) const { + if (p_dir == SYSTEM_DIR_DESKTOP && !system_dir_desktop_cache.is_empty()) { + return system_dir_desktop_cache; + } + String xdgparam; switch (p_dir) { @@ -517,31 +748,24 @@ String OS_LinuxBSD::get_system_dir(SystemDir p_dir, bool p_shared_storage) const } break; case SYSTEM_DIR_DCIM: { xdgparam = "PICTURES"; - } break; case SYSTEM_DIR_DOCUMENTS: { xdgparam = "DOCUMENTS"; - } break; case SYSTEM_DIR_DOWNLOADS: { xdgparam = "DOWNLOAD"; - } break; case SYSTEM_DIR_MOVIES: { xdgparam = "VIDEOS"; - } break; case SYSTEM_DIR_MUSIC: { xdgparam = "MUSIC"; - } break; case SYSTEM_DIR_PICTURES: { xdgparam = "PICTURES"; - } break; case SYSTEM_DIR_RINGTONES: { xdgparam = "MUSIC"; - } break; } diff --git a/platform/linuxbsd/os_linuxbsd.h b/platform/linuxbsd/os_linuxbsd.h index 722d83ba19..aa7af92aa1 100644 --- a/platform/linuxbsd/os_linuxbsd.h +++ b/platform/linuxbsd/os_linuxbsd.h @@ -69,6 +69,11 @@ class OS_LinuxBSD : public OS_Unix { String get_systemd_os_release_info_value(const String &key) const; + Vector<String> lspci_device_filter(Vector<String> vendor_device_id_mapping, String class_suffix, String check_column, String whitelist) const; + Vector<String> lspci_get_device_value(Vector<String> vendor_device_id_mapping, String check_column, String blacklist) const; + + String system_dir_desktop_cache; + protected: virtual void initialize() override; virtual void finalize() override; @@ -82,6 +87,8 @@ public: virtual String get_distribution_name() const override; virtual String get_version() const override; + virtual Vector<String> get_video_adapter_driver_info() const override; + virtual MainLoop *get_main_loop() const override; virtual uint64_t get_embedded_pck_offset() const override; diff --git a/platform/linuxbsd/platform_config.h b/platform/linuxbsd/platform_config.h index 3c05c67444..79e15e2512 100644 --- a/platform/linuxbsd/platform_config.h +++ b/platform/linuxbsd/platform_config.h @@ -44,4 +44,4 @@ #endif #endif -#define OPENGL_INCLUDE_H "thirdparty/glad/glad/glad.h" +#define OPENGL_INCLUDE_H "thirdparty/glad/glad/gl.h" diff --git a/platform/linuxbsd/x11/SCsub b/platform/linuxbsd/x11/SCsub new file mode 100644 index 0000000000..30c6080355 --- /dev/null +++ b/platform/linuxbsd/x11/SCsub @@ -0,0 +1,21 @@ +#!/usr/bin/env python + +Import("env") + +source_files = [ + "display_server_x11.cpp", + "key_mapping_x11.cpp", +] + +if env["vulkan"]: + source_files.append("vulkan_context_x11.cpp") + +if env["opengl3"]: + source_files.append(["gl_manager_x11.cpp", "detect_prime_x11.cpp", "#thirdparty/glad/glx.c"]) + +objects = [] + +for source_file in source_files: + objects.append(env.Object(source_file)) + +Return("objects") diff --git a/platform/linuxbsd/detect_prime_x11.cpp b/platform/linuxbsd/x11/detect_prime_x11.cpp index fb833ab5e6..ed046432d8 100644 --- a/platform/linuxbsd/detect_prime_x11.cpp +++ b/platform/linuxbsd/x11/detect_prime_x11.cpp @@ -38,8 +38,9 @@ #include <stdlib.h> -#include <GL/gl.h> -#include <GL/glx.h> +#include "thirdparty/glad/glad/gl.h" +#include "thirdparty/glad/glad/glx.h" + #include <X11/Xlib.h> #include <X11/Xutil.h> @@ -77,8 +78,6 @@ void create_context() { Window x11_window; GLXContext glx_context; - GLXCREATECONTEXTATTRIBSARBPROC glXCreateContextAttribsARB = (GLXCREATECONTEXTATTRIBSARBPROC)glXGetProcAddress((const GLubyte *)"glXCreateContextAttribsARB"); - static int visual_attribs[] = { GLX_RENDER_TYPE, GLX_RGBA_BIT, GLX_DRAWABLE_TYPE, GLX_WINDOW_BIT, @@ -101,7 +100,7 @@ void create_context() { GLXFBConfig *fbc = glXChooseFBConfig(x11_display, DefaultScreen(x11_display), visual_attribs, &fbcount); if (!fbc) { - exit(1); + quick_exit(1); } vi = glXGetVisualFromFBConfig(x11_display, fbc[0]); @@ -122,7 +121,7 @@ void create_context() { x11_window = XCreateWindow(x11_display, RootWindow(x11_display, vi->screen), 0, 0, 10, 10, 0, vi->depth, InputOutput, vi->visual, valuemask, &swa); if (!x11_window) { - exit(1); + quick_exit(1); } glXMakeCurrent(x11_display, x11_window, glx_context); @@ -189,8 +188,20 @@ int detect_prime() { if (i) { setenv("DRI_PRIME", "1", 1); } + + if (gladLoaderLoadGLX(NULL, 0) == 0) { + print_verbose("Unable to load GLX, GPU detection skipped."); + quick_exit(1); + } + create_context(); + PFNGLGETSTRINGPROC glGetString = (PFNGLGETSTRINGPROC)glXGetProcAddressARB((GLubyte *)"glGetString"); + if (!glGetString) { + print_verbose("Unable to get glGetString, GPU detection skipped."); + quick_exit(1); + } + const char *vendor = (const char *)glGetString(GL_VENDOR); const char *renderer = (const char *)glGetString(GL_RENDERER); @@ -208,7 +219,10 @@ int detect_prime() { print_verbose("Couldn't write vendor/renderer string."); } close(fdset[1]); - exit(0); + + // The function quick_exit() is used because exit() will call destructors on static objects copied by fork(). + // These objects will be freed anyway when the process finishes execution. + quick_exit(0); } } diff --git a/platform/linuxbsd/detect_prime_x11.h b/platform/linuxbsd/x11/detect_prime_x11.h index 7eb7064cc5..7eb7064cc5 100644 --- a/platform/linuxbsd/detect_prime_x11.h +++ b/platform/linuxbsd/x11/detect_prime_x11.h diff --git a/platform/linuxbsd/display_server_x11.cpp b/platform/linuxbsd/x11/display_server_x11.cpp index 66dea6cf1b..b86bc10643 100644 --- a/platform/linuxbsd/display_server_x11.cpp +++ b/platform/linuxbsd/x11/display_server_x11.cpp @@ -49,10 +49,14 @@ #include "drivers/gles3/rasterizer_gles3.h" #endif +#include <dlfcn.h> #include <limits.h> #include <stdio.h> #include <stdlib.h> #include <string.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <unistd.h> #include <X11/Xatom.h> #include <X11/Xutil.h> @@ -66,17 +70,6 @@ #define _NET_WM_STATE_REMOVE 0L // remove/unset property #define _NET_WM_STATE_ADD 1L // add/set property -#include <dlfcn.h> -#include <fcntl.h> -#include <sys/stat.h> -#include <sys/types.h> -#include <unistd.h> - -//stupid linux.h -#ifdef KEY_TAB -#undef KEY_TAB -#endif - #undef CursorShape #include <X11/XKBlib.h> @@ -133,7 +126,7 @@ bool DisplayServerX11::has_feature(Feature p_feature) const { case FEATURE_WINDOW_TRANSPARENCY: //case FEATURE_HIDPI: case FEATURE_ICON: - case FEATURE_NATIVE_ICON: + //case FEATURE_NATIVE_ICON: case FEATURE_SWAP_BUFFERS: #ifdef DBUS_ENABLED case FEATURE_KEEP_SCREEN_ON: @@ -383,10 +376,18 @@ void DisplayServerX11::mouse_set_mode(MouseMode p_mode) { } // The only modes that show a cursor are VISIBLE and CONFINED - bool showCursor = (p_mode == MOUSE_MODE_VISIBLE || p_mode == MOUSE_MODE_CONFINED); + bool show_cursor = (p_mode == MOUSE_MODE_VISIBLE || p_mode == MOUSE_MODE_CONFINED); + bool previously_shown = (mouse_mode == MOUSE_MODE_VISIBLE || mouse_mode == MOUSE_MODE_CONFINED); + + if (show_cursor && !previously_shown) { + WindowID window_id = get_window_at_screen_position(mouse_get_position()); + if (window_id != INVALID_WINDOW_ID) { + _send_window_event(windows[window_id], WINDOW_EVENT_MOUSE_ENTER); + } + } for (const KeyValue<WindowID, WindowData> &E : windows) { - if (showCursor) { + if (show_cursor) { XDefineCursor(x11_display, E.value.x11_window, cursors[current_cursor]); // show cursor } else { XDefineCursor(x11_display, E.value.x11_window, null_cursor); // hide cursor @@ -1209,16 +1210,6 @@ float DisplayServerX11::screen_get_refresh_rate(int p_screen) const { return SCREEN_REFRESH_RATE_FALLBACK; } -bool DisplayServerX11::screen_is_touchscreen(int p_screen) const { - _THREAD_SAFE_METHOD_ - -#ifndef _MSC_VER -#warning Need to get from proper window -#endif - - return DisplayServer::screen_is_touchscreen(p_screen); -} - #ifdef DBUS_ENABLED void DisplayServerX11::screen_set_keep_on(bool p_enable) { if (screen_is_kept_on() == p_enable) { @@ -1326,6 +1317,14 @@ int64_t DisplayServerX11::window_get_native_handle(HandleType p_handle_type, Win case WINDOW_VIEW: { return 0; // Not supported. } +#ifdef GLES3_ENABLED + case OPENGL_CONTEXT: { + if (gl_manager) { + return (int64_t)gl_manager->get_glx_context(p_window); + } + return 0; + } +#endif default: { return 0; } @@ -1746,6 +1745,18 @@ void DisplayServerX11::window_set_size(const Size2i p_size, WindowID p_window) { usleep(10000); } + + // Keep rendering context window size in sync +#if defined(VULKAN_ENABLED) + if (context_vulkan) { + context_vulkan->window_resize(p_window, xwa.width, xwa.height); + } +#endif +#if defined(GLES3_ENABLED) + if (gl_manager) { + gl_manager->window_resize(p_window, xwa.width, xwa.height); + } +#endif } Size2i DisplayServerX11::window_get_size(WindowID p_window) const { @@ -3142,9 +3153,14 @@ void DisplayServerX11::_window_changed(XEvent *event) { return; } + // Query display server about a possible new window state. + wd.fullscreen = _window_fullscreen_check(window_id); + wd.minimized = _window_minimize_check(window_id); + wd.maximized = _window_maximize_check(window_id, "_NET_WM_STATE"); + { //the position in xconfigure is not useful here, obtain it manually - int x, y; + int x = 0, y = 0; Window child; XTranslateCoordinates(x11_display, wd.x11_window, DefaultRootWindow(x11_display), 0, 0, &x, &y, &child); new_rect.position.x = x; @@ -3307,7 +3323,7 @@ void DisplayServerX11::_check_pending_events(LocalVector<XEvent> &r_events) { XFlush(x11_display); // Non-blocking wait for next event and remove it from the queue. - XEvent ev; + XEvent ev = {}; while (XCheckIfEvent(x11_display, &ev, _predicate_all_events, nullptr)) { // Check if the input manager wants to process the event. if (XFilterEvent(&ev, None)) { @@ -3414,7 +3430,7 @@ bool DisplayServerX11::mouse_process_popups() { XWindowAttributes root_attrs; XGetWindowAttributes(x11_display, root, &root_attrs); Vector2i pos = Vector2i(root_attrs.x + root_x, root_attrs.y + root_y); - if ((pos != last_mouse_monitor_pos) || (mask != last_mouse_monitor_mask)) { + if (mask != last_mouse_monitor_mask) { if (((mask & Button1Mask) || (mask & Button2Mask) || (mask & Button3Mask) || (mask & Button4Mask) || (mask & Button5Mask))) { List<WindowID>::Element *C = nullptr; List<WindowID>::Element *E = popup_list.back(); @@ -3440,7 +3456,6 @@ bool DisplayServerX11::mouse_process_popups() { } } last_mouse_monitor_mask = mask; - last_mouse_monitor_pos = pos; } } return closed; @@ -4113,10 +4128,10 @@ void DisplayServerX11::process_events() { if (event.xselection.target == requested) { Property p = _read_property(x11_display, windows[window_id].x11_window, XInternAtom(x11_display, "PRIMARY", 0)); - Vector<String> files = String((char *)p.data).split("\n", false); + Vector<String> files = String((char *)p.data).split("\r\n", false); XFree(p.data); for (int i = 0; i < files.size(); i++) { - files.write[i] = files[i].replace("file://", "").uri_decode().strip_edges(); + files.write[i] = files[i].replace("file://", "").uri_decode(); } if (!windows[window_id].drop_files_callback.is_null()) { @@ -4432,13 +4447,24 @@ Vector<String> DisplayServerX11::get_rendering_drivers_func() { return drivers; } -DisplayServer *DisplayServerX11::create_func(const String &p_rendering_driver, WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i &p_resolution, Error &r_error) { - DisplayServer *ds = memnew(DisplayServerX11(p_rendering_driver, p_mode, p_vsync_mode, p_flags, p_resolution, r_error)); +DisplayServer *DisplayServerX11::create_func(const String &p_rendering_driver, WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i *p_position, const Vector2i &p_resolution, Error &r_error) { + DisplayServer *ds = memnew(DisplayServerX11(p_rendering_driver, p_mode, p_vsync_mode, p_flags, p_position, p_resolution, r_error)); if (r_error != OK) { - OS::get_singleton()->alert("Your video card driver does not support any of the supported Vulkan or OpenGL versions.\n" - "Please update your drivers or if you have a very old or integrated GPU, upgrade it.\n" - "If you have updated your graphics drivers recently, try rebooting.", - "Unable to initialize Video driver"); + if (p_rendering_driver == "vulkan") { + String executable_name = OS::get_singleton()->get_executable_path().get_file(); + OS::get_singleton()->alert("Your video card driver does not support the selected Vulkan version.\n" + "Please try updating your GPU driver or try using the OpenGL 3 driver.\n" + "You can enable the OpenGL 3 driver by starting the engine from the\n" + "command line with the command:\n'./" + + executable_name + " --rendering-driver opengl3'.\n " + "If you have updated your graphics drivers recently, try rebooting.", + "Unable to initialize Video driver"); + } else { + OS::get_singleton()->alert("Your video card driver does not support the selected OpenGL version.\n" + "Please try updating your GPU driver.\n" + "If you have updated your graphics drivers recently, try rebooting.", + "Unable to initialize Video driver"); + } } return ds; } @@ -4682,7 +4708,7 @@ DisplayServerX11::WindowID DisplayServerX11::_create_window(WindowMode p_mode, V return id; } -DisplayServerX11::DisplayServerX11(const String &p_rendering_driver, WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i &p_resolution, Error &r_error) { +DisplayServerX11::DisplayServerX11(const String &p_rendering_driver, WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i *p_position, const Vector2i &p_resolution, Error &r_error) { Input::get_singleton()->set_event_dispatch_function(_dispatch_input_events); r_error = OK; @@ -4909,6 +4935,11 @@ DisplayServerX11::DisplayServerX11(const String &p_rendering_driver, WindowMode Point2i window_position( (screen_get_size(0).width - p_resolution.width) / 2, (screen_get_size(0).height - p_resolution.height) / 2); + + if (p_position != nullptr) { + window_position = *p_position; + } + WindowID main_window = _create_window(p_mode, p_vsync_mode, p_flags, Rect2i(window_position, p_resolution)); if (main_window == INVALID_WINDOW_ID) { r_error = ERR_CANT_CREATE; diff --git a/platform/linuxbsd/display_server_x11.h b/platform/linuxbsd/x11/display_server_x11.h index a5fa7613bc..4be8c3a534 100644 --- a/platform/linuxbsd/display_server_x11.h +++ b/platform/linuxbsd/x11/display_server_x11.h @@ -47,7 +47,7 @@ #include "servers/rendering_server.h" #if defined(SPEECHD_ENABLED) -#include "tts_linux.h" +#include "../tts_linux.h" #endif #if defined(GLES3_ENABLED) @@ -56,12 +56,12 @@ #if defined(VULKAN_ENABLED) #include "drivers/vulkan/rendering_device_vulkan.h" -#include "platform/linuxbsd/vulkan_context_x11.h" +#include "vulkan_context_x11.h" #endif #if defined(DBUS_ENABLED) -#include "freedesktop_portal_desktop.h" -#include "freedesktop_screensaver.h" +#include "../freedesktop_portal_desktop.h" +#include "../freedesktop_screensaver.h" #endif #include <X11/Xcursor/Xcursor.h> @@ -169,7 +169,6 @@ class DisplayServerX11 : public DisplayServer { HashMap<WindowID, WindowData> windows; unsigned int last_mouse_monitor_mask = 0; - Vector2i last_mouse_monitor_pos; uint64_t time_since_popup = 0; List<WindowID> popup_list; @@ -349,7 +348,6 @@ public: virtual Rect2i screen_get_usable_rect(int p_screen = SCREEN_OF_MAIN_WINDOW) const override; virtual int screen_get_dpi(int p_screen = SCREEN_OF_MAIN_WINDOW) const override; virtual float screen_get_refresh_rate(int p_screen = SCREEN_OF_MAIN_WINDOW) const override; - virtual bool screen_is_touchscreen(int p_screen = SCREEN_OF_MAIN_WINDOW) const override; #if defined(DBUS_ENABLED) virtual void screen_set_keep_on(bool p_enable) override; @@ -445,12 +443,12 @@ public: virtual void set_native_icon(const String &p_filename) override; virtual void set_icon(const Ref<Image> &p_icon) override; - static DisplayServer *create_func(const String &p_rendering_driver, WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i &p_resolution, Error &r_error); + static DisplayServer *create_func(const String &p_rendering_driver, WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i *p_position, const Vector2i &p_resolution, Error &r_error); static Vector<String> get_rendering_drivers_func(); static void register_x11_driver(); - DisplayServerX11(const String &p_rendering_driver, WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i &p_resolution, Error &r_error); + DisplayServerX11(const String &p_rendering_driver, WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i *p_position, const Vector2i &p_resolution, Error &r_error); ~DisplayServerX11(); }; diff --git a/platform/linuxbsd/gl_manager_x11.cpp b/platform/linuxbsd/x11/gl_manager_x11.cpp index 838be2c042..4d8d63c64a 100644 --- a/platform/linuxbsd/gl_manager_x11.cpp +++ b/platform/linuxbsd/x11/gl_manager_x11.cpp @@ -37,9 +37,7 @@ #include <stdlib.h> #include <unistd.h> -#define GLX_GLXEXT_PROTOTYPES -#include <GL/glx.h> -#include <GL/glxext.h> +#include "thirdparty/glad/glad/glx.h" #define GLX_CONTEXT_MAJOR_VERSION_ARB 0x2091 #define GLX_CONTEXT_MINOR_VERSION_ARB 0x2092 @@ -310,7 +308,7 @@ void GLManager_X11::swap_buffers() { return; } - // On X11, when enabled, transparancy is always active, so clear alpha manually. + // On X11, when enabled, transparency is always active, so clear alpha manually. if (OS::get_singleton()->is_layered_allowed()) { if (!DisplayServer::get_singleton()->window_get_flag(DisplayServer::WINDOW_FLAG_TRANSPARENT, _current_window->window_id)) { glColorMask(false, false, false, true); @@ -324,11 +322,14 @@ void GLManager_X11::swap_buffers() { } Error GLManager_X11::initialize() { + if (!gladLoaderLoadGLX(nullptr, 0)) { + return ERR_CANT_CREATE; + } + return OK; } void GLManager_X11::set_use_vsync(bool p_use) { - static bool setup = false; static PFNGLXSWAPINTERVALEXTPROC glXSwapIntervalEXT = nullptr; static PFNGLXSWAPINTERVALSGIPROC glXSwapIntervalMESA = nullptr; static PFNGLXSWAPINTERVALSGIPROC glXSwapIntervalSGI = nullptr; @@ -345,25 +346,12 @@ void GLManager_X11::set_use_vsync(bool p_use) { } const GLDisplay &disp = get_current_display(); - if (!setup) { - setup = true; - String extensions = glXQueryExtensionsString(disp.x11_display, DefaultScreen(disp.x11_display)); - if (extensions.find("GLX_EXT_swap_control") != -1) { - glXSwapIntervalEXT = (PFNGLXSWAPINTERVALEXTPROC)glXGetProcAddressARB((const GLubyte *)"glXSwapIntervalEXT"); - } - if (extensions.find("GLX_MESA_swap_control") != -1) { - glXSwapIntervalMESA = (PFNGLXSWAPINTERVALSGIPROC)glXGetProcAddressARB((const GLubyte *)"glXSwapIntervalMESA"); - } - if (extensions.find("GLX_SGI_swap_control") != -1) { - glXSwapIntervalSGI = (PFNGLXSWAPINTERVALSGIPROC)glXGetProcAddressARB((const GLubyte *)"glXSwapIntervalSGI"); - } - } int val = p_use ? 1 : 0; - if (glXSwapIntervalMESA) { + if (GLAD_GLX_MESA_swap_control) { glXSwapIntervalMESA(val); - } else if (glXSwapIntervalSGI) { + } else if (GLAD_GLX_SGI_swap_control) { glXSwapIntervalSGI(val); - } else if (glXSwapIntervalEXT) { + } else if (GLAD_GLX_EXT_swap_control) { GLXDrawable drawable = glXGetCurrentDrawable(); glXSwapIntervalEXT(disp.x11_display, drawable, val); } else { @@ -376,6 +364,17 @@ bool GLManager_X11::is_using_vsync() const { return use_vsync; } +void *GLManager_X11::get_glx_context(DisplayServer::WindowID p_window_id) { + if (p_window_id == -1) { + return nullptr; + } + + const GLWindow &win = _windows[p_window_id]; + const GLDisplay &disp = get_display(win.gldisplay_id); + + return (void *)disp.context->glx_context; +} + GLManager_X11::GLManager_X11(const Vector2i &p_size, ContextType p_context_type) { context_type = p_context_type; diff --git a/platform/linuxbsd/gl_manager_x11.h b/platform/linuxbsd/x11/gl_manager_x11.h index 4f78c45c88..1594c82801 100644 --- a/platform/linuxbsd/gl_manager_x11.h +++ b/platform/linuxbsd/x11/gl_manager_x11.h @@ -116,6 +116,8 @@ public: void set_use_vsync(bool p_use); bool is_using_vsync() const; + void *get_glx_context(DisplayServer::WindowID p_window_id); + GLManager_X11(const Vector2i &p_size, ContextType p_context_type); ~GLManager_X11(); }; diff --git a/platform/linuxbsd/key_mapping_x11.cpp b/platform/linuxbsd/x11/key_mapping_x11.cpp index 047ee74671..f774c99d99 100644 --- a/platform/linuxbsd/key_mapping_x11.cpp +++ b/platform/linuxbsd/x11/key_mapping_x11.cpp @@ -108,17 +108,21 @@ static _XTranslatePair _xkeysym_to_keycode[] = { { XK_KP_7, Key::KP_7 }, { XK_KP_8, Key::KP_8 }, { XK_KP_9, Key::KP_9 }, - // same but with numlock - { XK_KP_Insert, Key::KP_0 }, - { XK_KP_End, Key::KP_1 }, - { XK_KP_Down, Key::KP_2 }, - { XK_KP_Page_Down, Key::KP_3 }, - { XK_KP_Left, Key::KP_4 }, - { XK_KP_Begin, Key::KP_5 }, - { XK_KP_Right, Key::KP_6 }, - { XK_KP_Home, Key::KP_7 }, - { XK_KP_Up, Key::KP_8 }, - { XK_KP_Page_Up, Key::KP_9 }, + // same keys but with numlock off + { XK_KP_Insert, Key::INSERT }, + { XK_KP_End, Key::END }, + { XK_KP_Down, Key::DOWN }, + { XK_KP_Page_Down, Key::PAGEDOWN }, + { XK_KP_Left, Key::LEFT }, + // X11 documents this (numpad 5) as "begin of line" but no toolkit + // seems to interpret it this way. + // On Windows this is emitting Key::Clear so for consistency it + // will be mapped to Key::Clear + { XK_KP_Begin, Key::CLEAR }, + { XK_KP_Right, Key::RIGHT }, + { XK_KP_Home, Key::HOME }, + { XK_KP_Up, Key::UP }, + { XK_KP_Page_Up, Key::PAGEUP }, { XK_F1, Key::F1 }, { XK_F2, Key::F2 }, { XK_F3, Key::F3 }, diff --git a/platform/linuxbsd/key_mapping_x11.h b/platform/linuxbsd/x11/key_mapping_x11.h index b7b8a3b787..b7b8a3b787 100644 --- a/platform/linuxbsd/key_mapping_x11.h +++ b/platform/linuxbsd/x11/key_mapping_x11.h diff --git a/platform/linuxbsd/vulkan_context_x11.cpp b/platform/linuxbsd/x11/vulkan_context_x11.cpp index b4f585726f..92aaf33b05 100644 --- a/platform/linuxbsd/vulkan_context_x11.cpp +++ b/platform/linuxbsd/x11/vulkan_context_x11.cpp @@ -28,6 +28,8 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ +#ifdef VULKAN_ENABLED + #include "vulkan_context_x11.h" #ifdef USE_VOLK @@ -59,3 +61,5 @@ VulkanContextX11::VulkanContextX11() { VulkanContextX11::~VulkanContextX11() { } + +#endif // VULKAN_ENABLED diff --git a/platform/linuxbsd/vulkan_context_x11.h b/platform/linuxbsd/x11/vulkan_context_x11.h index 0c4a6cd278..0adb50ef44 100644 --- a/platform/linuxbsd/vulkan_context_x11.h +++ b/platform/linuxbsd/x11/vulkan_context_x11.h @@ -31,6 +31,8 @@ #ifndef VULKAN_CONTEXT_X11_H #define VULKAN_CONTEXT_X11_H +#ifdef VULKAN_ENABLED + #include "drivers/vulkan/vulkan_context.h" #include <X11/Xlib.h> @@ -44,4 +46,6 @@ public: ~VulkanContextX11(); }; +#endif // VULKAN_ENABLED + #endif // VULKAN_CONTEXT_X11_H diff --git a/platform/macos/detect.py b/platform/macos/detect.py index 58d9c0e99f..67e4b49b14 100644 --- a/platform/macos/detect.py +++ b/platform/macos/detect.py @@ -3,6 +3,11 @@ import sys from methods import detect_darwin_sdk_path from platform_methods import detect_arch +from typing import TYPE_CHECKING + +if TYPE_CHECKING: + from SCons import Environment + def is_active(): return True @@ -27,8 +32,6 @@ def get_opts(): ("MACOS_SDK_PATH", "Path to the macOS SDK", ""), ("vulkan_sdk_path", "Path to the Vulkan SDK", ""), EnumVariable("macports_clang", "Build using Clang from MacPorts", "no", ("no", "5.0", "devel")), - BoolVariable("debug_symbols", "Add debugging symbols to release/release_debug builds", True), - BoolVariable("separate_debug_symbols", "Create a separate file containing debugging symbols", False), BoolVariable("use_ubsan", "Use LLVM/GCC compiler undefined behavior sanitizer (UBSAN)", False), BoolVariable("use_asan", "Use LLVM/GCC compiler address sanitizer (ASAN)", False), BoolVariable("use_tsan", "Use LLVM/GCC compiler thread sanitizer (TSAN)", False), @@ -74,7 +77,7 @@ def get_mvk_sdk_path(): return os.path.join(os.path.join(dirname, ver_file), "MoltenVK/MoltenVK.xcframework/macos-arm64_x86_64/") -def configure(env): +def configure(env: "Environment"): # Validate arch. supported_arches = ["x86_64", "arm64"] if env["arch"] not in supported_arches: @@ -86,27 +89,10 @@ def configure(env): ## Build type - if env["target"] == "release": - if env["optimize"] == "speed": # optimize for speed (default) - env.Prepend(CCFLAGS=["-O3"]) - elif env["optimize"] == "size": # optimize for size - env.Prepend(CCFLAGS=["-Os"]) + if env["target"] == "template_release": if env["arch"] != "arm64": env.Prepend(CCFLAGS=["-msse2"]) - - if env["debug_symbols"]: - env.Prepend(CCFLAGS=["-g2"]) - - elif env["target"] == "release_debug": - if env["optimize"] == "speed": # optimize for speed (default) - env.Prepend(CCFLAGS=["-O2"]) - elif env["optimize"] == "size": # optimize for size - env.Prepend(CCFLAGS=["-Os"]) - if env["debug_symbols"]: - env.Prepend(CCFLAGS=["-g2"]) - - elif env["target"] == "debug": - env.Prepend(CCFLAGS=["-g3"]) + elif env.dev_build: env.Prepend(LINKFLAGS=["-Xlinker", "-no_deduplicate"]) ## Compiler configuration @@ -237,6 +223,8 @@ def configure(env): "AVFoundation", "-framework", "CoreMedia", + "-framework", + "QuartzCore", ] ) env.Append(LIBS=["pthread", "z"]) @@ -250,22 +238,28 @@ def configure(env): if env["vulkan"]: env.Append(CPPDEFINES=["VULKAN_ENABLED"]) - env.Append(LINKFLAGS=["-framework", "Metal", "-framework", "QuartzCore", "-framework", "IOSurface"]) + env.Append(LINKFLAGS=["-framework", "Metal", "-framework", "IOSurface"]) if not env["use_volk"]: env.Append(LINKFLAGS=["-lMoltenVK"]) mvk_found = False + + mkv_list = [get_mvk_sdk_path(), "/opt/homebrew/lib", "/usr/local/homebrew/lib", "/opt/local/lib"] if env["vulkan_sdk_path"] != "": - mvk_path = os.path.join( - os.path.expanduser(env["vulkan_sdk_path"]), "MoltenVK/MoltenVK.xcframework/macos-arm64_x86_64/" + mkv_list.insert(0, os.path.expanduser(env["vulkan_sdk_path"])) + mkv_list.insert( + 0, + os.path.join( + os.path.expanduser(env["vulkan_sdk_path"]), "MoltenVK/MoltenVK.xcframework/macos-arm64_x86_64/" + ), ) - if os.path.isfile(os.path.join(mvk_path, "libMoltenVK.a")): - mvk_found = True - env.Append(LINKFLAGS=["-L" + mvk_path]) - if not mvk_found: - mvk_path = get_mvk_sdk_path() + + for mvk_path in mkv_list: if mvk_path and os.path.isfile(os.path.join(mvk_path, "libMoltenVK.a")): mvk_found = True + print("MoltenVK found at: " + mvk_path) env.Append(LINKFLAGS=["-L" + mvk_path]) + break + if not mvk_found: print( "MoltenVK SDK installation directory not found, use 'vulkan_sdk_path' SCons parameter to specify SDK path." diff --git a/platform/macos/dir_access_macos.h b/platform/macos/dir_access_macos.h index 920e69ef3e..c76b2835e8 100644 --- a/platform/macos/dir_access_macos.h +++ b/platform/macos/dir_access_macos.h @@ -31,16 +31,16 @@ #ifndef DIR_ACCESS_MACOS_H #define DIR_ACCESS_MACOS_H -#if defined(UNIX_ENABLED) || defined(LIBC_FILEIO_ENABLED) +#if defined(UNIX_ENABLED) + +#include "core/io/dir_access.h" +#include "drivers/unix/dir_access_unix.h" #include <dirent.h> #include <sys/stat.h> #include <sys/types.h> #include <unistd.h> -#include "core/io/dir_access.h" -#include "drivers/unix/dir_access_unix.h" - class DirAccessMacOS : public DirAccessUnix { protected: virtual String fix_unicode_name(const char *p_name) const override; @@ -51,6 +51,6 @@ protected: virtual bool is_hidden(const String &p_name) override; }; -#endif // UNIX ENABLED || LIBC_FILEIO_ENABLED +#endif // UNIX ENABLED #endif // DIR_ACCESS_MACOS_H diff --git a/platform/macos/dir_access_macos.mm b/platform/macos/dir_access_macos.mm index 3373cada1f..22ebac2db4 100644 --- a/platform/macos/dir_access_macos.mm +++ b/platform/macos/dir_access_macos.mm @@ -30,7 +30,7 @@ #include "dir_access_macos.h" -#if defined(UNIX_ENABLED) || defined(LIBC_FILEIO_ENABLED) +#if defined(UNIX_ENABLED) #include <errno.h> @@ -78,4 +78,4 @@ bool DirAccessMacOS::is_hidden(const String &p_name) { return [hidden boolValue]; } -#endif // UNIX_ENABLED || LIBC_FILEIO_ENABLED +#endif // UNIX_ENABLED diff --git a/platform/macos/display_server_macos.h b/platform/macos/display_server_macos.h index e72273a681..8e75b98302 100644 --- a/platform/macos/display_server_macos.h +++ b/platform/macos/display_server_macos.h @@ -106,6 +106,7 @@ public: bool layered_window = false; bool fullscreen = false; + bool exclusive_fullscreen = false; bool on_top = false; bool borderless = false; bool resize_disabled = false; @@ -401,7 +402,7 @@ public: virtual bool window_minimize_on_title_dbl_click() const override; virtual void window_set_window_buttons_offset(const Vector2i &p_offset, WindowID p_window = MAIN_WINDOW_ID) override; - virtual Vector2i window_get_safe_title_margins(WindowID p_window = MAIN_WINDOW_ID) const override; + virtual Vector3i window_get_safe_title_margins(WindowID p_window = MAIN_WINDOW_ID) const override; virtual Point2i ime_get_selection() const override; virtual String ime_get_text() const override; @@ -430,12 +431,12 @@ public: virtual void set_native_icon(const String &p_filename) override; virtual void set_icon(const Ref<Image> &p_icon) override; - static DisplayServer *create_func(const String &p_rendering_driver, WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i &p_resolution, Error &r_error); + static DisplayServer *create_func(const String &p_rendering_driver, WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i *p_position, const Vector2i &p_resolution, Error &r_error); static Vector<String> get_rendering_drivers_func(); static void register_macos_driver(); - DisplayServerMacOS(const String &p_rendering_driver, WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i &p_resolution, Error &r_error); + DisplayServerMacOS(const String &p_rendering_driver, WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i *p_position, const Vector2i &p_resolution, Error &r_error); ~DisplayServerMacOS(); }; diff --git a/platform/macos/display_server_macos.mm b/platform/macos/display_server_macos.mm index 1914c5f35d..42a984a4eb 100644 --- a/platform/macos/display_server_macos.mm +++ b/platform/macos/display_server_macos.mm @@ -586,7 +586,7 @@ void DisplayServerMacOS::send_event(NSEvent *p_event) { // Special case handling of command-period, which is traditionally a special // shortcut in macOS and doesn't arrive at our regular keyDown handler. if ([p_event type] == NSEventTypeKeyDown) { - if (([p_event modifierFlags] & NSEventModifierFlagCommand) && [p_event keyCode] == 0x2f) { + if ((([p_event modifierFlags] & NSEventModifierFlagDeviceIndependentFlagsMask) == NSEventModifierFlagCommand) && [p_event keyCode] == 0x2f) { Ref<InputEventKey> k; k.instantiate(); @@ -1843,11 +1843,22 @@ void DisplayServerMacOS::mouse_set_mode(MouseMode p_mode) { window_id = MAIN_WINDOW_ID; } WindowData &wd = windows[window_id]; + + bool show_cursor = (p_mode == MOUSE_MODE_VISIBLE || p_mode == MOUSE_MODE_CONFINED); + bool previously_shown = (mouse_mode == MOUSE_MODE_VISIBLE || mouse_mode == MOUSE_MODE_CONFINED); + + if (show_cursor && !previously_shown) { + WindowID window_id = get_window_at_screen_position(mouse_get_position()); + if (window_id != INVALID_WINDOW_ID) { + send_window_event(windows[window_id], WINDOW_EVENT_MOUSE_ENTER); + } + } + if (p_mode == MOUSE_MODE_CAPTURED) { // Apple Docs state that the display parameter is not used. // "This parameter is not used. By default, you may pass kCGDirectMainDisplay." // https://developer.apple.com/library/mac/documentation/graphicsimaging/reference/Quartz_Services_Ref/Reference/reference.html - if (mouse_mode == MOUSE_MODE_VISIBLE || mouse_mode == MOUSE_MODE_CONFINED) { + if (previously_shown) { CGDisplayHideCursor(kCGDirectMainDisplay); } CGAssociateMouseAndMouseCursorPosition(false); @@ -1858,7 +1869,7 @@ void DisplayServerMacOS::mouse_set_mode(MouseMode p_mode) { CGPoint lMouseWarpPos = { pointOnScreen.x, CGDisplayBounds(CGMainDisplayID()).size.height - pointOnScreen.y }; CGWarpMouseCursorPosition(lMouseWarpPos); } else if (p_mode == MOUSE_MODE_HIDDEN) { - if (mouse_mode == MOUSE_MODE_VISIBLE || mouse_mode == MOUSE_MODE_CONFINED) { + if (previously_shown) { CGDisplayHideCursor(kCGDirectMainDisplay); } [wd.window_object setMovable:YES]; @@ -1868,7 +1879,7 @@ void DisplayServerMacOS::mouse_set_mode(MouseMode p_mode) { [wd.window_object setMovable:NO]; CGAssociateMouseAndMouseCursorPosition(false); } else if (p_mode == MOUSE_MODE_CONFINED_HIDDEN) { - if (mouse_mode == MOUSE_MODE_VISIBLE || mouse_mode == MOUSE_MODE_CONFINED) { + if (previously_shown) { CGDisplayHideCursor(kCGDirectMainDisplay); } [wd.window_object setMovable:NO]; @@ -1884,7 +1895,7 @@ void DisplayServerMacOS::mouse_set_mode(MouseMode p_mode) { warp_events.clear(); mouse_mode = p_mode; - if (mouse_mode == MOUSE_MODE_VISIBLE || mouse_mode == MOUSE_MODE_CONFINED) { + if (show_cursor) { cursor_update_shape(); } } @@ -2179,7 +2190,7 @@ void DisplayServerMacOS::screen_set_keep_on(bool p_enable) { } if (p_enable) { - String app_name_string = ProjectSettings::get_singleton()->get("application/config/name"); + String app_name_string = GLOBAL_GET("application/config/name"); NSString *name = [NSString stringWithUTF8String:(app_name_string.is_empty() ? "Godot Engine" : app_name_string.utf8().get_data())]; NSString *reason = @"Godot Engine running with display/window/energy_saving/keep_screen_on = true"; IOPMAssertionCreateWithDescription(kIOPMAssertPreventUserIdleDisplaySleep, (__bridge CFStringRef)name, (__bridge CFStringRef)reason, (__bridge CFStringRef)reason, nullptr, 0, nullptr, &screen_keep_on_assertion); @@ -2573,7 +2584,13 @@ void DisplayServerMacOS::window_set_mode(WindowMode p_mode, WindowID p_window) { [wd.window_object setContentMaxSize:NSMakeSize(size.x, size.y)]; } [wd.window_object toggleFullScreen:nil]; + + if (old_mode == WINDOW_MODE_EXCLUSIVE_FULLSCREEN) { + [NSApp setPresentationOptions:NSApplicationPresentationDefault]; + } + wd.fullscreen = false; + wd.exclusive_fullscreen = false; } break; case WINDOW_MODE_MAXIMIZED: { if ([wd.window_object isZoomed]) { @@ -2598,7 +2615,15 @@ void DisplayServerMacOS::window_set_mode(WindowMode p_mode, WindowID p_window) { [wd.window_object setContentMinSize:NSMakeSize(0, 0)]; [wd.window_object setContentMaxSize:NSMakeSize(FLT_MAX, FLT_MAX)]; [wd.window_object toggleFullScreen:nil]; + wd.fullscreen = true; + if (p_mode == WINDOW_MODE_EXCLUSIVE_FULLSCREEN) { + const NSUInteger presentationOptions = NSApplicationPresentationHideDock | NSApplicationPresentationHideMenuBar; + [NSApp setPresentationOptions:presentationOptions]; + wd.exclusive_fullscreen = true; + } else { + wd.exclusive_fullscreen = false; + } } break; case WINDOW_MODE_MAXIMIZED: { if (![wd.window_object isZoomed]) { @@ -2615,7 +2640,11 @@ DisplayServer::WindowMode DisplayServerMacOS::window_get_mode(WindowID p_window) const WindowData &wd = windows[p_window]; if (wd.fullscreen) { // If fullscreen, it's not in another mode. - return WINDOW_MODE_FULLSCREEN; + if (wd.exclusive_fullscreen) { + return WINDOW_MODE_EXCLUSIVE_FULLSCREEN; + } else { + return WINDOW_MODE_FULLSCREEN; + } } if ([wd.window_object isZoomed] && !wd.resize_disabled) { return WINDOW_MODE_MAXIMIZED; @@ -2655,25 +2684,33 @@ void DisplayServerMacOS::window_set_window_buttons_offset(const Vector2i &p_offs ERR_FAIL_COND(!windows.has(p_window)); WindowData &wd = windows[p_window]; - wd.wb_offset = p_offset; + float scale = screen_get_max_scale(); + wd.wb_offset = p_offset / scale; + wd.wb_offset.x = MAX(wd.wb_offset.x, 12); + wd.wb_offset.y = MAX(wd.wb_offset.y, 12); + if (wd.window_button_view) { + [wd.window_button_view setOffset:NSMakePoint(wd.wb_offset.x, wd.wb_offset.y)]; + } } -Vector2i DisplayServerMacOS::window_get_safe_title_margins(WindowID p_window) const { +Vector3i DisplayServerMacOS::window_get_safe_title_margins(WindowID p_window) const { _THREAD_SAFE_METHOD_ - ERR_FAIL_COND_V(!windows.has(p_window), Vector2i()); + ERR_FAIL_COND_V(!windows.has(p_window), Vector3i()); const WindowData &wd = windows[p_window]; if (!wd.window_button_view) { - return Vector2i(); + return Vector3i(); } - float max_x = wd.wb_offset.x + [wd.window_button_view frame].size.width; + float scale = screen_get_max_scale(); + float max_x = [wd.window_button_view getOffset].x + [wd.window_button_view frame].size.width; + float max_y = [wd.window_button_view getOffset].y + [wd.window_button_view frame].size.height; if ([wd.window_object windowTitlebarLayoutDirection] == NSUserInterfaceLayoutDirectionRightToLeft) { - return Vector2i(0, max_x * screen_get_max_scale()); + return Vector3i(0, max_x * scale, max_y * scale); } else { - return Vector2i(max_x * screen_get_max_scale(), 0); + return Vector3i(max_x * scale, 0, max_y * scale); } } @@ -2924,6 +2961,14 @@ int64_t DisplayServerMacOS::window_get_native_handle(HandleType p_handle_type, W case WINDOW_VIEW: { return (int64_t)windows[p_window].window_view; } +#ifdef GLES3_ENABLED + case OPENGL_CONTEXT: { + if (gl_manager) { + return (int64_t)gl_manager->get_context(p_window); + } + return 0; + } +#endif default: { return 0; } @@ -3397,10 +3442,29 @@ void DisplayServerMacOS::set_icon(const Ref<Image> &p_icon) { [NSApp setApplicationIconImage:nsimg]; } -DisplayServer *DisplayServerMacOS::create_func(const String &p_rendering_driver, WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i &p_resolution, Error &r_error) { - DisplayServer *ds = memnew(DisplayServerMacOS(p_rendering_driver, p_mode, p_vsync_mode, p_flags, p_resolution, r_error)); +DisplayServer *DisplayServerMacOS::create_func(const String &p_rendering_driver, WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i *p_position, const Vector2i &p_resolution, Error &r_error) { + DisplayServer *ds = memnew(DisplayServerMacOS(p_rendering_driver, p_mode, p_vsync_mode, p_flags, p_position, p_resolution, r_error)); if (r_error != OK) { - OS::get_singleton()->alert("Your video card driver does not support any of the supported Vulkan or OpenGL versions.", "Unable to initialize Video driver"); + if (p_rendering_driver == "vulkan") { + String executable_command; + if (OS::get_singleton()->get_bundle_resource_dir() == OS::get_singleton()->get_executable_path().get_base_dir()) { + executable_command = vformat("%s --rendering-driver opengl3", OS::get_singleton()->get_executable_path()); + } else { + executable_command = vformat("open %s --args --rendering-driver opengl3", OS::get_singleton()->get_bundle_resource_dir().path_join("../..").simplify_path()); + } + OS::get_singleton()->alert("Your video card driver does not support the selected Vulkan version.\n" + "Please try updating your GPU driver or try using the OpenGL 3 driver.\n" + "You can enable the OpenGL 3 driver by starting the engine from the\n" + "command line with the command: '" + + executable_command + "'.\n" + "If you have updated your graphics drivers recently, try rebooting.", + "Unable to initialize Video driver"); + } else { + OS::get_singleton()->alert("Your video card driver does not support the selected OpenGL version.\n" + "Please try updating your GPU driver.\n" + "If you have updated your graphics drivers recently, try rebooting.", + "Unable to initialize Video driver"); + } } return ds; } @@ -3548,7 +3612,7 @@ bool DisplayServerMacOS::mouse_process_popups(bool p_close) { return closed; } -DisplayServerMacOS::DisplayServerMacOS(const String &p_rendering_driver, WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i &p_resolution, Error &r_error) { +DisplayServerMacOS::DisplayServerMacOS(const String &p_rendering_driver, WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i *p_position, const Vector2i &p_resolution, Error &r_error) { Input::get_singleton()->set_event_dispatch_function(_dispatch_input_events); r_error = OK; @@ -3657,6 +3721,11 @@ DisplayServerMacOS::DisplayServerMacOS(const String &p_rendering_driver, WindowM Point2i window_position( screen_get_position(0).x + (screen_get_size(0).width - p_resolution.width) / 2, screen_get_position(0).y + (screen_get_size(0).height - p_resolution.height) / 2); + + if (p_position != nullptr) { + window_position = *p_position; + } + WindowID main_window = _create_window(p_mode, p_vsync_mode, Rect2i(window_position, p_resolution)); ERR_FAIL_COND(main_window == INVALID_WINDOW_ID); for (int i = 0; i < WINDOW_FLAG_MAX; i++) { diff --git a/platform/macos/export/export_plugin.cpp b/platform/macos/export/export_plugin.cpp index 070830c486..de6016cb9b 100644 --- a/platform/macos/export/export_plugin.cpp +++ b/platform/macos/export/export_plugin.cpp @@ -31,6 +31,8 @@ #include "export_plugin.h" #include "codesign.h" +#include "lipo.h" +#include "macho.h" #include "core/string/translation.h" #include "editor/editor_node.h" @@ -381,7 +383,7 @@ void EditorExportPlatformMacOS::_fix_plist(const Ref<EditorExportPreset> &p_pres if (lines[i].find("$binary") != -1) { strnew += lines[i].replace("$binary", p_binary) + "\n"; } else if (lines[i].find("$name") != -1) { - strnew += lines[i].replace("$name", ProjectSettings::get_singleton()->get("application/config/name")) + "\n"; + strnew += lines[i].replace("$name", GLOBAL_GET("application/config/name")) + "\n"; } else if (lines[i].find("$bundle_identifier") != -1) { strnew += lines[i].replace("$bundle_identifier", p_preset->get("application/bundle_identifier")) + "\n"; } else if (lines[i].find("$short_version") != -1) { @@ -471,7 +473,7 @@ Error EditorExportPlatformMacOS::_notarize(const Ref<EditorExportPreset> &p_pres case 1: { // "rcodesign" print_verbose("using rcodesign notarization..."); - String rcodesign = EditorSettings::get_singleton()->get("export/macos/rcodesign").operator String(); + String rcodesign = EDITOR_GET("export/macos/rcodesign").operator String(); if (rcodesign.is_empty()) { add_message(EXPORT_MESSAGE_ERROR, TTR("Notarization"), TTR("rcodesign path is not set. Configure rcodesign path in the Editor Settings (Export > macOS > rcodesign).")); return Error::FAILED; @@ -634,7 +636,7 @@ Error EditorExportPlatformMacOS::_code_sign(const Ref<EditorExportPreset> &p_pre case 2: { // "rcodesign" print_verbose("using rcodesign codesign..."); - String rcodesign = EditorSettings::get_singleton()->get("export/macos/rcodesign").operator String(); + String rcodesign = EDITOR_GET("export/macos/rcodesign").operator String(); if (rcodesign.is_empty()) { add_message(EXPORT_MESSAGE_ERROR, TTR("Code Signing"), TTR("Xrcodesign path is not set. Configure rcodesign path in the Editor Settings (Export > macOS > rcodesign).")); return Error::FAILED; @@ -754,6 +756,7 @@ Error EditorExportPlatformMacOS::_code_sign_directory(const Ref<EditorExportPres if (extensions_to_sign.is_empty()) { extensions_to_sign.push_back("dylib"); extensions_to_sign.push_back("framework"); + extensions_to_sign.push_back(""); } Error dir_access_error; @@ -778,6 +781,10 @@ Error EditorExportPlatformMacOS::_code_sign_directory(const Ref<EditorExportPres if (code_sign_error != OK) { return code_sign_error; } + if (is_executable(current_file_path)) { + // chmod with 0755 if the file is executable. + FileAccess::set_unix_permissions(current_file_path, 0755); + } } else if (dir_access->current_is_dir()) { Error code_sign_error{ _code_sign_directory(p_preset, current_file_path, p_ent_path, p_should_error_on_non_code) }; if (code_sign_error != OK) { @@ -799,6 +806,14 @@ Error EditorExportPlatformMacOS::_copy_and_sign_files(Ref<DirAccess> &dir_access const String &p_in_app_path, bool p_sign_enabled, const Ref<EditorExportPreset> &p_preset, const String &p_ent_path, bool p_should_error_on_non_code_sign) { + static Vector<String> extensions_to_sign; + + if (extensions_to_sign.is_empty()) { + extensions_to_sign.push_back("dylib"); + extensions_to_sign.push_back("framework"); + extensions_to_sign.push_back(""); + } + Error err{ OK }; if (dir_access->dir_exists(p_src_path)) { #ifndef UNIX_ENABLED @@ -818,7 +833,13 @@ Error EditorExportPlatformMacOS::_copy_and_sign_files(Ref<DirAccess> &dir_access // If it is a directory, find and sign all dynamic libraries. err = _code_sign_directory(p_preset, p_in_app_path, p_ent_path, p_should_error_on_non_code_sign); } else { - err = _code_sign(p_preset, p_in_app_path, p_ent_path, false); + if (extensions_to_sign.find(p_in_app_path.get_extension()) > -1) { + err = _code_sign(p_preset, p_in_app_path, p_ent_path, false); + } + if (is_executable(p_in_app_path)) { + // chmod with 0755 if the file is executable. + FileAccess::set_unix_permissions(p_in_app_path, 0755); + } } } return err; @@ -877,6 +898,17 @@ Error EditorExportPlatformMacOS::_create_dmg(const String &p_dmg_path, const Str return OK; } +bool EditorExportPlatformMacOS::is_shbang(const String &p_path) const { + Ref<FileAccess> fb = FileAccess::open(p_path, FileAccess::READ); + ERR_FAIL_COND_V_MSG(fb.is_null(), false, vformat("Can't open file: \"%s\".", p_path)); + uint16_t magic = fb->get_16(); + return (magic == 0x2123); +} + +bool EditorExportPlatformMacOS::is_executable(const String &p_path) const { + return MachO::is_macho(p_path) || LipO::is_lipo(p_path) || is_shbang(p_path); +} + Error EditorExportPlatformMacOS::_export_debug_script(const Ref<EditorExportPreset> &p_preset, const String &p_app_name, const String &p_pkg_name, const String &p_path) { Ref<FileAccess> f = FileAccess::open(p_path, FileAccess::WRITE); if (f.is_null()) { @@ -950,8 +982,8 @@ Error EditorExportPlatformMacOS::export_project(const Ref<EditorExportPreset> &p String binary_to_use = "godot_macos_" + String(p_debug ? "debug" : "release") + "." + architecture; String pkg_name; - if (String(ProjectSettings::get_singleton()->get("application/config/name")) != "") { - pkg_name = String(ProjectSettings::get_singleton()->get("application/config/name")); + if (String(GLOBAL_GET("application/config/name")) != "") { + pkg_name = String(GLOBAL_GET("application/config/name")); } else { pkg_name = "Unnamed"; } @@ -1041,7 +1073,7 @@ Error EditorExportPlatformMacOS::export_project(const Ref<EditorExportPreset> &p } } - Dictionary appnames = ProjectSettings::get_singleton()->get("application/config/name_localized"); + Dictionary appnames = GLOBAL_GET("application/config/name_localized"); Dictionary microphone_usage_descriptions = p_preset->get("privacy/microphone_usage_description_localized"); Dictionary camera_usage_descriptions = p_preset->get("privacy/camera_usage_description_localized"); Dictionary location_usage_descriptions = p_preset->get("privacy/location_usage_description_localized"); @@ -1055,7 +1087,7 @@ Error EditorExportPlatformMacOS::export_project(const Ref<EditorExportPreset> &p Dictionary removable_volumes_usage_descriptions = p_preset->get("privacy/removable_volumes_usage_description_localized"); Dictionary copyrights = p_preset->get("application/copyright_localized"); - Vector<String> translations = ProjectSettings::get_singleton()->get("internationalization/locale/translations"); + Vector<String> translations = GLOBAL_GET("internationalization/locale/translations"); if (translations.size() > 0) { { String fname = tmp_app_path_name + "/Contents/Resources/en.lproj"; @@ -1063,7 +1095,7 @@ Error EditorExportPlatformMacOS::export_project(const Ref<EditorExportPreset> &p Ref<FileAccess> f = FileAccess::open(fname + "/InfoPlist.strings", FileAccess::WRITE); f->store_line("/* Localized versions of Info.plist keys */"); f->store_line(""); - f->store_line("CFBundleDisplayName = \"" + ProjectSettings::get_singleton()->get("application/config/name").operator String() + "\";"); + f->store_line("CFBundleDisplayName = \"" + GLOBAL_GET("application/config/name").operator String() + "\";"); if (!((String)p_preset->get("privacy/microphone_usage_description")).is_empty()) { f->store_line("NSMicrophoneUsageDescription = \"" + p_preset->get("privacy/microphone_usage_description").operator String() + "\";"); } @@ -1158,11 +1190,8 @@ Error EditorExportPlatformMacOS::export_project(const Ref<EditorExportPreset> &p // Now process our template. bool found_binary = false; - Vector<String> dylibs_found; while (ret == UNZ_OK && err == OK) { - bool is_execute = false; - // Get filename. unz_file_info info; char fname[16384]; @@ -1219,7 +1248,6 @@ Error EditorExportPlatformMacOS::export_project(const Ref<EditorExportPreset> &p continue; // skip } found_binary = true; - is_execute = true; file = "Contents/MacOS/" + pkg_name; } @@ -1229,7 +1257,7 @@ Error EditorExportPlatformMacOS::export_project(const Ref<EditorExportPreset> &p if (p_preset->get("application/icon") != "") { iconpath = p_preset->get("application/icon"); } else { - iconpath = ProjectSettings::get_singleton()->get("application/config/icon"); + iconpath = GLOBAL_GET("application/config/icon"); } if (!iconpath.is_empty()) { @@ -1251,25 +1279,6 @@ Error EditorExportPlatformMacOS::export_project(const Ref<EditorExportPreset> &p } if (data.size() > 0) { - if (file.find("/data.mono.macos.release_debug." + architecture + "/") != -1) { - if (!p_debug) { - ret = unzGoToNextFile(src_pkg_zip); - continue; // skip - } - file = file.replace("/data.mono.macos.release_debug." + architecture + "/", "/GodotSharp/"); - } - if (file.find("/data.mono.macos.release." + architecture + "/") != -1) { - if (p_debug) { - ret = unzGoToNextFile(src_pkg_zip); - continue; // skip - } - file = file.replace("/data.mono.macos.release." + architecture + "/", "/GodotSharp/"); - } - - if (file.ends_with(".dylib")) { - dylibs_found.push_back(file); - } - print_verbose("ADDING: " + file + " size: " + itos(data.size())); // Write it into our application bundle. @@ -1285,7 +1294,7 @@ Error EditorExportPlatformMacOS::export_project(const Ref<EditorExportPreset> &p if (f.is_valid()) { f->store_buffer(data.ptr(), data.size()); f.unref(); - if (is_execute) { + if (is_executable(file)) { // chmod with 0755 if the file is executable. FileAccess::set_unix_permissions(file, 0755); } @@ -1324,12 +1333,35 @@ Error EditorExportPlatformMacOS::export_project(const Ref<EditorExportPreset> &p return ERR_SKIP; } + // See if we can code sign our new package. + bool sign_enabled = (p_preset->get("codesign/codesign").operator int() > 0); + bool ad_hoc = false; + int codesign_tool = p_preset->get("codesign/codesign"); + switch (codesign_tool) { + case 1: { // built-in ad-hoc + ad_hoc = true; + } break; + case 2: { // "rcodesign" + ad_hoc = p_preset->get("codesign/certificate_file").operator String().is_empty() || p_preset->get("codesign/certificate_password").operator String().is_empty(); + } break; +#ifdef MACOS_ENABLED + case 3: { // "codesign" + ad_hoc = (p_preset->get("codesign/identity") == "" || p_preset->get("codesign/identity") == "-"); + } break; +#endif + default: { + }; + } + String pack_path = tmp_app_path_name + "/Contents/Resources/" + pkg_name + ".pck"; Vector<SharedObject> shared_objects; err = save_pack(p_preset, p_debug, pack_path, &shared_objects); - // See if we can code sign our new package. - bool sign_enabled = (p_preset->get("codesign/codesign").operator int() > 0); + bool lib_validation = p_preset->get("codesign/entitlements/disable_library_validation"); + if (!shared_objects.is_empty() && sign_enabled && ad_hoc && !lib_validation) { + add_message(EXPORT_MESSAGE_INFO, TTR("Entitlements Modified"), TTR("Ad-hoc signed applications require the 'Disable Library Validation' entitlement to load dynamic libraries.")); + lib_validation = true; + } String ent_path = p_preset->get("codesign/entitlements/custom_file"); String hlp_ent_path = EditorPaths::get_singleton()->get_cache_dir().path_join(pkg_name + "_helper.entitlements"); @@ -1365,7 +1397,7 @@ Error EditorExportPlatformMacOS::export_project(const Ref<EditorExportPreset> &p } } - if ((bool)p_preset->get("codesign/entitlements/disable_library_validation")) { + if (lib_validation) { ent_f->store_line("<key>com.apple.security.cs.disable-library-validation</key>"); ent_f->store_line("<true/>"); } @@ -1495,32 +1527,6 @@ Error EditorExportPlatformMacOS::export_project(const Ref<EditorExportPreset> &p } } - bool ad_hoc = false; - int codesign_tool = p_preset->get("codesign/codesign"); - switch (codesign_tool) { - case 1: { // built-in ad-hoc - ad_hoc = true; - } break; - case 2: { // "rcodesign" - ad_hoc = p_preset->get("codesign/certificate_file").operator String().is_empty() || p_preset->get("codesign/certificate_password").operator String().is_empty(); - } break; -#ifdef MACOS_ENABLED - case 3: { // "codesign" - ad_hoc = (p_preset->get("codesign/identity") == "" || p_preset->get("codesign/identity") == "-"); - } break; -#endif - default: { - }; - } - - if (err == OK) { - bool lib_validation = p_preset->get("codesign/entitlements/disable_library_validation"); - if ((!dylibs_found.is_empty() || !shared_objects.is_empty()) && sign_enabled && ad_hoc && !lib_validation) { - add_message(EXPORT_MESSAGE_ERROR, TTR("Code Signing"), TTR("Ad-hoc signed applications require the 'Disable Library Validation' entitlement to load dynamic libraries.")); - err = ERR_CANT_CREATE; - } - } - if (err == OK) { Ref<DirAccess> da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM); for (int i = 0; i < shared_objects.size(); i++) { @@ -1529,8 +1535,9 @@ Error EditorExportPlatformMacOS::export_project(const Ref<EditorExportPreset> &p String path_in_app = tmp_app_path_name + "/Contents/Frameworks/" + src_path.get_file(); err = _copy_and_sign_files(da, src_path, path_in_app, sign_enabled, p_preset, ent_path, true); } else { - String path_in_app = tmp_app_path_name.path_join(shared_objects[i].target).path_join(src_path.get_file()); - err = _copy_and_sign_files(da, src_path, path_in_app, sign_enabled, p_preset, ent_path, false); + String path_in_app = tmp_app_path_name.path_join(shared_objects[i].target); + tmp_app_dir->make_dir_recursive(path_in_app); + err = _copy_and_sign_files(da, src_path, path_in_app.path_join(src_path.get_file()), sign_enabled, p_preset, ent_path, false); } if (err != OK) { break; @@ -1546,14 +1553,6 @@ Error EditorExportPlatformMacOS::export_project(const Ref<EditorExportPreset> &p } } - if (sign_enabled) { - for (int i = 0; i < dylibs_found.size(); i++) { - if (err == OK) { - err = _code_sign(p_preset, tmp_app_path_name + "/" + dylibs_found[i], ent_path, false); - } - } - } - if (err == OK && sign_enabled) { if (ep.step(TTR("Code signing bundle"), 2)) { return ERR_SKIP; @@ -1683,8 +1682,6 @@ void EditorExportPlatformMacOS::_zip_folder_recursive(zipFile &p_zip, const Stri } else if (da->current_is_dir()) { _zip_folder_recursive(p_zip, p_root_path, p_folder.path_join(f), p_pkg_name); } else { - bool is_executable = (p_folder.ends_with("MacOS") && (f == p_pkg_name)) || p_folder.ends_with("Helpers") || f.ends_with(".command"); - OS::DateTime dt = OS::get_singleton()->get_datetime(); zip_fileinfo zipfi; @@ -1698,7 +1695,7 @@ void EditorExportPlatformMacOS::_zip_folder_recursive(zipFile &p_zip, const Stri // 0100000: regular file type // 0000755: permissions rwxr-xr-x // 0000644: permissions rw-r--r-- - uint32_t _mode = (is_executable ? 0100755 : 0100644); + uint32_t _mode = (is_executable(dir.path_join(f)) ? 0100755 : 0100644); zipfi.external_fa = (_mode << 16L) | !(_mode & 0200); zipfi.internal_fa = 0; @@ -1855,7 +1852,7 @@ bool EditorExportPlatformMacOS::has_valid_project_configuration(const Ref<Editor valid = false; } - String rcodesign = EditorSettings::get_singleton()->get("export/macos/rcodesign").operator String(); + String rcodesign = EDITOR_GET("export/macos/rcodesign").operator String(); if (rcodesign.is_empty()) { err += TTR("Notarization: rcodesign path is not set. Configure rcodesign path in the Editor Settings (Export > macOS > rcodesign).") + "\n"; valid = false; @@ -1878,7 +1875,7 @@ bool EditorExportPlatformMacOS::has_valid_project_configuration(const Ref<Editor valid = false; } } else if (codesign_tool == 2) { - String rcodesign = EditorSettings::get_singleton()->get("export/macos/rcodesign").operator String(); + String rcodesign = EDITOR_GET("export/macos/rcodesign").operator String(); if (rcodesign.is_empty()) { err += TTR("Code signing: rcodesign path is not set. Configure rcodesign path in the Editor Settings (Export > macOS > rcodesign).") + "\n"; valid = false; diff --git a/platform/macos/export/export_plugin.h b/platform/macos/export/export_plugin.h index 87790129d3..b6ad587caa 100644 --- a/platform/macos/export/export_plugin.h +++ b/platform/macos/export/export_plugin.h @@ -97,6 +97,7 @@ class EditorExportPlatformMacOS : public EditorExportPlatform { return true; } + bool is_shbang(const String &p_path) const; protected: virtual void get_preset_features(const Ref<EditorExportPreset> &p_preset, List<String> *r_features) const override; @@ -108,6 +109,7 @@ public: virtual String get_os_name() const override { return "macOS"; } virtual Ref<Texture2D> get_logo() const override { return logo; } + virtual bool is_executable(const String &p_path) const override; virtual List<String> get_binary_extensions(const Ref<EditorExportPreset> &p_preset) const override { List<String> list; if (use_dmg()) { diff --git a/platform/macos/export/lipo.cpp b/platform/macos/export/lipo.cpp index 82baf18c52..76d4eee418 100644 --- a/platform/macos/export/lipo.cpp +++ b/platform/macos/export/lipo.cpp @@ -28,12 +28,8 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#include "modules/modules_enabled.gen.h" // For regex. - #include "lipo.h" -#ifdef MODULE_REGEX_ENABLED - bool LipO::is_lipo(const String &p_path) { Ref<FileAccess> fb = FileAccess::open(p_path, FileAccess::READ); ERR_FAIL_COND_V_MSG(fb.is_null(), false, vformat("LipO: Can't open file: \"%s\".", p_path)); @@ -232,5 +228,3 @@ void LipO::close() { LipO::~LipO() { close(); } - -#endif // MODULE_REGEX_ENABLED diff --git a/platform/macos/export/lipo.h b/platform/macos/export/lipo.h index 6378f9899c..bd8b7f6f2c 100644 --- a/platform/macos/export/lipo.h +++ b/platform/macos/export/lipo.h @@ -35,12 +35,9 @@ #include "core/io/file_access.h" #include "core/object/ref_counted.h" -#include "modules/modules_enabled.gen.h" // For regex. #include "macho.h" -#ifdef MODULE_REGEX_ENABLED - class LipO : public RefCounted { struct FatArch { uint32_t cputype; @@ -71,6 +68,4 @@ public: ~LipO(); }; -#endif // MODULE_REGEX_ENABLED - #endif // MACOS_LIPO_H diff --git a/platform/macos/export/macho.cpp b/platform/macos/export/macho.cpp index e6e67eff06..642d99e098 100644 --- a/platform/macos/export/macho.cpp +++ b/platform/macos/export/macho.cpp @@ -28,24 +28,20 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#include "modules/modules_enabled.gen.h" // For regex. - #include "macho.h" -#ifdef MODULE_REGEX_ENABLED - uint32_t MachO::seg_align(uint64_t p_vmaddr, uint32_t p_min, uint32_t p_max) { - uint32_t align = p_max; + uint32_t salign = p_max; if (p_vmaddr != 0) { uint64_t seg_align = 1; - align = 0; + salign = 0; while ((seg_align & p_vmaddr) == 0) { seg_align = seg_align << 1; - align++; + salign++; } - align = CLAMP(align, p_min, p_max); + salign = CLAMP(salign, p_min, p_max); } - return align; + return salign; } bool MachO::alloc_signature(uint64_t p_size) { @@ -544,5 +540,3 @@ bool MachO::set_signature_size(uint64_t p_size) { } return true; } - -#endif // MODULE_REGEX_ENABLED diff --git a/platform/macos/export/macho.h b/platform/macos/export/macho.h index 0d42a7013f..0c954e66b1 100644 --- a/platform/macos/export/macho.h +++ b/platform/macos/export/macho.h @@ -37,9 +37,6 @@ #include "core/crypto/crypto_core.h" #include "core/io/file_access.h" #include "core/object/ref_counted.h" -#include "modules/modules_enabled.gen.h" // For regex. - -#ifdef MODULE_REGEX_ENABLED class MachO : public RefCounted { struct MachHeader { @@ -210,6 +207,4 @@ public: bool set_signature_size(uint64_t p_size); }; -#endif // MODULE_REGEX_ENABLED - #endif // MACOS_MACHO_H diff --git a/platform/macos/export/plist.cpp b/platform/macos/export/plist.cpp index 36de9dd34b..cad014e65b 100644 --- a/platform/macos/export/plist.cpp +++ b/platform/macos/export/plist.cpp @@ -28,12 +28,8 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#include "modules/modules_enabled.gen.h" // For regex. - #include "plist.h" -#ifdef MODULE_REGEX_ENABLED - Ref<PListNode> PListNode::new_array() { Ref<PListNode> node = memnew(PListNode()); ERR_FAIL_COND_V(node.is_null(), Ref<PListNode>()); @@ -566,5 +562,3 @@ String PList::save_text() const { Ref<PListNode> PList::get_root() { return root; } - -#endif // MODULE_REGEX_ENABLED diff --git a/platform/macos/export/plist.h b/platform/macos/export/plist.h index b3c51a9635..97331a3629 100644 --- a/platform/macos/export/plist.h +++ b/platform/macos/export/plist.h @@ -35,9 +35,6 @@ #include "core/crypto/crypto_core.h" #include "core/io/file_access.h" -#include "modules/modules_enabled.gen.h" // For regex. - -#ifdef MODULE_REGEX_ENABLED class PListNode; @@ -111,6 +108,4 @@ public: ~PListNode() {} }; -#endif // MODULE_REGEX_ENABLED - #endif // MACOS_PLIST_H diff --git a/platform/macos/gl_manager_macos_legacy.h b/platform/macos/gl_manager_macos_legacy.h index 8752086551..d7ad5b1197 100644 --- a/platform/macos/gl_manager_macos_legacy.h +++ b/platform/macos/gl_manager_macos_legacy.h @@ -89,6 +89,8 @@ public: void set_use_vsync(bool p_use); bool is_using_vsync() const; + NSOpenGLContext *get_context(DisplayServer::WindowID p_window_id); + GLManager_MacOS(ContextType p_context_type); ~GLManager_MacOS(); }; diff --git a/platform/macos/gl_manager_macos_legacy.mm b/platform/macos/gl_manager_macos_legacy.mm index dec4821b86..ea5c36da6c 100644 --- a/platform/macos/gl_manager_macos_legacy.mm +++ b/platform/macos/gl_manager_macos_legacy.mm @@ -215,6 +215,15 @@ bool GLManager_MacOS::is_using_vsync() const { return use_vsync; } +NSOpenGLContext *GLManager_MacOS::get_context(DisplayServer::WindowID p_window_id) { + if (!windows.has(p_window_id)) { + return nullptr; + } + + GLWindow &win = windows[p_window_id]; + return win.context; +} + GLManager_MacOS::GLManager_MacOS(ContextType p_context_type) { context_type = p_context_type; } diff --git a/platform/macos/godot_button_view.h b/platform/macos/godot_button_view.h index e7627a9e9b..ef1d5fe412 100644 --- a/platform/macos/godot_button_view.h +++ b/platform/macos/godot_button_view.h @@ -42,10 +42,15 @@ CGFloat spacing; bool mouse_in_group; bool rtl; + NSButton *close_button; + NSButton *miniaturize_button; + NSButton *zoom_button; } - (void)initButtons:(CGFloat)button_spacing offset:(NSPoint)button_offset rtl:(bool)is_rtl; - (void)displayButtons; +- (void)setOffset:(NSPoint)button_offset; +- (NSPoint)getOffset; @end diff --git a/platform/macos/godot_button_view.mm b/platform/macos/godot_button_view.mm index 9106f0b0db..7d380cbe11 100644 --- a/platform/macos/godot_button_view.mm +++ b/platform/macos/godot_button_view.mm @@ -40,6 +40,9 @@ spacing = 20; mouse_in_group = false; rtl = false; + close_button = nullptr; + miniaturize_button = nullptr; + zoom_button = nullptr; return self; } @@ -48,15 +51,15 @@ spacing = button_spacing; rtl = is_rtl; - NSButton *close_button = [NSWindow standardWindowButton:NSWindowCloseButton forStyleMask:NSWindowStyleMaskTitled]; + close_button = [NSWindow standardWindowButton:NSWindowCloseButton forStyleMask:NSWindowStyleMaskTitled]; [close_button setFrameOrigin:NSMakePoint(rtl ? spacing * 2 : 0, 0)]; [self addSubview:close_button]; - NSButton *miniaturize_button = [NSWindow standardWindowButton:NSWindowMiniaturizeButton forStyleMask:NSWindowStyleMaskTitled]; + miniaturize_button = [NSWindow standardWindowButton:NSWindowMiniaturizeButton forStyleMask:NSWindowStyleMaskTitled]; [miniaturize_button setFrameOrigin:NSMakePoint(spacing, 0)]; [self addSubview:miniaturize_button]; - NSButton *zoom_button = [NSWindow standardWindowButton:NSWindowZoomButton forStyleMask:NSWindowStyleMaskTitled]; + zoom_button = [NSWindow standardWindowButton:NSWindowZoomButton forStyleMask:NSWindowStyleMaskTitled]; [zoom_button setFrameOrigin:NSMakePoint(rtl ? 0 : spacing * 2, 0)]; [self addSubview:zoom_button]; @@ -71,6 +74,19 @@ [self displayButtons]; } +- (void)setOffset:(NSPoint)button_offset { + if (zoom_button) { + offset.y = button_offset.y - zoom_button.frame.size.height / 2; + offset.x = button_offset.x - zoom_button.frame.size.width / 2; + + [self viewDidMoveToWindow]; + } +} + +- (NSPoint)getOffset { + return offset; +} + - (void)viewDidMoveToWindow { if (!self.window) { return; diff --git a/platform/macos/godot_window_delegate.mm b/platform/macos/godot_window_delegate.mm index 279fd2a359..3bdbc8c5ec 100644 --- a/platform/macos/godot_window_delegate.mm +++ b/platform/macos/godot_window_delegate.mm @@ -147,7 +147,12 @@ } DisplayServerMacOS::WindowData &wd = ds->get_window(window_id); + if (wd.exclusive_fullscreen) { + [NSApp setPresentationOptions:NSApplicationPresentationDefault]; + } + wd.fullscreen = false; + wd.exclusive_fullscreen = false; [(GodotWindow *)wd.window_object setAnimDuration:-1.0f]; diff --git a/platform/macos/joypad_macos.h b/platform/macos/joypad_macos.h index 4b14fed6d5..8743fc91a9 100644 --- a/platform/macos/joypad_macos.h +++ b/platform/macos/joypad_macos.h @@ -31,14 +31,10 @@ #ifndef JOYPAD_MACOS_H #define JOYPAD_MACOS_H -#ifdef MACOS_10_0_4 -#import <IOKit/hidsystem/IOHIDUsageTables.h> -#else -#import <Kernel/IOKit/hidsystem/IOHIDUsageTables.h> -#endif #import <ForceFeedback/ForceFeedback.h> #import <ForceFeedback/ForceFeedbackConstants.h> #import <IOKit/hid/IOHIDLib.h> +#import <Kernel/IOKit/hidsystem/IOHIDUsageTables.h> #include "core/input/input.h" diff --git a/platform/macos/os_macos.mm b/platform/macos/os_macos.mm index ae8534f6ab..e620b058d3 100644 --- a/platform/macos/os_macos.mm +++ b/platform/macos/os_macos.mm @@ -148,9 +148,11 @@ void OS_MacOS::alert(const String &p_alert, const String &p_title) { NSString *ns_title = [NSString stringWithUTF8String:p_title.utf8().get_data()]; NSString *ns_alert = [NSString stringWithUTF8String:p_alert.utf8().get_data()]; + NSTextField *text_field = [NSTextField labelWithString:ns_alert]; + [text_field setAlignment:NSTextAlignmentCenter]; [window addButtonWithTitle:@"OK"]; [window setMessageText:ns_title]; - [window setInformativeText:ns_alert]; + [window setAccessoryView:text_field]; [window setAlertStyle:NSAlertStyleWarning]; id key_window = [[NSApplication sharedApplication] keyWindow]; @@ -497,7 +499,14 @@ String OS_MacOS::get_unique_id() const { } bool OS_MacOS::_check_internal_feature_support(const String &p_feature) { - return p_feature == "pc"; + if (p_feature == "system_fonts") { + return true; + } + if (p_feature == "pc") { + return true; + } + + return false; } void OS_MacOS::disable_crash_handler() { diff --git a/platform/macos/platform_config.h b/platform/macos/platform_config.h index e114606b82..46c46b8803 100644 --- a/platform/macos/platform_config.h +++ b/platform/macos/platform_config.h @@ -30,5 +30,5 @@ #include <alloca.h> -#define OPENGL_INCLUDE_H "thirdparty/glad/glad/glad.h" +#define OPENGL_INCLUDE_H "thirdparty/glad/glad/gl.h" #define PTHREAD_RENAME_SELF diff --git a/platform/macos/vulkan_context_macos.h b/platform/macos/vulkan_context_macos.h index 579c42b042..2a81336994 100644 --- a/platform/macos/vulkan_context_macos.h +++ b/platform/macos/vulkan_context_macos.h @@ -31,6 +31,8 @@ #ifndef VULKAN_CONTEXT_MACOS_H #define VULKAN_CONTEXT_MACOS_H +#ifdef VULKAN_ENABLED + #include "drivers/vulkan/vulkan_context.h" #import <AppKit/AppKit.h> @@ -44,4 +46,6 @@ public: ~VulkanContextMacOS(); }; +#endif // VULKAN_ENABLED + #endif // VULKAN_CONTEXT_MACOS_H diff --git a/platform/macos/vulkan_context_macos.mm b/platform/macos/vulkan_context_macos.mm index cf317f3c68..1df6b3ed18 100644 --- a/platform/macos/vulkan_context_macos.mm +++ b/platform/macos/vulkan_context_macos.mm @@ -28,6 +28,7 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ +#ifdef VULKAN_ENABLED #include "vulkan_context_macos.h" #ifdef USE_VOLK #include <volk.h> @@ -57,3 +58,5 @@ VulkanContextMacOS::VulkanContextMacOS() { VulkanContextMacOS::~VulkanContextMacOS() { } + +#endif // VULKAN_ENABLED diff --git a/platform/uwp/detect.py b/platform/uwp/detect.py index 2c5746cb06..64fe5bc4a2 100644 --- a/platform/uwp/detect.py +++ b/platform/uwp/detect.py @@ -3,6 +3,11 @@ import os import sys from platform_methods import detect_arch +from typing import TYPE_CHECKING + +if TYPE_CHECKING: + from SCons import Environment + def is_active(): return True @@ -39,7 +44,7 @@ def get_flags(): ] -def configure(env): +def configure(env: "Environment"): # Validate arch. supported_arches = ["x86_32", "x86_64", "arm32"] if env["arch"] not in supported_arches: @@ -83,7 +88,7 @@ def configure(env): env.AppendUnique(CCFLAGS=["/utf-8"]) # ANGLE - angle_root = os.getenv("ANGLE_SRC_PATH") + angle_root = os.environ["ANGLE_SRC_PATH"] env.Prepend(CPPPATH=[angle_root + "/include"]) jobs = str(env.GetOption("num_jobs")) angle_build_cmd = ( @@ -94,7 +99,7 @@ def configure(env): + " /p:Configuration=Release /p:Platform=" ) - if os.path.isfile(str(os.getenv("ANGLE_SRC_PATH")) + "/winrt/10/src/angle.sln"): + if os.path.isfile(f"{angle_root}/winrt/10/src/angle.sln"): env["build_angle"] = True ## Architecture diff --git a/platform/uwp/export/app_packager.cpp b/platform/uwp/export/app_packager.cpp index 87224d38b8..6f8966b9ff 100644 --- a/platform/uwp/export/app_packager.cpp +++ b/platform/uwp/export/app_packager.cpp @@ -301,7 +301,7 @@ Error AppxPackager::add_file(String p_file_name, const uint8_t *p_buffer, size_t Vector<uint8_t> file_buffer; // Data for compression - z_stream strm; + z_stream strm{}; Vector<uint8_t> strm_in; strm_in.resize(BLOCK_SIZE); Vector<uint8_t> strm_out; diff --git a/platform/uwp/export/export_plugin.cpp b/platform/uwp/export/export_plugin.cpp index 4e4afb9704..ab0b20762f 100644 --- a/platform/uwp/export/export_plugin.cpp +++ b/platform/uwp/export/export_plugin.cpp @@ -442,7 +442,7 @@ Error EditorExportPlatformUWP::export_project(const Ref<EditorExportPreset> &p_p #ifdef WINDOWS_ENABLED // Sign with signtool - String signtool_path = EditorSettings::get_singleton()->get("export/uwp/signtool"); + String signtool_path = EDITOR_GET("export/uwp/signtool"); if (signtool_path.is_empty()) { return OK; } @@ -454,9 +454,9 @@ Error EditorExportPlatformUWP::export_project(const Ref<EditorExportPreset> &p_p static String algs[] = { "MD5", "SHA1", "SHA256" }; - String cert_path = EditorSettings::get_singleton()->get("export/uwp/debug_certificate"); - String cert_pass = EditorSettings::get_singleton()->get("export/uwp/debug_password"); - int cert_alg = EditorSettings::get_singleton()->get("export/uwp/debug_algorithm"); + String cert_path = EDITOR_GET("export/uwp/debug_certificate"); + String cert_pass = EDITOR_GET("export/uwp/debug_password"); + int cert_alg = EDITOR_GET("export/uwp/debug_algorithm"); if (!p_debug) { cert_path = p_preset->get("signing/certificate"); diff --git a/platform/uwp/export/export_plugin.h b/platform/uwp/export/export_plugin.h index b0427d1a65..74b9ab4875 100644 --- a/platform/uwp/export/export_plugin.h +++ b/platform/uwp/export/export_plugin.h @@ -213,7 +213,7 @@ class EditorExportPlatformUWP : public EditorExportPlatform { String architecture = arch == "arm32" ? "arm" : (arch == "x86_32" ? "x86" : "x64"); result = result.replace("$architecture$", architecture); - result = result.replace("$display_name$", String(p_preset->get("package/display_name")).is_empty() ? (String)ProjectSettings::get_singleton()->get("application/config/name") : String(p_preset->get("package/display_name"))); + result = result.replace("$display_name$", String(p_preset->get("package/display_name")).is_empty() ? (String)GLOBAL_GET("application/config/name") : String(p_preset->get("package/display_name"))); result = result.replace("$publisher_display_name$", p_preset->get("package/publisher_display_name")); result = result.replace("$app_description$", p_preset->get("package/description")); diff --git a/platform/uwp/os_uwp.cpp b/platform/uwp/os_uwp.cpp index 8050d299f0..141c28c713 100644 --- a/platform/uwp/os_uwp.cpp +++ b/platform/uwp/os_uwp.cpp @@ -159,7 +159,7 @@ Error OS_UWP::initialize(const VideoMode &p_desired, int p_video_driver, int p_a outside = true; // FIXME: Hardcoded for now, add Vulkan support. - p_video_driver = VIDEO_DRIVER_OPENGL; + p_video_driver = RENDERING_DRIVER_OPENGL3; ContextEGL_UWP::Driver opengl_api_type = ContextEGL_UWP::GLES_2_0; bool gl_initialization_error = false; @@ -826,10 +826,6 @@ OS_UWP::OS_UWP() { pressrc = 0; old_invalid = true; mouse_mode = MOUSE_MODE_VISIBLE; -#ifdef STDOUT_FILE - stdo = fopen("stdout.txt", "wb"); -#endif - gl_context = nullptr; display_request = ref new Windows::System::Display::DisplayRequest(); @@ -847,7 +843,4 @@ OS_UWP::OS_UWP() { } OS_UWP::~OS_UWP() { -#ifdef STDOUT_FILE - fclose(stdo); -#endif } diff --git a/platform/uwp/os_uwp.h b/platform/uwp/os_uwp.h index 0c1c4a793a..c1df7827cd 100644 --- a/platform/uwp/os_uwp.h +++ b/platform/uwp/os_uwp.h @@ -42,9 +42,9 @@ #include "servers/rendering/renderer_compositor.h" #include "servers/rendering_server.h" -#include <fcntl.h> #include <io.h> #include <stdio.h> + #define WIN32_LEAN_AND_MEAN #include <windows.h> diff --git a/platform/web/.eslintrc.html.js b/platform/web/.eslintrc.html.js new file mode 100644 index 0000000000..5cb8de360a --- /dev/null +++ b/platform/web/.eslintrc.html.js @@ -0,0 +1,19 @@ +module.exports = { + "plugins": [ + "html", + "@html-eslint", + ], + "parser": "@html-eslint/parser", + "extends": ["plugin:@html-eslint/recommended", "./.eslintrc.js"], + "rules": { + "no-alert": "off", + "no-console": "off", + "@html-eslint/require-closing-tags": ["error", { "selfClosing": "never" }], + "@html-eslint/indent": ["error", "tab"], + }, + "globals": { + "Godot": true, + "Engine": true, + "$GODOT_CONFIG": true, + }, +}; diff --git a/platform/web/SCsub b/platform/web/SCsub index 013b734be2..077024507a 100644 --- a/platform/web/SCsub +++ b/platform/web/SCsub @@ -2,6 +2,20 @@ Import("env") +# The HTTP server "targets". Run with "scons p=web serve", or "scons p=web run" +if "serve" in COMMAND_LINE_TARGETS or "run" in COMMAND_LINE_TARGETS: + from serve import serve + import os + + port = os.environ.get("GODOT_WEB_TEST_PORT", 8060) + try: + port = int(port) + except Exception: + print("GODOT_WEB_TEST_PORT must be a valid integer") + sys.exit(255) + serve(env.Dir("#bin/.web_zip").abspath, port, "run" in COMMAND_LINE_TARGETS) + sys.exit(0) + web_files = [ "audio_driver_web.cpp", "display_server_web.cpp", @@ -21,6 +35,7 @@ sys_env.AddJSLibraries( "js/libs/library_godot_os.js", "js/libs/library_godot_runtime.js", "js/libs/library_godot_input.js", + "js/libs/library_godot_webgl2.js", ] ) diff --git a/platform/web/audio_driver_web.cpp b/platform/web/audio_driver_web.cpp index 0e37afc2cc..c4b27c782d 100644 --- a/platform/web/audio_driver_web.cpp +++ b/platform/web/audio_driver_web.cpp @@ -184,51 +184,6 @@ Error AudioDriverWeb::capture_stop() { return OK; } -#ifdef NO_THREADS -/// ScriptProcessorNode implementation -AudioDriverScriptProcessor *AudioDriverScriptProcessor::singleton = nullptr; - -void AudioDriverScriptProcessor::_process_callback() { - AudioDriverScriptProcessor::singleton->_audio_driver_capture(); - AudioDriverScriptProcessor::singleton->_audio_driver_process(); -} - -Error AudioDriverScriptProcessor::create(int &p_buffer_samples, int p_channels) { - if (!godot_audio_has_script_processor()) { - return ERR_UNAVAILABLE; - } - return (Error)godot_audio_script_create(&p_buffer_samples, p_channels); -} - -void AudioDriverScriptProcessor::start(float *p_out_buf, int p_out_buf_size, float *p_in_buf, int p_in_buf_size) { - godot_audio_script_start(p_in_buf, p_in_buf_size, p_out_buf, p_out_buf_size, &_process_callback); -} - -/// AudioWorkletNode implementation (no threads) -AudioDriverWorklet *AudioDriverWorklet::singleton = nullptr; - -Error AudioDriverWorklet::create(int &p_buffer_size, int p_channels) { - if (!godot_audio_has_worklet()) { - return ERR_UNAVAILABLE; - } - return (Error)godot_audio_worklet_create(p_channels); -} - -void AudioDriverWorklet::start(float *p_out_buf, int p_out_buf_size, float *p_in_buf, int p_in_buf_size) { - _audio_driver_process(); - godot_audio_worklet_start_no_threads(p_out_buf, p_out_buf_size, &_process_callback, p_in_buf, p_in_buf_size, &_capture_callback); -} - -void AudioDriverWorklet::_process_callback(int p_pos, int p_samples) { - AudioDriverWorklet *driver = AudioDriverWorklet::singleton; - driver->_audio_driver_process(p_pos, p_samples); -} - -void AudioDriverWorklet::_capture_callback(int p_pos, int p_samples) { - AudioDriverWorklet *driver = AudioDriverWorklet::singleton; - driver->_audio_driver_capture(p_pos, p_samples); -} -#else /// AudioWorkletNode implementation (threads) void AudioDriverWorklet::_audio_thread_func(void *p_data) { AudioDriverWorklet *driver = static_cast<AudioDriverWorklet *>(p_data); @@ -290,4 +245,3 @@ void AudioDriverWorklet::finish_driver() { quit = true; // Ask thread to quit. thread.wait_to_finish(); } -#endif diff --git a/platform/web/audio_driver_web.h b/platform/web/audio_driver_web.h index dfce277c0c..0a322d61b4 100644 --- a/platform/web/audio_driver_web.h +++ b/platform/web/audio_driver_web.h @@ -89,46 +89,6 @@ public: AudioDriverWeb() {} }; -#ifdef NO_THREADS -class AudioDriverScriptProcessor : public AudioDriverWeb { -private: - static void _process_callback(); - - static AudioDriverScriptProcessor *singleton; - -protected: - Error create(int &p_buffer_samples, int p_channels) override; - void start(float *p_out_buf, int p_out_buf_size, float *p_in_buf, int p_in_buf_size) override; - -public: - virtual const char *get_name() const override { return "ScriptProcessor"; } - - virtual void lock() override {} - virtual void unlock() override {} - - AudioDriverScriptProcessor() { singleton = this; } -}; - -class AudioDriverWorklet : public AudioDriverWeb { -private: - static void _process_callback(int p_pos, int p_samples); - static void _capture_callback(int p_pos, int p_samples); - - static AudioDriverWorklet *singleton; - -protected: - virtual Error create(int &p_buffer_size, int p_output_channels) override; - virtual void start(float *p_out_buf, int p_out_buf_size, float *p_in_buf, int p_in_buf_size) override; - -public: - virtual const char *get_name() const override { return "AudioWorklet"; } - - virtual void lock() override {} - virtual void unlock() override {} - - AudioDriverWorklet() { singleton = this; } -}; -#else class AudioDriverWorklet : public AudioDriverWeb { private: enum { @@ -156,6 +116,5 @@ public: void lock() override; void unlock() override; }; -#endif #endif // AUDIO_DRIVER_WEB_H diff --git a/platform/web/detect.py b/platform/web/detect.py index 9cce73efc4..08c1ff7b4a 100644 --- a/platform/web/detect.py +++ b/platform/web/detect.py @@ -11,6 +11,10 @@ from emscripten_helpers import ( ) from methods import get_compiler_version from SCons.Util import WhereIs +from typing import TYPE_CHECKING + +if TYPE_CHECKING: + from SCons import Environment def is_active(): @@ -47,7 +51,7 @@ def get_opts(): def get_flags(): return [ ("arch", "wasm32"), - ("tools", False), + ("target", "template_debug"), ("builtin_pcre2_with_jit", False), ("vulkan", False), # Use -Os to prioritize optimizing for reduced file size. This is @@ -60,7 +64,7 @@ def get_flags(): ] -def configure(env): +def configure(env: "Environment"): # Validate arch. supported_arches = ["wasm32"] if env["arch"] not in supported_arches: @@ -77,26 +81,17 @@ def configure(env): sys.exit(255) ## Build type - if env["target"].startswith("release"): - if env["optimize"] == "size": - env.Append(CCFLAGS=["-Os"]) - env.Append(LINKFLAGS=["-Os"]) - elif env["optimize"] == "speed": - env.Append(CCFLAGS=["-O3"]) - env.Append(LINKFLAGS=["-O3"]) - - if env["target"] == "release_debug": - # Retain function names for backtraces at the cost of file size. - env.Append(LINKFLAGS=["--profiling-funcs"]) - else: # "debug" - env.Append(CCFLAGS=["-O1", "-g"]) - env.Append(LINKFLAGS=["-O1", "-g"]) + + if env.debug_features: + # Retain function names for backtraces at the cost of file size. + env.Append(LINKFLAGS=["--profiling-funcs"]) + else: env["use_assertions"] = True if env["use_assertions"]: env.Append(LINKFLAGS=["-s", "ASSERTIONS=1"]) - if env["tools"]: + if env.editor_build: if env["initial_memory"] < 64: print('Note: Forcing "initial_memory=64" as it is required for the web editor.') env["initial_memory"] = 64 diff --git a/platform/web/display_server_web.cpp b/platform/web/display_server_web.cpp index f6a61b18e4..f704124704 100644 --- a/platform/web/display_server_web.cpp +++ b/platform/web/display_server_web.cpp @@ -54,7 +54,15 @@ DisplayServerWeb *DisplayServerWeb::get_singleton() { // Window (canvas) bool DisplayServerWeb::check_size_force_redraw() { - return godot_js_display_size_update() != 0; + bool size_changed = godot_js_display_size_update() != 0; + if (size_changed && !rect_changed_callback.is_null()) { + Variant size = Rect2i(Point2i(), window_get_size()); // TODO use window_get_position if implemented. + Variant *vp = &size; + Variant ret; + Callable::CallError ce; + rect_changed_callback.callp((const Variant **)&vp, 1, ret, ce); + } + return size_changed; } void DisplayServerWeb::fullscreen_change_callback(int p_fullscreen) { @@ -212,7 +220,7 @@ int DisplayServerWeb::mouse_button_callback(int p_pressed, int p_button, double void DisplayServerWeb::mouse_move_callback(double p_x, double p_y, double p_rel_x, double p_rel_y, int p_modifiers) { MouseButton input_mask = Input::get_singleton()->get_mouse_button_mask(); // For motion outside the canvas, only read mouse movement if dragging - // started inside the canvas; imitating desktop app behaviour. + // started inside the canvas; imitating desktop app behavior. if (!get_singleton()->cursor_inside_canvas && input_mask == MouseButton::NONE) { return; } @@ -738,11 +746,11 @@ void DisplayServerWeb::_dispatch_input_event(const Ref<InputEvent> &p_event) { } } -DisplayServer *DisplayServerWeb::create_func(const String &p_rendering_driver, WindowMode p_window_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Size2i &p_resolution, Error &r_error) { - return memnew(DisplayServerWeb(p_rendering_driver, p_window_mode, p_vsync_mode, p_flags, p_resolution, r_error)); +DisplayServer *DisplayServerWeb::create_func(const String &p_rendering_driver, WindowMode p_window_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Point2i *p_position, const Size2i &p_resolution, Error &r_error) { + return memnew(DisplayServerWeb(p_rendering_driver, p_window_mode, p_vsync_mode, p_flags, p_position, p_resolution, r_error)); } -DisplayServerWeb::DisplayServerWeb(const String &p_rendering_driver, WindowMode p_window_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Size2i &p_resolution, Error &r_error) { +DisplayServerWeb::DisplayServerWeb(const String &p_rendering_driver, WindowMode p_window_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Point2i *p_position, const Size2i &p_resolution, Error &r_error) { r_error = OK; // Always succeeds for now. // Ensure the canvas ID. @@ -758,10 +766,8 @@ DisplayServerWeb::DisplayServerWeb(const String &p_rendering_driver, WindowMode godot_js_os_request_quit_cb(request_quit_callback); #ifdef GLES3_ENABLED - // TODO "vulkan" defaults to webgl2 for now. - bool wants_webgl2 = p_rendering_driver == "opengl3" || p_rendering_driver == "vulkan"; - bool webgl2_init_failed = wants_webgl2 && !godot_js_display_has_webgl(2); - if (wants_webgl2 && !webgl2_init_failed) { + bool webgl2_inited = false; + if (godot_js_display_has_webgl(2)) { EmscriptenWebGLContextAttributes attributes; emscripten_webgl_init_context_attributes(&attributes); attributes.alpha = OS::get_singleton()->is_layered_allowed(); @@ -770,17 +776,17 @@ DisplayServerWeb::DisplayServerWeb(const String &p_rendering_driver, WindowMode attributes.explicitSwapControl = true; webgl_ctx = emscripten_webgl_create_context(canvas_id, &attributes); - if (emscripten_webgl_make_context_current(webgl_ctx) != EMSCRIPTEN_RESULT_SUCCESS) { - webgl2_init_failed = true; - } else { - RasterizerGLES3::make_current(); - } + webgl2_inited = webgl_ctx && emscripten_webgl_make_context_current(webgl_ctx) == EMSCRIPTEN_RESULT_SUCCESS; } - if (webgl2_init_failed) { + if (webgl2_inited) { + if (!emscripten_webgl_enable_extension(webgl_ctx, "OVR_multiview2")) { + print_verbose("Failed to enable WebXR extension."); + } + RasterizerGLES3::make_current(); + + } else { OS::get_singleton()->alert("Your browser does not seem to support WebGL2. Please update your browser version.", "Unable to initialize video driver"); - } - if (!wants_webgl2 || webgl2_init_failed) { RasterizerDummy::make_current(); } #else @@ -905,7 +911,7 @@ ObjectID DisplayServerWeb::window_get_attached_instance_id(WindowID p_window) co } void DisplayServerWeb::window_set_rect_changed_callback(const Callable &p_callable, WindowID p_window) { - // Not supported. + rect_changed_callback = p_callable; } void DisplayServerWeb::window_set_window_event_callback(const Callable &p_callable, WindowID p_window) { diff --git a/platform/web/display_server_web.h b/platform/web/display_server_web.h index 85076b906f..1919736802 100644 --- a/platform/web/display_server_web.h +++ b/platform/web/display_server_web.h @@ -60,6 +60,7 @@ private: WindowMode window_mode = WINDOW_MODE_WINDOWED; ObjectID window_attached_instance_id = {}; + Callable rect_changed_callback; Callable window_event_callback; Callable input_event_callback; Callable input_text_callback; @@ -96,7 +97,7 @@ private: static void _js_utterance_callback(int p_event, int p_id, int p_pos); static Vector<String> get_rendering_drivers_func(); - static DisplayServer *create_func(const String &p_rendering_driver, WindowMode p_window_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i &p_resolution, Error &r_error); + static DisplayServer *create_func(const String &p_rendering_driver, WindowMode p_window_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i *p_position, const Vector2i &p_resolution, Error &r_error); static void _dispatch_input_event(const Ref<InputEvent> &p_event); @@ -221,7 +222,7 @@ public: virtual void swap_buffers() override; static void register_web_driver(); - DisplayServerWeb(const String &p_rendering_driver, WindowMode p_window_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Size2i &p_resolution, Error &r_error); + DisplayServerWeb(const String &p_rendering_driver, WindowMode p_window_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Point2i *p_position, const Size2i &p_resolution, Error &r_error); ~DisplayServerWeb(); }; diff --git a/platform/web/emscripten_helpers.py b/platform/web/emscripten_helpers.py index 6045bc6fbd..ec33397842 100644 --- a/platform/web/emscripten_helpers.py +++ b/platform/web/emscripten_helpers.py @@ -38,7 +38,7 @@ def create_engine_file(env, target, source, externs): def create_template_zip(env, js, wasm, worker, side): - binary_name = "godot.tools" if env["tools"] else "godot" + binary_name = "godot.editor" if env.editor_build else "godot" zip_dir = env.Dir("#bin/.web_zip") in_files = [ js, @@ -58,19 +58,19 @@ def create_template_zip(env, js, wasm, worker, side): out_files.append(zip_dir.File(binary_name + ".side.wasm")) service_worker = "#misc/dist/html/service-worker.js" - if env["tools"]: + if env.editor_build: # HTML html = "#misc/dist/html/editor.html" cache = [ - "godot.tools.html", + "godot.editor.html", "offline.html", - "godot.tools.js", - "godot.tools.worker.js", - "godot.tools.audio.worklet.js", + "godot.editor.js", + "godot.editor.worker.js", + "godot.editor.audio.worklet.js", "logo.svg", "favicon.png", ] - opt_cache = ["godot.tools.wasm"] + opt_cache = ["godot.editor.wasm"] subst_dict = { "@GODOT_VERSION@": get_build_version(), "@GODOT_NAME@": "GodotEngine", diff --git a/platform/web/export/export_plugin.cpp b/platform/web/export/export_plugin.cpp index 1c327fe4b2..f59ac54f20 100644 --- a/platform/web/export/export_plugin.cpp +++ b/platform/web/export/export_plugin.cpp @@ -485,6 +485,7 @@ Error EditorExportPlatformWeb::export_project(const Ref<EditorExportPreset> &p_p } html.resize(f->get_length()); f->get_buffer(html.ptrw(), html.size()); + f.unref(); // close file. // Generate HTML file with replaced strings. _fix_html(html, p_preset, base_name, p_debug, p_flags, shared_objects, file_sizes); diff --git a/platform/web/godot_webgl2.h b/platform/web/godot_webgl2.h index 968b70f84b..ae6b23ae18 100644 --- a/platform/web/godot_webgl2.h +++ b/platform/web/godot_webgl2.h @@ -34,4 +34,21 @@ #include "GLES3/gl3.h" #include "webgl/webgl2.h" +#define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_NUM_VIEWS_OVR 0x9630 +#define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_BASE_VIEW_INDEX_OVR 0x9632 +#define GL_MAX_VIEWS_OVR 0x9631 +#define GL_FRAMEBUFFER_INCOMPLETE_VIEW_TARGETS_OVR 0x9633 + +#ifdef __cplusplus +extern "C" { +#endif + +void godot_webgl2_glFramebufferTextureMultiviewOVR(GLenum target, GLenum attachment, GLuint texture, GLint level, GLint baseViewIndex, GLsizei numViews); + +#define glFramebufferTextureMultiviewOVR godot_webgl2_glFramebufferTextureMultiviewOVR + +#ifdef __cplusplus +} +#endif + #endif // GODOT_WEBGL2_H diff --git a/platform/web/js/engine/config.js b/platform/web/js/engine/config.js index 41be7b2512..4560f12b49 100644 --- a/platform/web/js/engine/config.js +++ b/platform/web/js/engine/config.js @@ -275,7 +275,7 @@ const InternalConfig = function (initConfig) { // eslint-disable-line no-unused- 'print': this.onPrint, 'printErr': this.onPrintError, 'thisProgram': this.executable, - 'noExitRuntime': true, + 'noExitRuntime': false, 'dynamicLibraries': [`${loadPath}.side.wasm`], 'instantiateWasm': function (imports, onSuccess) { function done(result) { diff --git a/platform/web/js/libs/library_godot_display.js b/platform/web/js/libs/library_godot_display.js index 91cb8e728a..39c8569655 100644 --- a/platform/web/js/libs/library_godot_display.js +++ b/platform/web/js/libs/library_godot_display.js @@ -174,7 +174,7 @@ const GodotDisplayCursor = { $GodotDisplayCursor__deps: ['$GodotOS', '$GodotConfig'], $GodotDisplayCursor__postset: 'GodotOS.atexit(function(resolve, reject) { GodotDisplayCursor.clear(); resolve(); });', $GodotDisplayCursor: { - shape: 'auto', + shape: 'default', visible: true, cursors: {}, set_style: function (style) { @@ -185,7 +185,7 @@ const GodotDisplayCursor = { 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}, default`; } if (GodotDisplayCursor.visible) { GodotDisplayCursor.set_style(css); @@ -193,7 +193,7 @@ const GodotDisplayCursor = { }, clear: function () { GodotDisplayCursor.set_style(''); - GodotDisplayCursor.shape = 'auto'; + GodotDisplayCursor.shape = 'default'; GodotDisplayCursor.visible = true; Object.keys(GodotDisplayCursor.cursors).forEach(function (key) { URL.revokeObjectURL(GodotDisplayCursor.cursors[key]); diff --git a/platform/web/js/libs/library_godot_javascript_singleton.js b/platform/web/js/libs/library_godot_javascript_singleton.js index 692f27676a..c86cbbae45 100644 --- a/platform/web/js/libs/library_godot_javascript_singleton.js +++ b/platform/web/js/libs/library_godot_javascript_singleton.js @@ -88,7 +88,7 @@ const GodotJSWrapper = { return GodotRuntime.getHeapValue(val, 'double'); case 4: return GodotRuntime.parseString(GodotRuntime.getHeapValue(val, '*')); - case 21: // OBJECT + case 24: // OBJECT return GodotJSWrapper.get_proxied_value(GodotRuntime.getHeapValue(val, 'i64')); default: return undefined; @@ -117,7 +117,7 @@ const GodotJSWrapper = { } const id = GodotJSWrapper.get_proxied(p_val); GodotRuntime.setHeapValue(p_exchange, id, 'i64'); - return 21; + return 24; // OBJECT }, }, diff --git a/platform/web/js/libs/library_godot_webgl2.js b/platform/web/js/libs/library_godot_webgl2.js new file mode 100644 index 0000000000..365f712be7 --- /dev/null +++ b/platform/web/js/libs/library_godot_webgl2.js @@ -0,0 +1,52 @@ +/*************************************************************************/ +/* library_godot_webgl2.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 GodotWebGL2 = { + $GodotWebGL2__deps: ['$GL', '$GodotRuntime'], + $GodotWebGL2: {}, + + godot_webgl2_glFramebufferTextureMultiviewOVR__deps: ['emscripten_webgl_get_current_context'], + godot_webgl2_glFramebufferTextureMultiviewOVR__proxy: 'sync', + godot_webgl2_glFramebufferTextureMultiviewOVR__sig: 'viiiiii', + godot_webgl2_glFramebufferTextureMultiviewOVR: function (target, attachment, texture, level, base_view_index, num_views) { + const context = GL.currentContext; + if (typeof context.multiviewExt === 'undefined') { + const ext = context.GLctx.getExtension('OVR_multiview2'); + if (!ext) { + console.error('Trying to call glFramebufferTextureMultiviewOVR() without the OVR_multiview2 extension'); + return; + } + context.multiviewExt = ext; + } + context.multiviewExt.framebufferTextureMultiviewOVR(target, attachment, GL.textures[texture], level, base_view_index, num_views); + }, +}; + +autoAddDeps(GodotWebGL2, '$GodotWebGL2'); +mergeInto(LibraryManager.library, GodotWebGL2); diff --git a/platform/web/os_web.cpp b/platform/web/os_web.cpp index ebe56924df..07c53e51d0 100644 --- a/platform/web/os_web.cpp +++ b/platform/web/os_web.cpp @@ -37,9 +37,6 @@ #include "platform/web/display_server_web.h" #include "modules/modules_enabled.gen.h" // For websocket. -#ifdef MODULE_WEBSOCKET_ENABLED -#include "modules/websocket/remote_debugger_peer_websocket.h" -#endif #include <dlfcn.h> #include <emscripten.h> @@ -56,11 +53,6 @@ void OS_Web::alert(const String &p_alert, const String &p_title) { void OS_Web::initialize() { OS_Unix::initialize_core(); DisplayServerWeb::register_web_driver(); - -#ifdef MODULE_WEBSOCKET_ENABLED - EngineDebugger::register_uri_handler("ws://", RemoteDebuggerPeerWebSocket::create); - EngineDebugger::register_uri_handler("wss://", RemoteDebuggerPeerWebSocket::create); -#endif } void OS_Web::resume_audio() { @@ -239,9 +231,6 @@ OS_Web::OS_Web() { godot_js_pwa_cb(&OS_Web::update_pwa_state_callback); if (AudioDriverWeb::is_available()) { -#ifdef NO_THREADS - audio_drivers.push_back(memnew(AudioDriverScriptProcessor)); -#endif audio_drivers.push_back(memnew(AudioDriverWorklet)); } for (int i = 0; i < audio_drivers.size(); i++) { diff --git a/platform/web/package-lock.json b/platform/web/package-lock.json index f8c67b206f..e1428546c6 100644 --- a/platform/web/package-lock.json +++ b/platform/web/package-lock.json @@ -8,12 +8,16 @@ "name": "godot", "version": "1.0.0", "license": "MIT", + "dependencies": { + "eslint-plugin-html": "^7.1.0" + }, "devDependencies": { - "eslint": "^7.28.0", + "@html-eslint/eslint-plugin": "^0.15.0", + "@html-eslint/parser": "^0.15.0", + "eslint": "^7.32.0", "eslint-config-airbnb-base": "^14.2.1", "eslint-plugin-import": "^2.23.4", - "jsdoc": "^3.6.7", - "serve": "^13.0.2" + "jsdoc": "^3.6.7" } }, "node_modules/@babel/code-frame": { @@ -84,9 +88,9 @@ } }, "node_modules/@eslint/eslintrc": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.4.2.tgz", - "integrity": "sha512-8nmGq/4ycLpIwzvhI4tNDmQztZ8sp+hI7cyG8i1nQDhkAbRzHpXPidRAHlNvCZQpJTKw5ItIpMw9RSToGF00mg==", + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.4.3.tgz", + "integrity": "sha512-J6KFFz5QCYUJq3pf0mjEcCJVERbzv71PUIDczuh9JkwGEzced6CO5ADLHB1rbf/+oPBtoPfMYNOpGDzCANlbXw==", "dev": true, "dependencies": { "ajv": "^6.12.4", @@ -103,6 +107,47 @@ "node": "^10.12.0 || >=12.0.0" } }, + "node_modules/@html-eslint/eslint-plugin": { + "version": "0.15.0", + "resolved": "https://registry.npmjs.org/@html-eslint/eslint-plugin/-/eslint-plugin-0.15.0.tgz", + "integrity": "sha512-6DUb2ZN1PUlzlNzNj4aBhoObBp3Kl/+YbZ6CnkgFAsQSW0tSFAu7p8WwESkz9RZLZZN9gCUlcaYKJnQjTkmnDA==", + "dev": true, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/@html-eslint/parser": { + "version": "0.15.0", + "resolved": "https://registry.npmjs.org/@html-eslint/parser/-/parser-0.15.0.tgz", + "integrity": "sha512-fA+HQtWnODhOIK6j1p4XWqltINx7hM0WNNTM2RvlH/2glzeRDCcYq3vEmeQhnytvGocidu4ofTzNk80cLnnyiw==", + "dev": true, + "dependencies": { + "es-html-parser": "^0.0.8" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/@humanwhocodes/config-array": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.5.0.tgz", + "integrity": "sha512-FagtKFz74XrTl7y6HCzQpwDfXP0yhxe9lHLD1UZxjvZIcbyRz8zTFF/yYNfSfzU414eDwZ1SrO0Qvtyf+wFMQg==", + "dev": true, + "dependencies": { + "@humanwhocodes/object-schema": "^1.2.0", + "debug": "^4.1.1", + "minimatch": "^3.0.4" + }, + "engines": { + "node": ">=10.10.0" + } + }, + "node_modules/@humanwhocodes/object-schema": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", + "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", + "dev": true + }, "node_modules/@types/json5": { "version": "0.0.29", "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", @@ -131,25 +176,6 @@ "integrity": "sha512-eC4U9MlIcu2q0KQmXszyn5Akca/0jrQmwDRgpAMJai7qBWq4amIQhZyNau4VYGtCeALvW1/NtjzJJ567aZxfKA==", "dev": true }, - "node_modules/@zeit/schemas": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/@zeit/schemas/-/schemas-2.6.0.tgz", - "integrity": "sha512-uUrgZ8AxS+Lio0fZKAipJjAh415JyrOZowliZAzmnJSsf7piVL5w+G0+gFJ0KSu3QRhvui/7zuvpLz03YjXAhg==", - "dev": true - }, - "node_modules/accepts": { - "version": "1.3.7", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz", - "integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==", - "dev": true, - "dependencies": { - "mime-types": "~2.1.24", - "negotiator": "0.6.2" - }, - "engines": { - "node": ">= 0.6" - } - }, "node_modules/acorn": { "version": "7.4.1", "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", @@ -163,9 +189,9 @@ } }, "node_modules/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==", + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", "dev": true, "peerDependencies": { "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" @@ -187,15 +213,6 @@ "url": "https://github.com/sponsors/epoberezkin" } }, - "node_modules/ansi-align": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-3.0.1.tgz", - "integrity": "sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w==", - "dev": true, - "dependencies": { - "string-width": "^4.1.0" - } - }, "node_modules/ansi-colors": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", @@ -226,32 +243,6 @@ "node": ">=4" } }, - "node_modules/arch": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/arch/-/arch-2.2.0.tgz", - "integrity": "sha512-Of/R0wqp83cgHozfIYLbBMnej79U/SVGOOyuB3VVFv1NRM/PSFMK12x9KVtiYzJqmnU5WR2qp0Z5rHb7sWGnFQ==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, - "node_modules/arg": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/arg/-/arg-2.0.0.tgz", - "integrity": "sha512-XxNTUzKnz1ctK3ZIcI2XUPlD96wbHP2nGqkPKpvk/HNRlPveYrXIVSTk9m3LcqOgDPg3B1nMvdV/K8wZd7PG4w==", - "dev": true - }, "node_modules/argparse": { "version": "1.0.10", "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", @@ -318,28 +309,6 @@ "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==", "dev": true }, - "node_modules/boxen": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/boxen/-/boxen-5.1.2.tgz", - "integrity": "sha512-9gYgQKXx+1nP8mP7CzFyaUARhg7D3n1dF/FnErWmu9l6JvGpNUN278h0aSb+QjoiKSWG+iZ3uHrcqk0qrY9RQQ==", - "dev": true, - "dependencies": { - "ansi-align": "^3.0.0", - "camelcase": "^6.2.0", - "chalk": "^4.1.0", - "cli-boxes": "^2.2.1", - "string-width": "^4.2.2", - "type-fest": "^0.20.2", - "widest-line": "^3.1.0", - "wrap-ansi": "^7.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", @@ -350,15 +319,6 @@ "concat-map": "0.0.1" } }, - "node_modules/bytes": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", - "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=", - "dev": true, - "engines": { - "node": ">= 0.8" - } - }, "node_modules/call-bind": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", @@ -381,18 +341,6 @@ "node": ">=6" } }, - "node_modules/camelcase": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", - "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/catharsis": { "version": "0.9.0", "resolved": "https://registry.npmjs.org/catharsis/-/catharsis-0.9.0.tgz", @@ -475,32 +423,6 @@ "node": ">=8" } }, - "node_modules/cli-boxes": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-2.2.1.tgz", - "integrity": "sha512-y4coMcylgSCdVinjiDBuR8PCC2bLjyGTwEmPb9NHR/QaNU6EUOXcTY/s6VjGMD6ENSEaeQYHCY0GNGS5jfMwPw==", - "dev": true, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/clipboardy": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/clipboardy/-/clipboardy-2.3.0.tgz", - "integrity": "sha512-mKhiIL2DrQIsuXMgBgnfEHOZOryC7kY7YO//TN6c63wlEm3NG5tz+YgY5rVi29KCmq/QQjKYvM7a19+MDOTHOQ==", - "dev": true, - "dependencies": { - "arch": "^2.1.1", - "execa": "^1.0.0", - "is-wsl": "^2.1.1" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/color-convert": { "version": "1.9.3", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", @@ -516,51 +438,6 @@ "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", "dev": true }, - "node_modules/compressible": { - "version": "2.0.18", - "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz", - "integrity": "sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==", - "dev": true, - "dependencies": { - "mime-db": ">= 1.43.0 < 2" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/compression": { - "version": "1.7.3", - "resolved": "https://registry.npmjs.org/compression/-/compression-1.7.3.tgz", - "integrity": "sha512-HSjyBG5N1Nnz7tF2+O7A9XUhyjru71/fwgNb7oIsEVHR0WShfs2tIS/EySLgiTe98aOK18YDlMXpzjCXY/n9mg==", - "dev": true, - "dependencies": { - "accepts": "~1.3.5", - "bytes": "3.0.0", - "compressible": "~2.0.14", - "debug": "2.6.9", - "on-headers": "~1.0.1", - "safe-buffer": "5.1.2", - "vary": "~1.1.2" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/compression/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/compression/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true - }, "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", @@ -573,15 +450,6 @@ "integrity": "sha512-gNld/3lySHwuhaVluJUKLePYirM3QNCKzVxqAdhJII9/WXKVX5PURzMVJspS1jTslSqjeuG4KMVTSouit5YPHA==", "dev": true }, - "node_modules/content-disposition": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.2.tgz", - "integrity": "sha1-DPaLud318r55YcOoUXjLhdunjLQ=", - "dev": true, - "engines": { - "node": ">= 0.6" - } - }, "node_modules/cross-spawn": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", @@ -597,9 +465,9 @@ } }, "node_modules/debug": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", - "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", "dev": true, "dependencies": { "ms": "2.1.2" @@ -613,15 +481,6 @@ } } }, - "node_modules/deep-extend": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", - "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", - "dev": true, - "engines": { - "node": ">=4.0.0" - } - }, "node_modules/deep-is": { "version": "0.1.3", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", @@ -652,21 +511,74 @@ "node": ">=6.0.0" } }, + "node_modules/dom-serializer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", + "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==", + "dependencies": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.2", + "entities": "^4.2.0" + }, + "funding": { + "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" + } + }, + "node_modules/dom-serializer/node_modules/entities": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.4.0.tgz", + "integrity": "sha512-oYp7156SP8LkeGD0GF85ad1X9Ai79WtRsZ2gxJqtBuzH+98YUV6jkHEKlZkMbcrjJjIVJNIDP/3WL9wQkoPbWA==", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/domelementtype": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", + "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ] + }, + "node_modules/domhandler": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz", + "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==", + "dependencies": { + "domelementtype": "^2.3.0" + }, + "engines": { + "node": ">= 4" + }, + "funding": { + "url": "https://github.com/fb55/domhandler?sponsor=1" + } + }, + "node_modules/domutils": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.0.1.tgz", + "integrity": "sha512-z08c1l761iKhDFtfXO04C7kTdPBLi41zwOZl00WS8b5eiaebNpY00HKbztwBq+e3vyqWNwWF3mP9YLUeqIrF+Q==", + "dependencies": { + "dom-serializer": "^2.0.0", + "domelementtype": "^2.3.0", + "domhandler": "^5.0.1" + }, + "funding": { + "url": "https://github.com/fb55/domutils?sponsor=1" + } + }, "node_modules/emoji-regex": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", "dev": true }, - "node_modules/end-of-stream": { - "version": "1.4.4", - "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", - "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", - "dev": true, - "dependencies": { - "once": "^1.4.0" - } - }, "node_modules/enquirer": { "version": "2.3.6", "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz", @@ -727,6 +639,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/es-html-parser": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/es-html-parser/-/es-html-parser-0.0.8.tgz", + "integrity": "sha512-kjMH23xhvTBw/7Ve1Dtb/7yZdFajfvwOpdsgRHmnyt8yvTsDJnkFjUgEEaMZFW+e1OhN/eoZrvF9wehq+waTGg==", + "dev": true + }, "node_modules/es-to-primitive": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", @@ -757,13 +675,14 @@ } }, "node_modules/eslint": { - "version": "7.28.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.28.0.tgz", - "integrity": "sha512-UMfH0VSjP0G4p3EWirscJEQ/cHqnT/iuH6oNZOB94nBjWbMnhGEPxsZm1eyIW0C/9jLI0Fow4W5DXLjEI7mn1g==", + "version": "7.32.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.32.0.tgz", + "integrity": "sha512-VHZ8gX+EDfz+97jGcgyGCyRia/dPOd6Xh9yPv8Bl1+SoaIwD+a/vlrOmGRUyOYu7MwUhc7CxqeaDZU13S4+EpA==", "dev": true, "dependencies": { "@babel/code-frame": "7.12.11", - "@eslint/eslintrc": "^0.4.2", + "@eslint/eslintrc": "^0.4.3", + "@humanwhocodes/config-array": "^0.5.0", "ajv": "^6.10.0", "chalk": "^4.0.0", "cross-spawn": "^7.0.2", @@ -877,6 +796,14 @@ "ms": "^2.1.1" } }, + "node_modules/eslint-plugin-html": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-html/-/eslint-plugin-html-7.1.0.tgz", + "integrity": "sha512-fNLRraV/e6j8e3XYOC9xgND4j+U7b1Rq+OygMlLcMg+wI/IpVbF+ubQa3R78EjKB9njT6TQOlcK5rFKBVVtdfg==", + "dependencies": { + "htmlparser2": "^8.0.1" + } + }, "node_modules/eslint-plugin-import": { "version": "2.23.4", "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.23.4.tgz", @@ -1075,91 +1002,6 @@ "node": ">=0.10.0" } }, - "node_modules/execa": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz", - "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==", - "dev": true, - "dependencies": { - "cross-spawn": "^6.0.0", - "get-stream": "^4.0.0", - "is-stream": "^1.1.0", - "npm-run-path": "^2.0.0", - "p-finally": "^1.0.0", - "signal-exit": "^3.0.0", - "strip-eof": "^1.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/execa/node_modules/cross-spawn": { - "version": "6.0.5", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", - "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", - "dev": true, - "dependencies": { - "nice-try": "^1.0.4", - "path-key": "^2.0.1", - "semver": "^5.5.0", - "shebang-command": "^1.2.0", - "which": "^1.2.9" - }, - "engines": { - "node": ">=4.8" - } - }, - "node_modules/execa/node_modules/path-key": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", - "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/execa/node_modules/semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", - "dev": true, - "bin": { - "semver": "bin/semver" - } - }, - "node_modules/execa/node_modules/shebang-command": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", - "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", - "dev": true, - "dependencies": { - "shebang-regex": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/execa/node_modules/shebang-regex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", - "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/execa/node_modules/which": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", - "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", - "dev": true, - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "which": "bin/which" - } - }, "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", @@ -1178,21 +1020,6 @@ "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", "dev": true }, - "node_modules/fast-url-parser": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/fast-url-parser/-/fast-url-parser-1.1.3.tgz", - "integrity": "sha1-9K8+qfNNiicc9YrSs3WfQx8LMY0=", - "dev": true, - "dependencies": { - "punycode": "^1.3.2" - } - }, - "node_modules/fast-url-parser/node_modules/punycode": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", - "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=", - "dev": true - }, "node_modules/file-entry-cache": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", @@ -1268,18 +1095,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/get-stream": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", - "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", - "dev": true, - "dependencies": { - "pump": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, "node_modules/glob": { "version": "7.1.7", "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz", @@ -1313,9 +1128,9 @@ } }, "node_modules/globals": { - "version": "13.9.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.9.0.tgz", - "integrity": "sha512-74/FduwI/JaIrr1H8e71UbDE+5x7pIPs1C2rrwC52SszOo043CsWOZEMW7o2Y58xwm9b+0RBKDxY5n2sUpEFxA==", + "version": "13.17.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.17.0.tgz", + "integrity": "sha512-1C+6nQRb1GwGMKm2dH/E7enFAMxGTmGI7/dEdhy/DNelv85w9B72t3uc5frtMNXIbzrarJJ/lTCjcaZwbLJmyw==", "dev": true, "dependencies": { "type-fest": "^0.20.2" @@ -1381,6 +1196,35 @@ "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", "dev": true }, + "node_modules/htmlparser2": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-8.0.1.tgz", + "integrity": "sha512-4lVbmc1diZC7GUJQtRQ5yBAeUCL1exyMwmForWkRLnwyzWBFxN633SALPMGYaWZvKe9j1pRZJpauvmxENSp/EA==", + "funding": [ + "https://github.com/fb55/htmlparser2?sponsor=1", + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], + "dependencies": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.2", + "domutils": "^3.0.1", + "entities": "^4.3.0" + } + }, + "node_modules/htmlparser2/node_modules/entities": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.4.0.tgz", + "integrity": "sha512-oYp7156SP8LkeGD0GF85ad1X9Ai79WtRsZ2gxJqtBuzH+98YUV6jkHEKlZkMbcrjJjIVJNIDP/3WL9wQkoPbWA==", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, "node_modules/ignore": { "version": "4.0.6", "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", @@ -1431,12 +1275,6 @@ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", "dev": true }, - "node_modules/ini": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", - "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", - "dev": true - }, "node_modules/is-arrayish": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", @@ -1503,21 +1341,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-docker": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", - "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", - "dev": true, - "bin": { - "is-docker": "cli.js" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", @@ -1588,15 +1411,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-stream": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", - "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/is-string": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.6.tgz", @@ -1624,18 +1438,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-wsl": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", - "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", - "dev": true, - "dependencies": { - "is-docker": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", @@ -1883,31 +1685,10 @@ "integrity": "sha1-/oWy7HWlkDfyrf7BAP1sYBdhFS4=", "dev": true }, - "node_modules/mime-db": { - "version": "1.51.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.51.0.tgz", - "integrity": "sha512-5y8A56jg7XVQx2mbv1lu49NR4dokRnhZYTtL+KGfaa27uq4pSTXkwQkFJl4pkRMyNFz/EtYDSkiiEHx3F7UN6g==", - "dev": true, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mime-types": { - "version": "2.1.34", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.34.tgz", - "integrity": "sha512-6cP692WwGIs9XXdOO4++N+7qjqv0rqxxVvJ3VHPh/Sc9mVZcQP+ZGhkKiTvWMQRr2tbHkJP/Yn7Y0npb3ZBs4A==", - "dev": true, - "dependencies": { - "mime-db": "1.51.0" - }, - "engines": { - "node": ">= 0.6" - } - }, "node_modules/minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dev": true, "dependencies": { "brace-expansion": "^1.1.7" @@ -1946,21 +1727,6 @@ "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", "dev": true }, - "node_modules/negotiator": { - "version": "0.6.2", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", - "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==", - "dev": true, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/nice-try": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", - "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", - "dev": true - }, "node_modules/normalize-package-data": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", @@ -1982,27 +1748,6 @@ "semver": "bin/semver" } }, - "node_modules/npm-run-path": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", - "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=", - "dev": true, - "dependencies": { - "path-key": "^2.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/npm-run-path/node_modules/path-key": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", - "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=", - "dev": true, - "engines": { - "node": ">=4" - } - }, "node_modules/object-inspect": { "version": "1.10.3", "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.10.3.tgz", @@ -2070,15 +1815,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/on-headers": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz", - "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==", - "dev": true, - "engines": { - "node": ">= 0.8" - } - }, "node_modules/once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", @@ -2105,15 +1841,6 @@ "node": ">= 0.8.0" } }, - "node_modules/p-finally": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", - "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=", - "dev": true, - "engines": { - "node": ">=4" - } - }, "node_modules/p-limit": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", @@ -2190,12 +1917,6 @@ "node": ">=0.10.0" } }, - "node_modules/path-is-inside": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz", - "integrity": "sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM=", - "dev": true - }, "node_modules/path-key": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", @@ -2211,12 +1932,6 @@ "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", "dev": true }, - "node_modules/path-to-regexp": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-2.2.1.tgz", - "integrity": "sha512-gu9bD6Ta5bwGrrU8muHzVOBFFREpp2iRkVfhBJahwJ6p6Xw20SjT0MxLnwkjOibQmGSYhiUnf2FLe7k+jcFmGQ==", - "dev": true - }, "node_modules/path-type": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", @@ -2280,16 +1995,6 @@ "node": ">=0.4.0" } }, - "node_modules/pump": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", - "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", - "dev": true, - "dependencies": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" - } - }, "node_modules/punycode": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", @@ -2299,39 +2004,6 @@ "node": ">=6" } }, - "node_modules/range-parser": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.0.tgz", - "integrity": "sha1-9JvmtIeJTdxA3MlKMi9hEJLgDV4=", - "dev": true, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/rc": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", - "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", - "dev": true, - "dependencies": { - "deep-extend": "^0.6.0", - "ini": "~1.3.0", - "minimist": "^1.2.0", - "strip-json-comments": "~2.0.1" - }, - "bin": { - "rc": "cli.js" - } - }, - "node_modules/rc/node_modules/strip-json-comments": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", - "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/read-pkg": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz", @@ -2371,28 +2043,6 @@ "url": "https://github.com/sponsors/mysticatea" } }, - "node_modules/registry-auth-token": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-3.3.2.tgz", - "integrity": "sha512-JL39c60XlzCVgNrO+qq68FoNb56w/m7JYvGR2jT5iR1xBrUA3Mfx5Twk5rqTThPmQKMWydGmq8oFtDlxfrmxnQ==", - "dev": true, - "dependencies": { - "rc": "^1.1.6", - "safe-buffer": "^5.0.1" - } - }, - "node_modules/registry-url": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/registry-url/-/registry-url-3.1.0.tgz", - "integrity": "sha1-PU74cPc93h138M+aOBQyRE4XSUI=", - "dev": true, - "dependencies": { - "rc": "^1.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/require-from-string": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", @@ -2448,12 +2098,6 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true - }, "node_modules/semver": { "version": "7.3.5", "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", @@ -2469,86 +2113,6 @@ "node": ">=10" } }, - "node_modules/serve": { - "version": "13.0.2", - "resolved": "https://registry.npmjs.org/serve/-/serve-13.0.2.tgz", - "integrity": "sha512-71R6fKvNgKrqARAag6lYJNnxDzpH7DCNrMuvPY5PLVaC2PDhJsGTj/34o4o4tPWhTuLgEXqvgnAWbATQ9zGZTQ==", - "dev": true, - "dependencies": { - "@zeit/schemas": "2.6.0", - "ajv": "6.12.6", - "arg": "2.0.0", - "boxen": "5.1.2", - "chalk": "2.4.1", - "clipboardy": "2.3.0", - "compression": "1.7.3", - "serve-handler": "6.1.3", - "update-check": "1.5.2" - }, - "bin": { - "serve": "bin/serve.js" - } - }, - "node_modules/serve-handler": { - "version": "6.1.3", - "resolved": "https://registry.npmjs.org/serve-handler/-/serve-handler-6.1.3.tgz", - "integrity": "sha512-FosMqFBNrLyeiIDvP1zgO6YoTzFYHxLDEIavhlmQ+knB2Z7l1t+kGLHkZIDN7UVWqQAmKI3D20A6F6jo3nDd4w==", - "dev": true, - "dependencies": { - "bytes": "3.0.0", - "content-disposition": "0.5.2", - "fast-url-parser": "1.1.3", - "mime-types": "2.1.18", - "minimatch": "3.0.4", - "path-is-inside": "1.0.2", - "path-to-regexp": "2.2.1", - "range-parser": "1.2.0" - } - }, - "node_modules/serve-handler/node_modules/mime-db": { - "version": "1.33.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.33.0.tgz", - "integrity": "sha512-BHJ/EKruNIqJf/QahvxwQZXKygOQ256myeN/Ew+THcAa5q+PjyTTMMeNQC4DZw5AwfvelsUrA6B67NKMqXDbzQ==", - "dev": true, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/serve-handler/node_modules/mime-types": { - "version": "2.1.18", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.18.tgz", - "integrity": "sha512-lc/aahn+t4/SWV/qcmumYjymLsWfN3ELhpmVuUFjgsORruuZPVSwAQryq+HHGvO/SI2KVX26bx+En+zhM8g8hQ==", - "dev": true, - "dependencies": { - "mime-db": "~1.33.0" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/serve/node_modules/chalk": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.1.tgz", - "integrity": "sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==", - "dev": true, - "dependencies": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/serve/node_modules/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, - "engines": { - "node": ">=0.8.0" - } - }, "node_modules/shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", @@ -2570,12 +2134,6 @@ "node": ">=8" } }, - "node_modules/signal-exit": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.6.tgz", - "integrity": "sha512-sDl4qMFpijcGw22U5w63KmD3cZJfBuFlVNbVMKje2keoKML7X2UzWbc4XrmEbDwg0NXJc3yv4/ox7b+JWb57kQ==", - "dev": true - }, "node_modules/slice-ansi": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", @@ -2661,7 +2219,7 @@ "node_modules/sprintf-js": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", "dev": true }, "node_modules/string-width": { @@ -2725,15 +2283,6 @@ "node": ">=4" } }, - "node_modules/strip-eof": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", - "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/strip-json-comments": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", @@ -2872,16 +2421,6 @@ "integrity": "sha512-QvjkYpiD+dJJraRA8+dGAU4i7aBbb2s0S3jA45TFOvg2VgqvdCDd/3N6CqA8gluk1W91GLoXg5enMUx560QzuA==", "dev": true }, - "node_modules/update-check": { - "version": "1.5.2", - "resolved": "https://registry.npmjs.org/update-check/-/update-check-1.5.2.tgz", - "integrity": "sha512-1TrmYLuLj/5ZovwUS7fFd1jMH3NnFDN1y1A8dboedIDt7zs/zJMo6TwwlhYKkSeEwzleeiSBV5/3c9ufAQWDaQ==", - "dev": true, - "dependencies": { - "registry-auth-token": "3.3.2", - "registry-url": "3.1.0" - } - }, "node_modules/uri-js": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", @@ -2907,15 +2446,6 @@ "spdx-expression-parse": "^3.0.0" } }, - "node_modules/vary": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", - "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=", - "dev": true, - "engines": { - "node": ">= 0.8" - } - }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", @@ -2947,18 +2477,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/widest-line": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-3.1.0.tgz", - "integrity": "sha512-NsmoXalsWVDMGupxZ5R08ka9flZjjiLvHVAWYOKtiKM8ujtZWr9cRffak+uSE48+Ob8ObalXpwyeUiyDD6QFgg==", - "dev": true, - "dependencies": { - "string-width": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/word-wrap": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", @@ -2968,56 +2486,6 @@ "node": ">=0.10.0" } }, - "node_modules/wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/wrap-ansi/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/wrap-ansi/node_modules/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, - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/wrap-ansi/node_modules/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 - }, "node_modules/wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", @@ -3090,9 +2558,9 @@ "dev": true }, "@eslint/eslintrc": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.4.2.tgz", - "integrity": "sha512-8nmGq/4ycLpIwzvhI4tNDmQztZ8sp+hI7cyG8i1nQDhkAbRzHpXPidRAHlNvCZQpJTKw5ItIpMw9RSToGF00mg==", + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.4.3.tgz", + "integrity": "sha512-J6KFFz5QCYUJq3pf0mjEcCJVERbzv71PUIDczuh9JkwGEzced6CO5ADLHB1rbf/+oPBtoPfMYNOpGDzCANlbXw==", "dev": true, "requires": { "ajv": "^6.12.4", @@ -3106,6 +2574,38 @@ "strip-json-comments": "^3.1.1" } }, + "@html-eslint/eslint-plugin": { + "version": "0.15.0", + "resolved": "https://registry.npmjs.org/@html-eslint/eslint-plugin/-/eslint-plugin-0.15.0.tgz", + "integrity": "sha512-6DUb2ZN1PUlzlNzNj4aBhoObBp3Kl/+YbZ6CnkgFAsQSW0tSFAu7p8WwESkz9RZLZZN9gCUlcaYKJnQjTkmnDA==", + "dev": true + }, + "@html-eslint/parser": { + "version": "0.15.0", + "resolved": "https://registry.npmjs.org/@html-eslint/parser/-/parser-0.15.0.tgz", + "integrity": "sha512-fA+HQtWnODhOIK6j1p4XWqltINx7hM0WNNTM2RvlH/2glzeRDCcYq3vEmeQhnytvGocidu4ofTzNk80cLnnyiw==", + "dev": true, + "requires": { + "es-html-parser": "^0.0.8" + } + }, + "@humanwhocodes/config-array": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.5.0.tgz", + "integrity": "sha512-FagtKFz74XrTl7y6HCzQpwDfXP0yhxe9lHLD1UZxjvZIcbyRz8zTFF/yYNfSfzU414eDwZ1SrO0Qvtyf+wFMQg==", + "dev": true, + "requires": { + "@humanwhocodes/object-schema": "^1.2.0", + "debug": "^4.1.1", + "minimatch": "^3.0.4" + } + }, + "@humanwhocodes/object-schema": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", + "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", + "dev": true + }, "@types/json5": { "version": "0.0.29", "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", @@ -3134,22 +2634,6 @@ "integrity": "sha512-eC4U9MlIcu2q0KQmXszyn5Akca/0jrQmwDRgpAMJai7qBWq4amIQhZyNau4VYGtCeALvW1/NtjzJJ567aZxfKA==", "dev": true }, - "@zeit/schemas": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/@zeit/schemas/-/schemas-2.6.0.tgz", - "integrity": "sha512-uUrgZ8AxS+Lio0fZKAipJjAh415JyrOZowliZAzmnJSsf7piVL5w+G0+gFJ0KSu3QRhvui/7zuvpLz03YjXAhg==", - "dev": true - }, - "accepts": { - "version": "1.3.7", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz", - "integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==", - "dev": true, - "requires": { - "mime-types": "~2.1.24", - "negotiator": "0.6.2" - } - }, "acorn": { "version": "7.4.1", "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", @@ -3157,9 +2641,9 @@ "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==", + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", "dev": true, "requires": {} }, @@ -3175,15 +2659,6 @@ "uri-js": "^4.2.2" } }, - "ansi-align": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-3.0.1.tgz", - "integrity": "sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w==", - "dev": true, - "requires": { - "string-width": "^4.1.0" - } - }, "ansi-colors": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", @@ -3205,18 +2680,6 @@ "color-convert": "^1.9.0" } }, - "arch": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/arch/-/arch-2.2.0.tgz", - "integrity": "sha512-Of/R0wqp83cgHozfIYLbBMnej79U/SVGOOyuB3VVFv1NRM/PSFMK12x9KVtiYzJqmnU5WR2qp0Z5rHb7sWGnFQ==", - "dev": true - }, - "arg": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/arg/-/arg-2.0.0.tgz", - "integrity": "sha512-XxNTUzKnz1ctK3ZIcI2XUPlD96wbHP2nGqkPKpvk/HNRlPveYrXIVSTk9m3LcqOgDPg3B1nMvdV/K8wZd7PG4w==", - "dev": true - }, "argparse": { "version": "1.0.10", "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", @@ -3268,22 +2731,6 @@ "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==", "dev": true }, - "boxen": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/boxen/-/boxen-5.1.2.tgz", - "integrity": "sha512-9gYgQKXx+1nP8mP7CzFyaUARhg7D3n1dF/FnErWmu9l6JvGpNUN278h0aSb+QjoiKSWG+iZ3uHrcqk0qrY9RQQ==", - "dev": true, - "requires": { - "ansi-align": "^3.0.0", - "camelcase": "^6.2.0", - "chalk": "^4.1.0", - "cli-boxes": "^2.2.1", - "string-width": "^4.2.2", - "type-fest": "^0.20.2", - "widest-line": "^3.1.0", - "wrap-ansi": "^7.0.0" - } - }, "brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", @@ -3294,12 +2741,6 @@ "concat-map": "0.0.1" } }, - "bytes": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", - "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=", - "dev": true - }, "call-bind": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", @@ -3316,12 +2757,6 @@ "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", "dev": true }, - "camelcase": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", - "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", - "dev": true - }, "catharsis": { "version": "0.9.0", "resolved": "https://registry.npmjs.org/catharsis/-/catharsis-0.9.0.tgz", @@ -3382,23 +2817,6 @@ } } }, - "cli-boxes": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-2.2.1.tgz", - "integrity": "sha512-y4coMcylgSCdVinjiDBuR8PCC2bLjyGTwEmPb9NHR/QaNU6EUOXcTY/s6VjGMD6ENSEaeQYHCY0GNGS5jfMwPw==", - "dev": true - }, - "clipboardy": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/clipboardy/-/clipboardy-2.3.0.tgz", - "integrity": "sha512-mKhiIL2DrQIsuXMgBgnfEHOZOryC7kY7YO//TN6c63wlEm3NG5tz+YgY5rVi29KCmq/QQjKYvM7a19+MDOTHOQ==", - "dev": true, - "requires": { - "arch": "^2.1.1", - "execa": "^1.0.0", - "is-wsl": "^2.1.1" - } - }, "color-convert": { "version": "1.9.3", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", @@ -3414,47 +2832,6 @@ "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", "dev": true }, - "compressible": { - "version": "2.0.18", - "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz", - "integrity": "sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==", - "dev": true, - "requires": { - "mime-db": ">= 1.43.0 < 2" - } - }, - "compression": { - "version": "1.7.3", - "resolved": "https://registry.npmjs.org/compression/-/compression-1.7.3.tgz", - "integrity": "sha512-HSjyBG5N1Nnz7tF2+O7A9XUhyjru71/fwgNb7oIsEVHR0WShfs2tIS/EySLgiTe98aOK18YDlMXpzjCXY/n9mg==", - "dev": true, - "requires": { - "accepts": "~1.3.5", - "bytes": "3.0.0", - "compressible": "~2.0.14", - "debug": "2.6.9", - "on-headers": "~1.0.1", - "safe-buffer": "5.1.2", - "vary": "~1.1.2" - }, - "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 - } - } - }, "concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", @@ -3467,12 +2844,6 @@ "integrity": "sha512-gNld/3lySHwuhaVluJUKLePYirM3QNCKzVxqAdhJII9/WXKVX5PURzMVJspS1jTslSqjeuG4KMVTSouit5YPHA==", "dev": true }, - "content-disposition": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.2.tgz", - "integrity": "sha1-DPaLud318r55YcOoUXjLhdunjLQ=", - "dev": true - }, "cross-spawn": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", @@ -3485,20 +2856,14 @@ } }, "debug": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", - "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", "dev": true, "requires": { "ms": "2.1.2" } }, - "deep-extend": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", - "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", - "dev": true - }, "deep-is": { "version": "0.1.3", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", @@ -3523,21 +2888,52 @@ "esutils": "^2.0.2" } }, + "dom-serializer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", + "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==", + "requires": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.2", + "entities": "^4.2.0" + }, + "dependencies": { + "entities": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.4.0.tgz", + "integrity": "sha512-oYp7156SP8LkeGD0GF85ad1X9Ai79WtRsZ2gxJqtBuzH+98YUV6jkHEKlZkMbcrjJjIVJNIDP/3WL9wQkoPbWA==" + } + } + }, + "domelementtype": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", + "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==" + }, + "domhandler": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz", + "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==", + "requires": { + "domelementtype": "^2.3.0" + } + }, + "domutils": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.0.1.tgz", + "integrity": "sha512-z08c1l761iKhDFtfXO04C7kTdPBLi41zwOZl00WS8b5eiaebNpY00HKbztwBq+e3vyqWNwWF3mP9YLUeqIrF+Q==", + "requires": { + "dom-serializer": "^2.0.0", + "domelementtype": "^2.3.0", + "domhandler": "^5.0.1" + } + }, "emoji-regex": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", "dev": true }, - "end-of-stream": { - "version": "1.4.4", - "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", - "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", - "dev": true, - "requires": { - "once": "^1.4.0" - } - }, "enquirer": { "version": "2.3.6", "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz", @@ -3586,6 +2982,12 @@ "unbox-primitive": "^1.0.1" } }, + "es-html-parser": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/es-html-parser/-/es-html-parser-0.0.8.tgz", + "integrity": "sha512-kjMH23xhvTBw/7Ve1Dtb/7yZdFajfvwOpdsgRHmnyt8yvTsDJnkFjUgEEaMZFW+e1OhN/eoZrvF9wehq+waTGg==", + "dev": true + }, "es-to-primitive": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", @@ -3604,13 +3006,14 @@ "dev": true }, "eslint": { - "version": "7.28.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.28.0.tgz", - "integrity": "sha512-UMfH0VSjP0G4p3EWirscJEQ/cHqnT/iuH6oNZOB94nBjWbMnhGEPxsZm1eyIW0C/9jLI0Fow4W5DXLjEI7mn1g==", + "version": "7.32.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.32.0.tgz", + "integrity": "sha512-VHZ8gX+EDfz+97jGcgyGCyRia/dPOd6Xh9yPv8Bl1+SoaIwD+a/vlrOmGRUyOYu7MwUhc7CxqeaDZU13S4+EpA==", "dev": true, "requires": { "@babel/code-frame": "7.12.11", - "@eslint/eslintrc": "^0.4.2", + "@eslint/eslintrc": "^0.4.3", + "@humanwhocodes/config-array": "^0.5.0", "ajv": "^6.10.0", "chalk": "^4.0.0", "cross-spawn": "^7.0.2", @@ -3709,6 +3112,14 @@ } } }, + "eslint-plugin-html": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-html/-/eslint-plugin-html-7.1.0.tgz", + "integrity": "sha512-fNLRraV/e6j8e3XYOC9xgND4j+U7b1Rq+OygMlLcMg+wI/IpVbF+ubQa3R78EjKB9njT6TQOlcK5rFKBVVtdfg==", + "requires": { + "htmlparser2": "^8.0.1" + } + }, "eslint-plugin-import": { "version": "2.23.4", "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.23.4.tgz", @@ -3862,72 +3273,6 @@ "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", "dev": true }, - "execa": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz", - "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==", - "dev": true, - "requires": { - "cross-spawn": "^6.0.0", - "get-stream": "^4.0.0", - "is-stream": "^1.1.0", - "npm-run-path": "^2.0.0", - "p-finally": "^1.0.0", - "signal-exit": "^3.0.0", - "strip-eof": "^1.0.0" - }, - "dependencies": { - "cross-spawn": { - "version": "6.0.5", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", - "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", - "dev": true, - "requires": { - "nice-try": "^1.0.4", - "path-key": "^2.0.1", - "semver": "^5.5.0", - "shebang-command": "^1.2.0", - "which": "^1.2.9" - } - }, - "path-key": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", - "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=", - "dev": true - }, - "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", - "dev": true - }, - "shebang-command": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", - "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", - "dev": true, - "requires": { - "shebang-regex": "^1.0.0" - } - }, - "shebang-regex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", - "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=", - "dev": true - }, - "which": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", - "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", - "dev": true, - "requires": { - "isexe": "^2.0.0" - } - } - } - }, "fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", @@ -3946,23 +3291,6 @@ "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", "dev": true }, - "fast-url-parser": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/fast-url-parser/-/fast-url-parser-1.1.3.tgz", - "integrity": "sha1-9K8+qfNNiicc9YrSs3WfQx8LMY0=", - "dev": true, - "requires": { - "punycode": "^1.3.2" - }, - "dependencies": { - "punycode": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", - "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=", - "dev": true - } - } - }, "file-entry-cache": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", @@ -4026,15 +3354,6 @@ "has-symbols": "^1.0.1" } }, - "get-stream": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", - "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", - "dev": true, - "requires": { - "pump": "^3.0.0" - } - }, "glob": { "version": "7.1.7", "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz", @@ -4059,9 +3378,9 @@ } }, "globals": { - "version": "13.9.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.9.0.tgz", - "integrity": "sha512-74/FduwI/JaIrr1H8e71UbDE+5x7pIPs1C2rrwC52SszOo043CsWOZEMW7o2Y58xwm9b+0RBKDxY5n2sUpEFxA==", + "version": "13.17.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.17.0.tgz", + "integrity": "sha512-1C+6nQRb1GwGMKm2dH/E7enFAMxGTmGI7/dEdhy/DNelv85w9B72t3uc5frtMNXIbzrarJJ/lTCjcaZwbLJmyw==", "dev": true, "requires": { "type-fest": "^0.20.2" @@ -4106,6 +3425,24 @@ "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", "dev": true }, + "htmlparser2": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-8.0.1.tgz", + "integrity": "sha512-4lVbmc1diZC7GUJQtRQ5yBAeUCL1exyMwmForWkRLnwyzWBFxN633SALPMGYaWZvKe9j1pRZJpauvmxENSp/EA==", + "requires": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.2", + "domutils": "^3.0.1", + "entities": "^4.3.0" + }, + "dependencies": { + "entities": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.4.0.tgz", + "integrity": "sha512-oYp7156SP8LkeGD0GF85ad1X9Ai79WtRsZ2gxJqtBuzH+98YUV6jkHEKlZkMbcrjJjIVJNIDP/3WL9wQkoPbWA==" + } + } + }, "ignore": { "version": "4.0.6", "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", @@ -4144,12 +3481,6 @@ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", "dev": true }, - "ini": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", - "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", - "dev": true - }, "is-arrayish": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", @@ -4192,12 +3523,6 @@ "integrity": "sha512-/b4ZVsG7Z5XVtIxs/h9W8nvfLgSAyKYdtGWQLbqy6jA1icmgjf8WCoTKgeS4wy5tYaPePouzFMANbnj94c2Z+A==", "dev": true }, - "is-docker": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", - "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", - "dev": true - }, "is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", @@ -4241,12 +3566,6 @@ "has-symbols": "^1.0.2" } }, - "is-stream": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", - "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=", - "dev": true - }, "is-string": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.6.tgz", @@ -4262,15 +3581,6 @@ "has-symbols": "^1.0.2" } }, - "is-wsl": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", - "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", - "dev": true, - "requires": { - "is-docker": "^2.0.0" - } - }, "isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", @@ -4480,25 +3790,10 @@ "integrity": "sha1-/oWy7HWlkDfyrf7BAP1sYBdhFS4=", "dev": true }, - "mime-db": { - "version": "1.51.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.51.0.tgz", - "integrity": "sha512-5y8A56jg7XVQx2mbv1lu49NR4dokRnhZYTtL+KGfaa27uq4pSTXkwQkFJl4pkRMyNFz/EtYDSkiiEHx3F7UN6g==", - "dev": true - }, - "mime-types": { - "version": "2.1.34", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.34.tgz", - "integrity": "sha512-6cP692WwGIs9XXdOO4++N+7qjqv0rqxxVvJ3VHPh/Sc9mVZcQP+ZGhkKiTvWMQRr2tbHkJP/Yn7Y0npb3ZBs4A==", - "dev": true, - "requires": { - "mime-db": "1.51.0" - } - }, "minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dev": true, "requires": { "brace-expansion": "^1.1.7" @@ -4528,18 +3823,6 @@ "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", "dev": true }, - "negotiator": { - "version": "0.6.2", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", - "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==", - "dev": true - }, - "nice-try": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", - "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", - "dev": true - }, "normalize-package-data": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", @@ -4560,23 +3843,6 @@ } } }, - "npm-run-path": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", - "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=", - "dev": true, - "requires": { - "path-key": "^2.0.0" - }, - "dependencies": { - "path-key": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", - "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=", - "dev": true - } - } - }, "object-inspect": { "version": "1.10.3", "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.10.3.tgz", @@ -4623,12 +3889,6 @@ "es-abstract": "^1.18.2" } }, - "on-headers": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz", - "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==", - "dev": true - }, "once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", @@ -4652,12 +3912,6 @@ "word-wrap": "^1.2.3" } }, - "p-finally": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", - "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=", - "dev": true - }, "p-limit": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", @@ -4713,12 +3967,6 @@ "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", "dev": true }, - "path-is-inside": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz", - "integrity": "sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM=", - "dev": true - }, "path-key": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", @@ -4731,12 +3979,6 @@ "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", "dev": true }, - "path-to-regexp": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-2.2.1.tgz", - "integrity": "sha512-gu9bD6Ta5bwGrrU8muHzVOBFFREpp2iRkVfhBJahwJ6p6Xw20SjT0MxLnwkjOibQmGSYhiUnf2FLe7k+jcFmGQ==", - "dev": true - }, "path-type": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", @@ -4782,48 +4024,12 @@ "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", "dev": true }, - "pump": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", - "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", - "dev": true, - "requires": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" - } - }, "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 }, - "range-parser": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.0.tgz", - "integrity": "sha1-9JvmtIeJTdxA3MlKMi9hEJLgDV4=", - "dev": true - }, - "rc": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", - "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", - "dev": true, - "requires": { - "deep-extend": "^0.6.0", - "ini": "~1.3.0", - "minimist": "^1.2.0", - "strip-json-comments": "~2.0.1" - }, - "dependencies": { - "strip-json-comments": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", - "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", - "dev": true - } - } - }, "read-pkg": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz", @@ -4851,25 +4057,6 @@ "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==", "dev": true }, - "registry-auth-token": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-3.3.2.tgz", - "integrity": "sha512-JL39c60XlzCVgNrO+qq68FoNb56w/m7JYvGR2jT5iR1xBrUA3Mfx5Twk5rqTThPmQKMWydGmq8oFtDlxfrmxnQ==", - "dev": true, - "requires": { - "rc": "^1.1.6", - "safe-buffer": "^5.0.1" - } - }, - "registry-url": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/registry-url/-/registry-url-3.1.0.tgz", - "integrity": "sha1-PU74cPc93h138M+aOBQyRE4XSUI=", - "dev": true, - "requires": { - "rc": "^1.0.1" - } - }, "require-from-string": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", @@ -4910,12 +4097,6 @@ "glob": "^7.1.3" } }, - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true - }, "semver": { "version": "7.3.5", "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", @@ -4925,75 +4106,6 @@ "lru-cache": "^6.0.0" } }, - "serve": { - "version": "13.0.2", - "resolved": "https://registry.npmjs.org/serve/-/serve-13.0.2.tgz", - "integrity": "sha512-71R6fKvNgKrqARAag6lYJNnxDzpH7DCNrMuvPY5PLVaC2PDhJsGTj/34o4o4tPWhTuLgEXqvgnAWbATQ9zGZTQ==", - "dev": true, - "requires": { - "@zeit/schemas": "2.6.0", - "ajv": "6.12.6", - "arg": "2.0.0", - "boxen": "5.1.2", - "chalk": "2.4.1", - "clipboardy": "2.3.0", - "compression": "1.7.3", - "serve-handler": "6.1.3", - "update-check": "1.5.2" - }, - "dependencies": { - "chalk": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.1.tgz", - "integrity": "sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - }, - "escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", - "dev": true - } - } - }, - "serve-handler": { - "version": "6.1.3", - "resolved": "https://registry.npmjs.org/serve-handler/-/serve-handler-6.1.3.tgz", - "integrity": "sha512-FosMqFBNrLyeiIDvP1zgO6YoTzFYHxLDEIavhlmQ+knB2Z7l1t+kGLHkZIDN7UVWqQAmKI3D20A6F6jo3nDd4w==", - "dev": true, - "requires": { - "bytes": "3.0.0", - "content-disposition": "0.5.2", - "fast-url-parser": "1.1.3", - "mime-types": "2.1.18", - "minimatch": "3.0.4", - "path-is-inside": "1.0.2", - "path-to-regexp": "2.2.1", - "range-parser": "1.2.0" - }, - "dependencies": { - "mime-db": { - "version": "1.33.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.33.0.tgz", - "integrity": "sha512-BHJ/EKruNIqJf/QahvxwQZXKygOQ256myeN/Ew+THcAa5q+PjyTTMMeNQC4DZw5AwfvelsUrA6B67NKMqXDbzQ==", - "dev": true - }, - "mime-types": { - "version": "2.1.18", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.18.tgz", - "integrity": "sha512-lc/aahn+t4/SWV/qcmumYjymLsWfN3ELhpmVuUFjgsORruuZPVSwAQryq+HHGvO/SI2KVX26bx+En+zhM8g8hQ==", - "dev": true, - "requires": { - "mime-db": "~1.33.0" - } - } - } - }, "shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", @@ -5009,12 +4121,6 @@ "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", "dev": true }, - "signal-exit": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.6.tgz", - "integrity": "sha512-sDl4qMFpijcGw22U5w63KmD3cZJfBuFlVNbVMKje2keoKML7X2UzWbc4XrmEbDwg0NXJc3yv4/ox7b+JWb57kQ==", - "dev": true - }, "slice-ansi": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", @@ -5087,7 +4193,7 @@ "sprintf-js": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", "dev": true }, "string-width": { @@ -5136,12 +4242,6 @@ "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", "dev": true }, - "strip-eof": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", - "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=", - "dev": true - }, "strip-json-comments": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", @@ -5254,16 +4354,6 @@ "integrity": "sha512-QvjkYpiD+dJJraRA8+dGAU4i7aBbb2s0S3jA45TFOvg2VgqvdCDd/3N6CqA8gluk1W91GLoXg5enMUx560QzuA==", "dev": true }, - "update-check": { - "version": "1.5.2", - "resolved": "https://registry.npmjs.org/update-check/-/update-check-1.5.2.tgz", - "integrity": "sha512-1TrmYLuLj/5ZovwUS7fFd1jMH3NnFDN1y1A8dboedIDt7zs/zJMo6TwwlhYKkSeEwzleeiSBV5/3c9ufAQWDaQ==", - "dev": true, - "requires": { - "registry-auth-token": "3.3.2", - "registry-url": "3.1.0" - } - }, "uri-js": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", @@ -5289,12 +4379,6 @@ "spdx-expression-parse": "^3.0.0" } }, - "vary": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", - "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=", - "dev": true - }, "which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", @@ -5317,58 +4401,12 @@ "is-symbol": "^1.0.3" } }, - "widest-line": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-3.1.0.tgz", - "integrity": "sha512-NsmoXalsWVDMGupxZ5R08ka9flZjjiLvHVAWYOKtiKM8ujtZWr9cRffak+uSE48+Ob8ObalXpwyeUiyDD6QFgg==", - "dev": true, - "requires": { - "string-width": "^4.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 }, - "wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dev": true, - "requires": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - } - } - }, "wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", diff --git a/platform/web/package.json b/platform/web/package.json index 0a8d9e4334..e1dd7b1a22 100644 --- a/platform/web/package.json +++ b/platform/web/package.json @@ -5,25 +5,30 @@ "description": "Development and linting setup for Godot's Web platform code", "scripts": { "docs": "jsdoc --template js/jsdoc2rst/ js/engine/engine.js js/engine/config.js js/engine/features.js --destination ''", - "lint": "npm run lint:engine && npm run lint:libs && npm run lint:modules && npm run lint:tools", + "lint": "npm run lint:engine && npm run lint:libs && npm run lint:modules && npm run lint:tools && npm run lint:html", "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", "lint:tools": "eslint \"js/jsdoc2rst/**/*.js\" --no-eslintrc -c .eslintrc.engine.js", - "format": "npm run format:engine && npm run format:libs && npm run format:modules && npm run format:tools", + "lint:html": "eslint \"../../misc/dist/html/*.html\" --no-eslintrc -c .eslintrc.html.js", + "format": "npm run format:engine && npm run format:libs && npm run format:modules && npm run format:tools && npm run format:html", "format:engine": "npm run lint:engine -- --fix", "format:libs": "npm run lint:libs -- --fix", "format:modules": "npm run lint:modules -- --fix", "format:tools": "npm run lint:tools -- --fix", - "serve": "serve" + "format:html": "npm run lint:html -- --fix" }, "author": "Godot Engine contributors", "license": "MIT", "devDependencies": { - "eslint": "^7.28.0", + "@html-eslint/eslint-plugin": "^0.15.0", + "@html-eslint/parser": "^0.15.0", + "eslint": "^7.32.0", "eslint-config-airbnb-base": "^14.2.1", "eslint-plugin-import": "^2.23.4", - "jsdoc": "^3.6.7", - "serve": "^13.0.2" + "jsdoc": "^3.6.7" + }, + "dependencies": { + "eslint-plugin-html": "^7.1.0" } } diff --git a/platform/web/serve.json b/platform/web/serve.json deleted file mode 100644 index f2ef24751f..0000000000 --- a/platform/web/serve.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "public": "../../bin", - "headers": [{ - "source": "**/*", - "headers": [ - { - "key": "Cross-Origin-Embedder-Policy", - "value": "require-corp" - }, { - "key": "Cross-Origin-Opener-Policy", - "value": "same-origin" - }, { - "key": "Access-Control-Allow-Origin", - "value": "*" - }, { - "key": "Cache-Control", - "value": "no-store, max-age=0" - } - ] - }] -} diff --git a/platform/web/serve.py b/platform/web/serve.py new file mode 100755 index 0000000000..6a3efcc463 --- /dev/null +++ b/platform/web/serve.py @@ -0,0 +1,55 @@ +#!/usr/bin/env python3 + +from http.server import HTTPServer, SimpleHTTPRequestHandler, test # type: ignore +from pathlib import Path +import os +import sys +import argparse +import subprocess + + +class CORSRequestHandler(SimpleHTTPRequestHandler): + def end_headers(self): + self.send_header("Cross-Origin-Opener-Policy", "same-origin") + self.send_header("Cross-Origin-Embedder-Policy", "require-corp") + self.send_header("Access-Control-Allow-Origin", "*") + super().end_headers() + + +def shell_open(url): + if sys.platform == "win32": + os.startfile(url) + else: + opener = "open" if sys.platform == "darwin" else "xdg-open" + subprocess.call([opener, url]) + + +def serve(root, port, run_browser): + os.chdir(root) + + if run_browser: + # Open the served page in the user's default browser. + print("Opening the served URL in the default browser (use `--no-browser` or `-n` to disable this).") + shell_open(f"http://127.0.0.1:{port}") + + test(CORSRequestHandler, HTTPServer, port=port) + + +if __name__ == "__main__": + parser = argparse.ArgumentParser() + parser.add_argument("-p", "--port", help="port to listen on", default=8060, type=int) + parser.add_argument( + "-r", "--root", help="path to serve as root (relative to `platform/web/`)", default="../../bin", type=Path + ) + browser_parser = parser.add_mutually_exclusive_group(required=False) + browser_parser.add_argument( + "-n", "--no-browser", help="don't open default web browser automatically", dest="browser", action="store_false" + ) + parser.set_defaults(browser=True) + args = parser.parse_args() + + # Change to the directory where the script is located, + # so that the script can be run from any location. + os.chdir(Path(__file__).resolve().parent) + + serve(args.root, args.port, args.browser) diff --git a/platform/web/web_main.cpp b/platform/web/web_main.cpp index 287fe48c4d..fce782b546 100644 --- a/platform/web/web_main.cpp +++ b/platform/web/web_main.cpp @@ -41,30 +41,25 @@ static OS_Web *os = nullptr; static uint64_t target_ticks = 0; +static bool main_started = false; +static bool shutdown_complete = false; void exit_callback() { - emscripten_cancel_main_loop(); // After this, we can exit! - Main::cleanup(); + if (!shutdown_complete) { + return; // Still waiting. + } + if (main_started) { + Main::cleanup(); + main_started = false; + } int exit_code = OS_Web::get_singleton()->get_exit_code(); memdelete(os); os = nullptr; - emscripten_force_exit(exit_code); // No matter that we call cancel_main_loop, regular "exit" will not work, forcing. + emscripten_force_exit(exit_code); // Exit runtime. } void cleanup_after_sync() { - emscripten_set_main_loop(exit_callback, -1, false); -} - -void early_cleanup() { - emscripten_cancel_main_loop(); // After this, we can exit! - int exit_code = OS_Web::get_singleton()->get_exit_code(); - memdelete(os); - os = nullptr; - emscripten_force_exit(exit_code); // No matter that we call cancel_main_loop, regular "exit" will not work, forcing. -} - -void early_cleanup_sync() { - emscripten_set_main_loop(early_cleanup, -1, false); + shutdown_complete = true; } void main_loop_callback() { @@ -77,17 +72,18 @@ void main_loop_callback() { return; // Skip frame. } - int target_fps = Engine::get_singleton()->get_target_fps(); - if (target_fps > 0) { + int max_fps = Engine::get_singleton()->get_max_fps(); + if (max_fps > 0) { if (current_ticks - target_ticks > 1000000) { // When the window loses focus, we stop getting updates and accumulate delay. // For this reason, if the difference is too big, we reset target ticks to the current ticks. target_ticks = current_ticks; } - target_ticks += (uint64_t)(1000000 / target_fps); + target_ticks += (uint64_t)(1000000 / max_fps); } if (os->main_loop_iterate()) { - emscripten_cancel_main_loop(); // Cancel current loop and wait for cleanup_after_sync. + emscripten_cancel_main_loop(); // Cancel current loop and set the cleanup one. + emscripten_set_main_loop(exit_callback, -1, false); godot_js_os_finish_async(cleanup_after_sync); } } @@ -109,10 +105,14 @@ extern EMSCRIPTEN_KEEPALIVE int godot_web_main(int argc, char *argv[]) { } os->set_exit_code(exit_code); // Will only exit after sync. - godot_js_os_finish_async(early_cleanup_sync); + emscripten_set_main_loop(exit_callback, -1, false); + godot_js_os_finish_async(cleanup_after_sync); return exit_code; } + os->set_exit_code(0); + main_started = true; + // Ease up compatibility. ResourceLoader::set_abort_on_missing_resources(false); diff --git a/platform/windows/SCsub b/platform/windows/SCsub index 7e412b140f..efbb47d965 100644 --- a/platform/windows/SCsub +++ b/platform/windows/SCsub @@ -19,19 +19,44 @@ common_win = [ "gl_manager_windows.cpp", ] +common_win_wrap = [ + "console_wrapper_windows.cpp", +] + res_file = "godot_res.rc" res_target = "godot_res" + env["OBJSUFFIX"] res_obj = env.RES(res_target, res_file) prog = env.add_program("#bin/godot", common_win + res_obj, PROGSUFFIX=env["PROGSUFFIX"]) +# Build console wrapper app. +if env["windows_subsystem"] == "gui": + env_wrap = env.Clone() + res_wrap_file = "godot_res_wrap.rc" + res_wrap_target = "godot_res_wrap" + env["OBJSUFFIX"] + res_wrap_obj = env_wrap.RES(res_wrap_target, res_wrap_file) + + if env.msvc: + env_wrap.Append(LINKFLAGS=["/SUBSYSTEM:CONSOLE"]) + env_wrap.Append(LINKFLAGS=["version.lib"]) + else: + env_wrap.Append(LINKFLAGS=["-Wl,--subsystem,console"]) + env_wrap.Append(LIBS=["version"]) + + prog_wrap = env_wrap.add_program("#bin/godot", common_win_wrap + res_wrap_obj, PROGSUFFIX=env["PROGSUFFIX_WRAP"]) + # Microsoft Visual Studio Project Generation if env["vsproj"]: env.vs_srcs += ["platform/windows/" + res_file] env.vs_srcs += ["platform/windows/godot.natvis"] for x in common_win: env.vs_srcs += ["platform/windows/" + str(x)] + if env["windows_subsystem"] == "gui": + for x in common_win_wrap: + env.vs_srcs += ["platform/windows/" + str(x)] if not os.getenv("VCINSTALLDIR"): if env["debug_symbols"] and env["separate_debug_symbols"]: env.AddPostAction(prog, run_in_subprocess(platform_windows_builders.make_debug_mingw)) + if env["windows_subsystem"] == "gui": + env.AddPostAction(prog_wrap, run_in_subprocess(platform_windows_builders.make_debug_mingw)) diff --git a/platform/windows/console_wrapper_windows.cpp b/platform/windows/console_wrapper_windows.cpp new file mode 100644 index 0000000000..258176426b --- /dev/null +++ b/platform/windows/console_wrapper_windows.cpp @@ -0,0 +1,181 @@ +/*************************************************************************/ +/* console_wrapper_windows.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include <windows.h> + +#include <shlwapi.h> +#include <stdio.h> +#include <stdlib.h> + +#ifndef ENABLE_VIRTUAL_TERMINAL_PROCESSING +#define ENABLE_VIRTUAL_TERMINAL_PROCESSING 0x4 +#endif + +int main(int argc, char *argv[]) { + // Get executable name. + WCHAR exe_name[MAX_PATH] = {}; + if (!GetModuleFileNameW(nullptr, exe_name, MAX_PATH)) { + wprintf(L"GetModuleFileName failed, error %d\n", GetLastError()); + return -1; + } + + // Get product name from the resources and set console title. + DWORD ver_info_handle = 0; + DWORD ver_info_size = GetFileVersionInfoSizeW(exe_name, &ver_info_handle); + if (ver_info_size > 0) { + LPBYTE ver_info = (LPBYTE)malloc(ver_info_size); + if (ver_info) { + if (GetFileVersionInfoW(exe_name, ver_info_handle, ver_info_size, ver_info)) { + LPCWSTR text_ptr = nullptr; + UINT text_size = 0; + if (VerQueryValueW(ver_info, L"\\StringFileInfo\\040904b0\\ProductName", (void **)&text_ptr, &text_size) && (text_size > 0)) { + SetConsoleTitleW(text_ptr); + } + } + free(ver_info); + } + } + + // Enable virtual terminal sequences processing. + HANDLE stdout_handle = GetStdHandle(STD_OUTPUT_HANDLE); + DWORD out_mode = ENABLE_PROCESSED_OUTPUT | ENABLE_VIRTUAL_TERMINAL_PROCESSING; + SetConsoleMode(stdout_handle, out_mode); + + // Find main executable name and check if it exist. + static PCWSTR exe_renames[] = { + L".console.exe", + L"_console.exe", + L" console.exe", + L"console.exe", + nullptr, + }; + + bool rename_found = false; + for (int i = 0; exe_renames[i]; i++) { + PWSTR c = StrRStrIW(exe_name, nullptr, exe_renames[i]); + if (c) { + CopyMemory(c, L".exe", sizeof(WCHAR) * 5); + rename_found = true; + break; + } + } + if (!rename_found) { + wprintf(L"Invalid wrapper executable name.\n"); + return -1; + } + + DWORD file_attrib = GetFileAttributesW(exe_name); + if (file_attrib == INVALID_FILE_ATTRIBUTES || (file_attrib & FILE_ATTRIBUTE_DIRECTORY)) { + wprintf(L"Main executable %ls not found.\n", exe_name); + return -1; + } + + // Create job to monitor process tree. + HANDLE job_handle = CreateJobObjectW(nullptr, nullptr); + if (!job_handle) { + wprintf(L"CreateJobObject failed, error %d\n", GetLastError()); + return -1; + } + + HANDLE io_port_handle = CreateIoCompletionPort(INVALID_HANDLE_VALUE, nullptr, 0, 1); + if (!io_port_handle) { + wprintf(L"CreateIoCompletionPort failed, error %d\n", GetLastError()); + return -1; + } + + JOBOBJECT_ASSOCIATE_COMPLETION_PORT compl_port; + ZeroMemory(&compl_port, sizeof(compl_port)); + compl_port.CompletionKey = job_handle; + compl_port.CompletionPort = io_port_handle; + + if (!SetInformationJobObject(job_handle, JobObjectAssociateCompletionPortInformation, &compl_port, sizeof(compl_port))) { + wprintf(L"SetInformationJobObject(AssociateCompletionPortInformation) failed, error %d\n", GetLastError()); + return -1; + } + + JOBOBJECT_EXTENDED_LIMIT_INFORMATION jeli; + ZeroMemory(&jeli, sizeof(jeli)); + jeli.BasicLimitInformation.LimitFlags = JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE; + + if (!SetInformationJobObject(job_handle, JobObjectExtendedLimitInformation, &jeli, sizeof(jeli))) { + wprintf(L"SetInformationJobObject(ExtendedLimitInformation) failed, error %d\n", GetLastError()); + return -1; + } + + // Start the main process. + PROCESS_INFORMATION pi; + ZeroMemory(&pi, sizeof(pi)); + + STARTUPINFOW si; + ZeroMemory(&si, sizeof(si)); + si.cb = sizeof(si); + + WCHAR new_command_line[32767]; + _snwprintf_s(new_command_line, 32767, _TRUNCATE, L"%ls %ls", exe_name, PathGetArgsW(GetCommandLineW())); + + if (!CreateProcessW(nullptr, new_command_line, nullptr, nullptr, true, CREATE_SUSPENDED, nullptr, nullptr, &si, &pi)) { + wprintf(L"CreateProcess failed, error %d\n", GetLastError()); + return -1; + } + + if (!AssignProcessToJobObject(job_handle, pi.hProcess)) { + wprintf(L"AssignProcessToJobObject failed, error %d\n", GetLastError()); + return -1; + } + + ResumeThread(pi.hThread); + CloseHandle(pi.hThread); + + // Wait until main process and all of its children are finished. + DWORD completion_code = 0; + ULONG_PTR completion_key = 0; + LPOVERLAPPED overlapped = nullptr; + + while (GetQueuedCompletionStatus(io_port_handle, &completion_code, &completion_key, &overlapped, INFINITE)) { + if ((HANDLE)completion_key == job_handle && completion_code == JOB_OBJECT_MSG_ACTIVE_PROCESS_ZERO) { + break; + } + } + + CloseHandle(job_handle); + CloseHandle(io_port_handle); + + // Get exit code of the main process. + DWORD exit_code = 0; + GetExitCodeProcess(pi.hProcess, &exit_code); + + CloseHandle(pi.hProcess); + + return exit_code; +} + +int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { + return main(0, nullptr); +} diff --git a/platform/windows/detect.py b/platform/windows/detect.py index 095d688213..705e83dace 100644 --- a/platform/windows/detect.py +++ b/platform/windows/detect.py @@ -4,6 +4,11 @@ import subprocess import sys from platform_methods import detect_arch +from typing import TYPE_CHECKING + +if TYPE_CHECKING: + from SCons import Environment + # To match other platforms STACK_SIZE = 8388608 @@ -173,17 +178,7 @@ def get_opts(): "Targeted Windows version, >= 0x0601 (Windows 7)", "0x0601", ), - BoolVariable( - "debug_symbols", - "Add debugging symbols to release/release_debug builds", - True, - ), EnumVariable("windows_subsystem", "Windows subsystem", "gui", ("gui", "console")), - BoolVariable( - "separate_debug_symbols", - "Create a separate file containing debugging symbols", - False, - ), ( "msvc_version", "MSVC version to use. Ignored if VCINSTALLDIR is set in shell env.", @@ -330,31 +325,11 @@ def setup_mingw(env): def configure_msvc(env, vcvars_msvc_config): """Configure env to work with MSVC""" - # Build type - if env["target"] == "release": - if env["optimize"] == "speed": # optimize for speed (default) - env.Append(CCFLAGS=["/O2"]) - env.Append(LINKFLAGS=["/OPT:REF"]) - elif env["optimize"] == "size": # optimize for size - env.Append(CCFLAGS=["/O1"]) - env.Append(LINKFLAGS=["/OPT:REF"]) - env.Append(LINKFLAGS=["/ENTRY:mainCRTStartup"]) - - elif env["target"] == "release_debug": - if env["optimize"] == "speed": # optimize for speed (default) - env.Append(CCFLAGS=["/O2"]) - env.Append(LINKFLAGS=["/OPT:REF"]) - elif env["optimize"] == "size": # optimize for size - env.Append(CCFLAGS=["/O1"]) - env.Append(LINKFLAGS=["/OPT:REF"]) - - elif env["target"] == "debug": - env.AppendUnique(CCFLAGS=["/Zi", "/FS", "/Od", "/EHsc"]) - env.Append(LINKFLAGS=["/DEBUG"]) + ## Build type - if env["debug_symbols"]: - env.AppendUnique(CCFLAGS=["/Zi", "/FS"]) - env.AppendUnique(LINKFLAGS=["/DEBUG"]) + # TODO: Re-evaluate the need for this / streamline with common config. + if env["target"] == "template_release": + env.Append(LINKFLAGS=["/ENTRY:mainCRTStartup"]) if env["windows_subsystem"] == "gui": env.Append(LINKFLAGS=["/SUBSYSTEM:WINDOWS"]) @@ -426,6 +401,7 @@ def configure_msvc(env, vcvars_msvc_config): "Avrt", "dwmapi", "dwrite", + "wbemuuid", ] if env["vulkan"]: @@ -491,31 +467,10 @@ def configure_mingw(env): if env["use_llvm"] and not try_cmd("clang --version", env["mingw_prefix"], env["arch"]): env["use_llvm"] = False - if env["target"] == "release": + # TODO: Re-evaluate the need for this / streamline with common config. + if env["target"] == "template_release": env.Append(CCFLAGS=["-msse2"]) - - if env["optimize"] == "speed": # optimize for speed (default) - if env["arch"] == "x86_32": - env.Append(CCFLAGS=["-O2"]) - else: - env.Append(CCFLAGS=["-O3"]) - else: # optimize for size - env.Prepend(CCFLAGS=["-Os"]) - - if env["debug_symbols"]: - env.Prepend(CCFLAGS=["-g2"]) - - elif env["target"] == "release_debug": - env.Append(CCFLAGS=["-O2"]) - if env["debug_symbols"]: - env.Prepend(CCFLAGS=["-g2"]) - if env["optimize"] == "speed": # optimize for speed (default) - env.Append(CCFLAGS=["-O2"]) - else: # optimize for size - env.Prepend(CCFLAGS=["-Os"]) - - elif env["target"] == "debug": - env.Append(CCFLAGS=["-g3"]) + elif env.dev_build: # Allow big objects. It's supposed not to have drawbacks but seems to break # GCC LTO, so enabling for debug builds only (which are not built with LTO # and are the only ones with too big objects). @@ -623,15 +578,18 @@ def configure_mingw(env): "uuid", "dwmapi", "dwrite", + "wbemuuid", ] ) - env.Append(CPPDEFINES=["VULKAN_ENABLED"]) - if not env["use_volk"]: - env.Append(LIBS=["vulkan"]) + if env["vulkan"]: + env.Append(CPPDEFINES=["VULKAN_ENABLED"]) + if not env["use_volk"]: + env.Append(LIBS=["vulkan"]) - env.Append(CPPDEFINES=["GLES3_ENABLED"]) - env.Append(LIBS=["opengl32"]) + if env["opengl3"]: + env.Append(CPPDEFINES=["GLES3_ENABLED"]) + env.Append(LIBS=["opengl32"]) env.Append(CPPDEFINES=["MINGW_ENABLED", ("MINGW_HAS_SECURE_API", 1)]) @@ -639,7 +597,7 @@ def configure_mingw(env): env.Append(BUILDERS={"RES": env.Builder(action=build_res_file, suffix=".o", src_suffix=".rc")}) -def configure(env): +def configure(env: "Environment"): # Validate arch. supported_arches = ["x86_32", "x86_64", "arm32", "arm64"] if env["arch"] not in supported_arches: diff --git a/platform/windows/display_server_windows.cpp b/platform/windows/display_server_windows.cpp index b9e6f7b843..af80a07da9 100644 --- a/platform/windows/display_server_windows.cpp +++ b/platform/windows/display_server_windows.cpp @@ -561,20 +561,6 @@ float DisplayServerWindows::screen_get_refresh_rate(int p_screen) const { return data.rate; } -bool DisplayServerWindows::screen_is_touchscreen(int p_screen) const { -#ifndef _MSC_VER -#warning touchscreen not working -#endif - return false; -} - -void DisplayServerWindows::screen_set_orientation(ScreenOrientation p_orientation, int p_screen) { -} - -DisplayServer::ScreenOrientation DisplayServerWindows::screen_get_orientation(int p_screen) const { - return SCREEN_LANDSCAPE; -} - void DisplayServerWindows::screen_set_keep_on(bool p_enable) { if (keep_screen_on == p_enable) { return; @@ -755,9 +741,20 @@ int64_t DisplayServerWindows::window_get_native_handle(HandleType p_handle_type, case WINDOW_HANDLE: { return (int64_t)windows[p_window].hWnd; } +#if defined(GLES3_ENABLED) case WINDOW_VIEW: { - return 0; // Not supported. + if (gl_manager) { + return (int64_t)gl_manager->get_hdc(p_window); + } + return 0; + } + case OPENGL_CONTEXT: { + if (gl_manager) { + return (int64_t)gl_manager->get_hglrc(p_window); + } + return 0; } +#endif default: { return 0; } @@ -1361,7 +1358,8 @@ void DisplayServerWindows::window_set_flag(WindowFlags p_flag, bool p_enabled, W if (p_enabled) { //enable per-pixel alpha - DWM_BLURBEHIND bb = { 0 }; + DWM_BLURBEHIND bb; + ZeroMemory(&bb, sizeof(bb)); HRGN hRgn = CreateRectRgn(0, 0, -1, -1); bb.dwFlags = DWM_BB_ENABLE | DWM_BB_BLURREGION; bb.hRgnBlur = hRgn; @@ -1373,7 +1371,8 @@ void DisplayServerWindows::window_set_flag(WindowFlags p_flag, bool p_enabled, W //disable per-pixel alpha wd.layered_window = false; - DWM_BLURBEHIND bb = { 0 }; + DWM_BLURBEHIND bb; + ZeroMemory(&bb, sizeof(bb)); HRGN hRgn = CreateRectRgn(0, 0, -1, -1); bb.dwFlags = DWM_BB_ENABLE | DWM_BB_BLURREGION; bb.hRgnBlur = hRgn; @@ -1390,7 +1389,7 @@ void DisplayServerWindows::window_set_flag(WindowFlags p_flag, bool p_enabled, W ERR_FAIL_COND_MSG(IsWindowVisible(wd.hWnd) && (wd.is_popup != p_enabled), "Popup flag can't changed while window is opened."); wd.is_popup = p_enabled; } break; - case WINDOW_FLAG_MAX: + default: break; } } @@ -1419,7 +1418,7 @@ bool DisplayServerWindows::window_get_flag(WindowFlags p_flag, WindowID p_window case WINDOW_FLAG_POPUP: { return wd.is_popup; } break; - case WINDOW_FLAG_MAX: + default: break; } @@ -1893,7 +1892,7 @@ void DisplayServerWindows::set_native_icon(const String &p_filename) { pos += sizeof(WORD); f->seek(pos); - icon_dir = (ICONDIR *)memrealloc(icon_dir, 3 * sizeof(WORD) + icon_dir->idCount * sizeof(ICONDIRENTRY)); + icon_dir = (ICONDIR *)memrealloc(icon_dir, sizeof(ICONDIR) - sizeof(ICONDIRENTRY) + icon_dir->idCount * sizeof(ICONDIRENTRY)); f->get_buffer((uint8_t *)&icon_dir->idEntries[0], icon_dir->idCount * sizeof(ICONDIRENTRY)); int small_icon_index = -1; // Select 16x16 with largest color count. @@ -2456,10 +2455,16 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA return 0; // Jump back. } case WM_MOUSELEAVE: { - old_invalid = true; - windows[window_id].mouse_outside = true; + if (window_mouseover_id == window_id) { + old_invalid = true; + window_mouseover_id = INVALID_WINDOW_ID; - _send_window_event(windows[window_id], WINDOW_EVENT_MOUSE_EXIT); + _send_window_event(windows[window_id], WINDOW_EVENT_MOUSE_EXIT); + } else if (window_mouseover_id != INVALID_WINDOW_ID) { + // This is reached during drag and drop, after dropping in a different window. + // Once-off notification, must call again. + track_mouse_leave_event(windows[window_mouseover_id].hWnd); + } } break; case WM_INPUT: { @@ -2554,24 +2559,27 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA if ((tablet_get_current_driver() == "wintab") && wintab_available && windows[window_id].wtctx) { PACKET packet; if (wintab_WTPacket(windows[window_id].wtctx, wParam, &packet)) { - float pressure = float(packet.pkNormalPressure - windows[window_id].min_pressure) / float(windows[window_id].max_pressure - windows[window_id].min_pressure); - windows[window_id].last_pressure = pressure; + POINT coords; + GetCursorPos(&coords); + ScreenToClient(windows[window_id].hWnd, &coords); + windows[window_id].last_pressure_update = 0; + float pressure = float(packet.pkNormalPressure - windows[window_id].min_pressure) / float(windows[window_id].max_pressure - windows[window_id].min_pressure); double azim = (packet.pkOrientation.orAzimuth / 10.0f) * (Math_PI / 180); double alt = Math::tan((Math::abs(packet.pkOrientation.orAltitude / 10.0f)) * (Math_PI / 180)); + bool inverted = packet.pkStatus & TPS_INVERT; - if (windows[window_id].tilt_supported) { - windows[window_id].last_tilt = Vector2(Math::atan(Math::sin(azim) / alt), Math::atan(Math::cos(azim) / alt)); - } else { - windows[window_id].last_tilt = Vector2(); - } + Vector2 tilt = (windows[window_id].tilt_supported) ? Vector2(Math::atan(Math::sin(azim) / alt), Math::atan(Math::cos(azim) / alt)) : Vector2(); - windows[window_id].last_pen_inverted = packet.pkStatus & TPS_INVERT; + // Nothing changed, ignore event. + if (!old_invalid && coords.x == old_x && coords.y == old_y && windows[window_id].last_pressure == pressure && windows[window_id].last_tilt == tilt && windows[window_id].last_pen_inverted == inverted) { + break; + } - POINT coords; - GetCursorPos(&coords); - ScreenToClient(windows[window_id].hWnd, &coords); + windows[window_id].last_pressure = pressure; + windows[window_id].last_tilt = tilt; + windows[window_id].last_pen_inverted = inverted; // Don't calculate relative mouse movement if we don't have focus in CAPTURED mode. if (!windows[window_id].window_has_focus && mouse_mode == MOUSE_MODE_CAPTURED) { @@ -2687,17 +2695,21 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA } } - if (windows[window_id].mouse_outside) { + if (window_mouseover_id != window_id) { // Mouse enter. if (mouse_mode != MOUSE_MODE_CAPTURED) { + if (window_mouseover_id != INVALID_WINDOW_ID) { + // Leave previous window. + _send_window_event(windows[window_mouseover_id], WINDOW_EVENT_MOUSE_EXIT); + } _send_window_event(windows[window_id], WINDOW_EVENT_MOUSE_ENTER); } CursorShape c = cursor_shape; cursor_shape = CURSOR_MAX; cursor_set_shape(c); - windows[window_id].mouse_outside = false; + window_mouseover_id = window_id; // Once-off notification, must call again. track_mouse_leave_event(hWnd); @@ -2788,17 +2800,29 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA } } - if (windows[window_id].mouse_outside) { + DisplayServer::WindowID over_id = get_window_at_screen_position(mouse_get_position()); + if (!Rect2(window_get_position(over_id), Point2(windows[over_id].width, windows[over_id].height)).has_point(mouse_get_position())) { + // Don't consider the windowborder as part of the window. + over_id = INVALID_WINDOW_ID; + } + if (window_mouseover_id != over_id) { // Mouse enter. if (mouse_mode != MOUSE_MODE_CAPTURED) { - _send_window_event(windows[window_id], WINDOW_EVENT_MOUSE_ENTER); + if (window_mouseover_id != INVALID_WINDOW_ID) { + // Leave previous window. + _send_window_event(windows[window_mouseover_id], WINDOW_EVENT_MOUSE_EXIT); + } + + if (over_id != INVALID_WINDOW_ID) { + _send_window_event(windows[over_id], WINDOW_EVENT_MOUSE_ENTER); + } } CursorShape c = cursor_shape; cursor_shape = CURSOR_MAX; cursor_set_shape(c); - windows[window_id].mouse_outside = false; + window_mouseover_id = over_id; // Once-off notification, must call again. track_mouse_leave_event(hWnd); @@ -2809,9 +2833,13 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA break; } + DisplayServer::WindowID receiving_window_id = _get_focused_window_or_popup(); + if (receiving_window_id == INVALID_WINDOW_ID) { + receiving_window_id = window_id; + } Ref<InputEventMouseMotion> mm; mm.instantiate(); - mm->set_window_id(window_id); + mm->set_window_id(receiving_window_id); mm->set_ctrl_pressed((wParam & MK_CONTROL) != 0); mm->set_shift_pressed((wParam & MK_SHIFT) != 0); mm->set_alt_pressed(alt_mem); @@ -2868,9 +2896,14 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA mm->set_relative(Vector2(mm->get_position() - Vector2(old_x, old_y))); old_x = mm->get_position().x; old_y = mm->get_position().y; - if (windows[window_id].window_has_focus || window_get_active_popup() == window_id) { - Input::get_singleton()->parse_input_event(mm); + + if (!windows[receiving_window_id].window_has_focus) { + // In case of unfocused Popups, adjust event position. + Point2i pos = mm->get_position() - window_get_position(receiving_window_id) + window_get_position(window_id); + mm->set_position(pos); + mm->set_global_position(pos); } + Input::get_singleton()->parse_input_event(mm); } break; case WM_LBUTTONDOWN: @@ -3096,7 +3129,7 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA rect_changed = true; } #if defined(VULKAN_ENABLED) - if (context_vulkan && window_created) { + if (context_vulkan && window.context_created) { // Note: Trigger resize event to update swapchains when window is minimized/restored, even if size is not changed. context_vulkan->window_resize(window_id, window.width, window.height); } @@ -3550,21 +3583,19 @@ DisplayServer::WindowID DisplayServerWindows::_create_window(WindowMode p_mode, #ifdef VULKAN_ENABLED if (context_vulkan) { - if (context_vulkan->window_create(id, p_vsync_mode, wd.hWnd, hInstance, WindowRect.right - WindowRect.left, WindowRect.bottom - WindowRect.top) == -1) { + if (context_vulkan->window_create(id, p_vsync_mode, wd.hWnd, hInstance, WindowRect.right - WindowRect.left, WindowRect.bottom - WindowRect.top) != OK) { memdelete(context_vulkan); context_vulkan = nullptr; windows.erase(id); ERR_FAIL_V_MSG(INVALID_WINDOW_ID, "Failed to create Vulkan Window."); } + wd.context_created = true; } #endif #ifdef GLES3_ENABLED if (gl_manager) { - Error err = gl_manager->window_create(id, wd.hWnd, hInstance, WindowRect.right - WindowRect.left, WindowRect.bottom - WindowRect.top); - - // shut down OpenGL, to mirror behavior of Vulkan code - if (err != OK) { + if (gl_manager->window_create(id, wd.hWnd, hInstance, WindowRect.right - WindowRect.left, WindowRect.bottom - WindowRect.top) != OK) { memdelete(gl_manager); gl_manager = nullptr; windows.erase(id); @@ -3715,7 +3746,7 @@ void DisplayServerWindows::tablet_set_current_driver(const String &p_driver) { } } -DisplayServerWindows::DisplayServerWindows(const String &p_rendering_driver, WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i &p_resolution, Error &r_error) { +DisplayServerWindows::DisplayServerWindows(const String &p_rendering_driver, WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i *p_position, const Vector2i &p_resolution, Error &r_error) { drop_events = false; key_event_pos = 0; @@ -3867,6 +3898,10 @@ DisplayServerWindows::DisplayServerWindows(const String &p_rendering_driver, Win (screen_get_size(0).width - p_resolution.width) / 2, (screen_get_size(0).height - p_resolution.height) / 2); + if (p_position != nullptr) { + window_position = *p_position; + } + WindowID main_window = _create_window(p_mode, p_vsync_mode, 0, Rect2i(window_position, p_resolution)); ERR_FAIL_COND_MSG(main_window == INVALID_WINDOW_ID, "Failed to create main window."); @@ -3930,12 +3965,24 @@ Vector<String> DisplayServerWindows::get_rendering_drivers_func() { return drivers; } -DisplayServer *DisplayServerWindows::create_func(const String &p_rendering_driver, WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i &p_resolution, Error &r_error) { - DisplayServer *ds = memnew(DisplayServerWindows(p_rendering_driver, p_mode, p_vsync_mode, p_flags, p_resolution, r_error)); +DisplayServer *DisplayServerWindows::create_func(const String &p_rendering_driver, WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i *p_position, const Vector2i &p_resolution, Error &r_error) { + DisplayServer *ds = memnew(DisplayServerWindows(p_rendering_driver, p_mode, p_vsync_mode, p_flags, p_position, p_resolution, r_error)); if (r_error != OK) { - OS::get_singleton()->alert("Your video card driver does not support any of the supported Vulkan or OpenGL versions.\n" - "Please update your drivers or if you have a very old or integrated GPU upgrade it.", - "Unable to initialize Video driver"); + if (p_rendering_driver == "vulkan") { + String executable_name = OS::get_singleton()->get_executable_path().get_file(); + OS::get_singleton()->alert("Your video card driver does not support the selected Vulkan version.\n" + "Please try updating your GPU driver or try using the OpenGL 3 driver.\n" + "You can enable the OpenGL 3 driver by starting the engine from the\n" + "command line with the command:\n'./" + + executable_name + " --rendering-driver opengl3'.\n " + "If you have updated your graphics drivers recently, try rebooting.", + "Unable to initialize Video driver"); + } else { + OS::get_singleton()->alert("Your video card driver does not support the selected OpenGL version.\n" + "Please try updating your GPU driver.\n" + "If you have updated your graphics drivers recently, try rebooting.", + "Unable to initialize Video driver"); + } } return ds; } diff --git a/platform/windows/display_server_windows.h b/platform/windows/display_server_windows.h index d85d6364bd..8ac0086d69 100644 --- a/platform/windows/display_server_windows.h +++ b/platform/windows/display_server_windows.h @@ -61,9 +61,9 @@ #include "gl_manager_windows.h" #endif -#include <fcntl.h> #include <io.h> #include <stdio.h> + #define WIN32_LEAN_AND_MEAN #include <windows.h> #include <windowsx.h> @@ -323,6 +323,8 @@ class DisplayServerWindows : public DisplayServer { LPARAM lParam; }; + WindowID window_mouseover_id = INVALID_WINDOW_ID; + KeyEvent key_event_buffer[KEY_EVENT_BUFFER_SIZE]; int key_event_pos; @@ -369,6 +371,7 @@ class DisplayServerWindows : public DisplayServer { bool no_focus = false; bool window_has_focus = false; bool exclusive = false; + bool context_created = false; // Used to transfer data between events using timer. WPARAM saved_wparam; @@ -397,7 +400,6 @@ class DisplayServerWindows : public DisplayServer { Size2 window_rect; Point2 last_pos; - bool mouse_outside = true; ObjectID instance_id; @@ -516,10 +518,6 @@ public: virtual Rect2i screen_get_usable_rect(int p_screen = SCREEN_OF_MAIN_WINDOW) const override; virtual int screen_get_dpi(int p_screen = SCREEN_OF_MAIN_WINDOW) const override; virtual float screen_get_refresh_rate(int p_screen = SCREEN_OF_MAIN_WINDOW) const override; - virtual bool screen_is_touchscreen(int p_screen = SCREEN_OF_MAIN_WINDOW) const override; - - virtual void screen_set_orientation(ScreenOrientation p_orientation, int p_screen = SCREEN_OF_MAIN_WINDOW) override; - virtual ScreenOrientation screen_get_orientation(int p_screen = SCREEN_OF_MAIN_WINDOW) const override; virtual void screen_set_keep_on(bool p_enable) override; //disable screensaver virtual bool screen_is_kept_on() const override; @@ -626,11 +624,11 @@ public: virtual void set_context(Context p_context) override; - static DisplayServer *create_func(const String &p_rendering_driver, WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i &p_resolution, Error &r_error); + static DisplayServer *create_func(const String &p_rendering_driver, WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i *p_position, const Vector2i &p_resolution, Error &r_error); static Vector<String> get_rendering_drivers_func(); static void register_windows_driver(); - DisplayServerWindows(const String &p_rendering_driver, WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i &p_resolution, Error &r_error); + DisplayServerWindows(const String &p_rendering_driver, WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i *p_position, const Vector2i &p_resolution, Error &r_error); ~DisplayServerWindows(); }; diff --git a/platform/windows/export/export_plugin.cpp b/platform/windows/export/export_plugin.cpp index 016d201f2c..d15380ac7a 100644 --- a/platform/windows/export/export_plugin.cpp +++ b/platform/windows/export/export_plugin.cpp @@ -41,24 +41,13 @@ Error EditorExportPlatformWindows::sign_shared_object(const Ref<EditorExportPres } } -Error EditorExportPlatformWindows::_export_debug_script(const Ref<EditorExportPreset> &p_preset, const String &p_app_name, const String &p_pkg_name, const String &p_path) { - Ref<FileAccess> f = FileAccess::open(p_path, FileAccess::WRITE); - if (f.is_null()) { - add_message(EXPORT_MESSAGE_ERROR, TTR("Debug Script Export"), vformat(TTR("Could not open file \"%s\"."), p_path)); - return ERR_CANT_CREATE; - } - - f->store_line("@echo off"); - f->store_line("title \"" + p_app_name + "\""); - f->store_line("\"%~dp0" + p_pkg_name + "\" \"%*\""); - f->store_line("pause > nul"); - - return OK; -} - Error EditorExportPlatformWindows::modify_template(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags) { if (p_preset->get("application/modify_resources")) { - _rcedit_add_data(p_preset, p_path); + _rcedit_add_data(p_preset, p_path, true); + String wrapper_path = p_path.get_basename() + ".console.exe"; + if (FileAccess::exists(wrapper_path)) { + _rcedit_add_data(p_preset, wrapper_path, false); + } } return OK; } @@ -71,6 +60,10 @@ Error EditorExportPlatformWindows::export_project(const Ref<EditorExportPreset> Error err = EditorExportPlatformPC::export_project(p_preset, p_debug, pck_path, p_flags); if (p_preset->get("codesign/enable") && err == OK) { _code_sign(p_preset, pck_path); + String wrapper_path = p_path.get_basename() + ".console.exe"; + if (FileAccess::exists(wrapper_path)) { + _code_sign(p_preset, wrapper_path); + } } if (p_preset->get("binary_format/embed_pck") && err == OK) { @@ -81,25 +74,6 @@ Error EditorExportPlatformWindows::export_project(const Ref<EditorExportPreset> } } - String app_name; - if (String(ProjectSettings::get_singleton()->get("application/config/name")) != "") { - app_name = String(ProjectSettings::get_singleton()->get("application/config/name")); - } else { - app_name = "Unnamed"; - } - app_name = OS::get_singleton()->get_safe_dir_name(app_name); - - // Save console script. - if (err == OK) { - int con_scr = p_preset->get("debug/export_console_script"); - if ((con_scr == 1 && p_debug) || (con_scr == 2)) { - String scr_path = p_path.get_basename() + ".cmd"; - if (_export_debug_script(p_preset, app_name, p_path.get_file(), scr_path) != OK) { - add_message(EXPORT_MESSAGE_ERROR, TTR("Debug Script Export"), TTR("Could not create console script.")); - } - } - } - return err; } @@ -146,8 +120,8 @@ void EditorExportPlatformWindows::get_export_options(List<ExportOption> *r_optio r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/trademarks"), "")); } -Error EditorExportPlatformWindows::_rcedit_add_data(const Ref<EditorExportPreset> &p_preset, const String &p_path) { - String rcedit_path = EditorSettings::get_singleton()->get("export/windows/rcedit"); +Error EditorExportPlatformWindows::_rcedit_add_data(const Ref<EditorExportPreset> &p_preset, const String &p_path, bool p_set_icon) { + String rcedit_path = EDITOR_GET("export/windows/rcedit"); if (rcedit_path != String() && !FileAccess::exists(rcedit_path)) { add_message(EXPORT_MESSAGE_WARNING, TTR("Resources Modification"), vformat(TTR("Could not find rcedit executable at \"%s\"."), rcedit_path)); @@ -160,7 +134,7 @@ Error EditorExportPlatformWindows::_rcedit_add_data(const Ref<EditorExportPreset #ifndef WINDOWS_ENABLED // On non-Windows we need WINE to run rcedit - String wine_path = EditorSettings::get_singleton()->get("export/windows/wine"); + String wine_path = EDITOR_GET("export/windows/wine"); if (!wine_path.is_empty() && !FileAccess::exists(wine_path)) { add_message(EXPORT_MESSAGE_WARNING, TTR("Resources Modification"), vformat(TTR("Could not find wine executable at \"%s\"."), wine_path)); @@ -184,7 +158,7 @@ Error EditorExportPlatformWindows::_rcedit_add_data(const Ref<EditorExportPreset List<String> args; args.push_back(p_path); - if (!icon_path.is_empty()) { + if (!icon_path.is_empty() && p_set_icon) { args.push_back("--set-icon"); args.push_back(icon_path); } @@ -248,7 +222,7 @@ Error EditorExportPlatformWindows::_code_sign(const Ref<EditorExportPreset> &p_p List<String> args; #ifdef WINDOWS_ENABLED - String signtool_path = EditorSettings::get_singleton()->get("export/windows/signtool"); + String signtool_path = EDITOR_GET("export/windows/signtool"); if (!signtool_path.is_empty() && !FileAccess::exists(signtool_path)) { add_message(EXPORT_MESSAGE_WARNING, TTR("Code Signing"), vformat(TTR("Could not find signtool executable at \"%s\"."), signtool_path)); return ERR_FILE_NOT_FOUND; @@ -257,7 +231,7 @@ Error EditorExportPlatformWindows::_code_sign(const Ref<EditorExportPreset> &p_p signtool_path = "signtool"; // try to run signtool from PATH } #else - String signtool_path = EditorSettings::get_singleton()->get("export/windows/osslsigncode"); + String signtool_path = EDITOR_GET("export/windows/osslsigncode"); if (!signtool_path.is_empty() && !FileAccess::exists(signtool_path)) { add_message(EXPORT_MESSAGE_WARNING, TTR("Code Signing"), vformat(TTR("Could not find osslsigncode executable at \"%s\"."), signtool_path)); return ERR_FILE_NOT_FOUND; @@ -420,7 +394,7 @@ bool EditorExportPlatformWindows::has_valid_export_configuration(const Ref<Edito String err = ""; bool valid = EditorExportPlatformPC::has_valid_export_configuration(p_preset, err, r_missing_templates); - String rcedit_path = EditorSettings::get_singleton()->get("export/windows/rcedit"); + String rcedit_path = EDITOR_GET("export/windows/rcedit"); if (p_preset->get("application/modify_resources") && rcedit_path.is_empty()) { err += TTR("The rcedit tool must be configured in the Editor Settings (Export > Windows > rcedit) to change the icon or app information data.") + "\n"; } diff --git a/platform/windows/export/export_plugin.h b/platform/windows/export/export_plugin.h index f85331c898..ec3b60aa76 100644 --- a/platform/windows/export/export_plugin.h +++ b/platform/windows/export/export_plugin.h @@ -38,9 +38,8 @@ #include "platform/windows/logo.gen.h" class EditorExportPlatformWindows : public EditorExportPlatformPC { - Error _rcedit_add_data(const Ref<EditorExportPreset> &p_preset, const String &p_path); + Error _rcedit_add_data(const Ref<EditorExportPreset> &p_preset, const String &p_path, bool p_set_icon); Error _code_sign(const Ref<EditorExportPreset> &p_preset, const String &p_path); - Error _export_debug_script(const Ref<EditorExportPreset> &p_preset, const String &p_app_name, const String &p_pkg_name, const String &p_path); public: virtual Error export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags = 0) override; diff --git a/platform/windows/gl_manager_windows.cpp b/platform/windows/gl_manager_windows.cpp index 7689751f1b..900bca8258 100644 --- a/platform/windows/gl_manager_windows.cpp +++ b/platform/windows/gl_manager_windows.cpp @@ -339,6 +339,16 @@ bool GLManager_Windows::is_using_vsync() const { return use_vsync; } +HDC GLManager_Windows::get_hdc(DisplayServer::WindowID p_window_id) { + return get_window(p_window_id).hDC; +} + +HGLRC GLManager_Windows::get_hglrc(DisplayServer::WindowID p_window_id) { + const GLWindow &win = get_window(p_window_id); + const GLDisplay &disp = get_display(win.gldisplay_id); + return disp.hRC; +} + GLManager_Windows::GLManager_Windows(ContextType p_context_type) { context_type = p_context_type; diff --git a/platform/windows/gl_manager_windows.h b/platform/windows/gl_manager_windows.h index 5e43a3de2a..c6d5f9f855 100644 --- a/platform/windows/gl_manager_windows.h +++ b/platform/windows/gl_manager_windows.h @@ -113,6 +113,9 @@ public: void set_use_vsync(bool p_use); bool is_using_vsync() const; + HDC get_hdc(DisplayServer::WindowID p_window_id); + HGLRC get_hglrc(DisplayServer::WindowID p_window_id); + GLManager_Windows(ContextType p_context_type); ~GLManager_Windows(); }; diff --git a/platform/windows/godot_res_wrap.rc b/platform/windows/godot_res_wrap.rc new file mode 100644 index 0000000000..ed93bb1ec3 --- /dev/null +++ b/platform/windows/godot_res_wrap.rc @@ -0,0 +1,31 @@ +#include "core/version.h" +#ifndef _STR +#define _STR(m_x) #m_x +#define _MKSTR(m_x) _STR(m_x) +#endif + +1 VERSIONINFO +FILEVERSION VERSION_MAJOR,VERSION_MINOR,VERSION_PATCH,0 +PRODUCTVERSION VERSION_MAJOR,VERSION_MINOR,VERSION_PATCH,0 +FILEOS 4 +FILETYPE 1 +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904b0" + BEGIN + VALUE "CompanyName", "Godot Engine" + VALUE "FileDescription", VERSION_NAME " (Console)" + VALUE "FileVersion", VERSION_NUMBER + VALUE "ProductName", VERSION_NAME " (Console)" + VALUE "Licence", "MIT" + VALUE "LegalCopyright", "Copyright (c) 2007-" _MKSTR(VERSION_YEAR) " Juan Linietsky, Ariel Manzur and contributors" + VALUE "Info", "https://godotengine.org" + VALUE "ProductVersion", VERSION_FULL_BUILD + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 + END +END diff --git a/platform/windows/godot_windows.cpp b/platform/windows/godot_windows.cpp index 72920d2816..13602f7cbc 100644 --- a/platform/windows/godot_windows.cpp +++ b/platform/windows/godot_windows.cpp @@ -87,7 +87,8 @@ CommandLineToArgvA( i = 0; j = 0; - while ((a = CmdLine[i])) { + a = CmdLine[i]; + while (a) { if (in_QM) { if (a == '\"') { in_QM = FALSE; @@ -130,6 +131,7 @@ CommandLineToArgvA( } } i++; + a = CmdLine[i]; } _argv[j] = '\0'; argv[argc] = nullptr; diff --git a/platform/windows/joypad_windows.cpp b/platform/windows/joypad_windows.cpp index d039fd13a7..2b5c8cad48 100644 --- a/platform/windows/joypad_windows.cpp +++ b/platform/windows/joypad_windows.cpp @@ -167,7 +167,7 @@ bool JoypadWindows::setup_dinput_joypad(const DIDEVICEINSTANCE *instance) { const GUID &guid = instance->guidProduct; char uid[128]; - ERR_FAIL_COND_V_MSG(memcmp(&guid.Data4[2], "PIDVID", 6), false, "DirectInput device not recognised."); + ERR_FAIL_COND_V_MSG(memcmp(&guid.Data4[2], "PIDVID", 6), false, "DirectInput device not recognized."); WORD type = BSWAP16(0x03); WORD vendor = BSWAP16(LOWORD(guid.Data1)); WORD product = BSWAP16(HIWORD(guid.Data1)); diff --git a/platform/windows/joypad_windows.h b/platform/windows/joypad_windows.h index d239471a5c..56a9f3e9c9 100644 --- a/platform/windows/joypad_windows.h +++ b/platform/windows/joypad_windows.h @@ -85,6 +85,8 @@ private: last_pad = -1; attached = false; confirmed = false; + di_joy = nullptr; + guid = {}; for (int i = 0; i < MAX_JOY_BUTTONS; i++) { last_buttons[i] = false; diff --git a/platform/windows/os_windows.cpp b/platform/windows/os_windows.cpp index 1978ec5ab6..08897bb190 100644 --- a/platform/windows/os_windows.cpp +++ b/platform/windows/os_windows.cpp @@ -53,6 +53,7 @@ #include <process.h> #include <regstr.h> #include <shlobj.h> +#include <wbemcli.h> extern "C" { __declspec(dllexport) DWORD NvOptimusEnablement = 1; @@ -102,8 +103,6 @@ void RedirectIOToConsole() { RedirectStream("CONIN$", "r", stdin, STD_INPUT_HANDLE); RedirectStream("CONOUT$", "w", stdout, STD_OUTPUT_HANDLE); RedirectStream("CONOUT$", "w", stderr, STD_ERROR_HANDLE); - - printf("\n"); // Make sure our output is starting from the new line. } } @@ -295,11 +294,12 @@ String OS_Windows::get_distribution_name() const { } String OS_Windows::get_version() const { - typedef LONG NTSTATUS, *PNTSTATUS; + typedef LONG NTSTATUS; typedef NTSTATUS(WINAPI * RtlGetVersionPtr)(PRTL_OSVERSIONINFOW); RtlGetVersionPtr version_ptr = (RtlGetVersionPtr)GetProcAddress(GetModuleHandle("ntdll.dll"), "RtlGetVersion"); if (version_ptr != nullptr) { - RTL_OSVERSIONINFOW fow = { 0 }; + RTL_OSVERSIONINFOW fow; + ZeroMemory(&fow, sizeof(fow)); fow.dwOSVersionInfoSize = sizeof(fow); if (version_ptr(&fow) == 0x00000000) { return vformat("%d.%d.%d", (int64_t)fow.dwMajorVersion, (int64_t)fow.dwMinorVersion, (int64_t)fow.dwBuildNumber); @@ -308,6 +308,103 @@ String OS_Windows::get_version() const { return ""; } +Vector<String> OS_Windows::get_video_adapter_driver_info() const { + if (RenderingServer::get_singleton()->get_rendering_device() == nullptr) { + return Vector<String>(); + } + + REFCLSID clsid = CLSID_WbemLocator; // Unmarshaler CLSID + REFIID uuid = IID_IWbemLocator; // Interface UUID + IWbemLocator *wbemLocator = NULL; // to get the services + IWbemServices *wbemServices = NULL; // to get the class + IEnumWbemClassObject *iter = NULL; + IWbemClassObject *pnpSDriverObject[1]; // contains driver name, version, etc. + static String driver_name; + static String driver_version; + + const String device_name = RenderingServer::get_singleton()->get_rendering_device()->get_device_name(); + if (device_name.is_empty()) { + return Vector<String>(); + } + + CoInitialize(nullptr); + + HRESULT hr = CoCreateInstance(clsid, NULL, CLSCTX_INPROC_SERVER, uuid, (LPVOID *)&wbemLocator); + if (hr != S_OK) { + return Vector<String>(); + } + BSTR resource_name = SysAllocString(L"root\\CIMV2"); + hr = wbemLocator->ConnectServer(resource_name, NULL, NULL, NULL, 0, NULL, NULL, &wbemServices); + SysFreeString(resource_name); + + SAFE_RELEASE(wbemLocator) // from now on, use `wbemServices` + if (hr != S_OK) { + SAFE_RELEASE(wbemServices) + return Vector<String>(); + } + + const String gpu_device_class_query = vformat("SELECT * FROM Win32_PnPSignedDriver WHERE DeviceName = \"%s\"", device_name); + BSTR query = SysAllocString((const WCHAR *)gpu_device_class_query.utf16().get_data()); + BSTR query_lang = SysAllocString(L"WQL"); + hr = wbemServices->ExecQuery(query_lang, query, WBEM_FLAG_RETURN_IMMEDIATELY | WBEM_FLAG_FORWARD_ONLY, NULL, &iter); + SysFreeString(query_lang); + SysFreeString(query); + if (hr == S_OK) { + ULONG resultCount; + hr = iter->Next(5000, 1, pnpSDriverObject, &resultCount); // Get exactly 1. Wait max 5 seconds. + + if (hr == S_OK && resultCount > 0) { + VARIANT dn; + VariantInit(&dn); + + BSTR object_name = SysAllocString(L"DriverName"); + hr = pnpSDriverObject[0]->Get(object_name, 0, &dn, NULL, NULL); + SysFreeString(object_name); + if (hr == S_OK) { + String d_name = String(V_BSTR(&dn)); + if (d_name.is_empty()) { + object_name = SysAllocString(L"DriverProviderName"); + hr = pnpSDriverObject[0]->Get(object_name, 0, &dn, NULL, NULL); + SysFreeString(object_name); + if (hr == S_OK) { + driver_name = String(V_BSTR(&dn)); + } + } else { + driver_name = d_name; + } + } else { + object_name = SysAllocString(L"DriverProviderName"); + hr = pnpSDriverObject[0]->Get(object_name, 0, &dn, NULL, NULL); + SysFreeString(object_name); + if (hr == S_OK) { + driver_name = String(V_BSTR(&dn)); + } + } + + VARIANT dv; + VariantInit(&dv); + object_name = SysAllocString(L"DriverVersion"); + hr = pnpSDriverObject[0]->Get(object_name, 0, &dv, NULL, NULL); + SysFreeString(object_name); + if (hr == S_OK) { + driver_version = String(V_BSTR(&dv)); + } + for (ULONG i = 0; i < resultCount; i++) { + SAFE_RELEASE(pnpSDriverObject[i]) + } + } + } + + SAFE_RELEASE(wbemServices) + SAFE_RELEASE(iter) + + Vector<String> info; + info.push_back(driver_name); + info.push_back(driver_version); + + return info; +} + OS::DateTime OS_Windows::get_datetime(bool p_utc) const { SYSTEMTIME systemtime; if (p_utc) { @@ -870,17 +967,6 @@ BOOL is_wow64() { return wow64; } -int OS_Windows::get_processor_count() const { - SYSTEM_INFO sysinfo; - if (is_wow64()) { - GetNativeSystemInfo(&sysinfo); - } else { - GetSystemInfo(&sysinfo); - } - - return sysinfo.dwNumberOfProcessors; -} - String OS_Windows::get_processor_name() const { const String id = "Hardware\\Description\\System\\CentralProcessor\\0"; @@ -1069,11 +1155,11 @@ String OS_Windows::get_system_dir(SystemDir p_dir, bool p_shared_storage) const } String OS_Windows::get_user_data_dir() const { - String appname = get_safe_dir_name(ProjectSettings::get_singleton()->get("application/config/name")); + String appname = get_safe_dir_name(GLOBAL_GET("application/config/name")); if (!appname.is_empty()) { - bool use_custom_dir = ProjectSettings::get_singleton()->get("application/config/use_custom_user_dir"); + bool use_custom_dir = GLOBAL_GET("application/config/use_custom_user_dir"); if (use_custom_dir) { - String custom_dir = get_safe_dir_name(ProjectSettings::get_singleton()->get("application/config/custom_user_dir_name"), true); + String custom_dir = get_safe_dir_name(GLOBAL_GET("application/config/custom_user_dir_name"), true); if (custom_dir.is_empty()) { custom_dir = appname; } @@ -1093,7 +1179,14 @@ String OS_Windows::get_unique_id() const { } bool OS_Windows::_check_internal_feature_support(const String &p_feature) { - return p_feature == "pc"; + if (p_feature == "system_fonts") { + return true; + } + if (p_feature == "pc") { + return true; + } + + return false; } void OS_Windows::disable_crash_handler() { @@ -1133,15 +1226,7 @@ Error OS_Windows::move_to_trash(const String &p_path) { } OS_Windows::OS_Windows(HINSTANCE _hInstance) { - ticks_per_second = 0; - ticks_start = 0; - main_loop = nullptr; - process_map = nullptr; - hInstance = _hInstance; -#ifdef STDOUT_FILE - stdo = fopen("stdout.txt", "wb"); -#endif #ifdef WASAPI_ENABLED AudioDriverManager::add_driver(&driver_wasapi); @@ -1157,11 +1242,8 @@ OS_Windows::OS_Windows(HINSTANCE _hInstance) { // // NOTE: The engine does not use ANSI escape codes to color error/warning messages; it uses Windows API calls instead. // Therefore, error/warning messages are still colored on Windows versions older than 10. - HANDLE stdoutHandle; - stdoutHandle = GetStdHandle(STD_OUTPUT_HANDLE); - DWORD outMode = 0; - outMode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING; - + HANDLE stdoutHandle = GetStdHandle(STD_OUTPUT_HANDLE); + DWORD outMode = ENABLE_PROCESSED_OUTPUT | ENABLE_VIRTUAL_TERMINAL_PROCESSING; if (!SetConsoleMode(stdoutHandle, outMode)) { // Windows 8.1 or below, or Windows 10 prior to Anniversary Update. print_verbose("Can't set the ENABLE_VIRTUAL_TERMINAL_PROCESSING Windows console mode. `print_rich()` will not work as expected."); @@ -1173,7 +1255,4 @@ OS_Windows::OS_Windows(HINSTANCE _hInstance) { } OS_Windows::~OS_Windows() { -#ifdef STDOUT_FILE - fclose(stdo); -#endif } diff --git a/platform/windows/os_windows.h b/platform/windows/os_windows.h index 491de2266f..6f89be699a 100644 --- a/platform/windows/os_windows.h +++ b/platform/windows/os_windows.h @@ -40,6 +40,7 @@ #include "drivers/winmidi/midi_driver_winmidi.h" #include "key_mapping_windows.h" #include "servers/audio_server.h" + #ifdef XAUDIO2_ENABLED #include "drivers/xaudio2/audio_driver_xaudio2.h" #endif @@ -49,10 +50,10 @@ #include "platform/windows/vulkan_context_win.h" #endif -#include <fcntl.h> #include <io.h> #include <shellapi.h> #include <stdio.h> + #define WIN32_LEAN_AND_MEAN #include <windows.h> #include <windowsx.h> @@ -62,6 +63,10 @@ #define WINDOWS_DEBUG_OUTPUT_ENABLED #endif +#ifndef ENABLE_VIRTUAL_TERMINAL_PROCESSING +#define ENABLE_VIRTUAL_TERMINAL_PROCESSING 0x4 +#endif + template <class T> class ComAutoreleaseRef { public: @@ -83,13 +88,10 @@ public: }; class JoypadWindows; -class OS_Windows : public OS { -#ifdef STDOUT_FILE - FILE *stdo = nullptr; -#endif - uint64_t ticks_start; - uint64_t ticks_per_second; +class OS_Windows : public OS { + uint64_t ticks_start = 0; + uint64_t ticks_per_second = 0; HINSTANCE hInstance; MainLoop *main_loop = nullptr; @@ -129,7 +131,7 @@ protected: STARTUPINFO si; PROCESS_INFORMATION pi; }; - HashMap<ProcessID, ProcessInfo> *process_map; + HashMap<ProcessID, ProcessInfo> *process_map = nullptr; public: virtual void alert(const String &p_alert, const String &p_title = "ALERT!") override; @@ -146,6 +148,8 @@ public: virtual String get_distribution_name() const override; virtual String get_version() const override; + virtual Vector<String> get_video_adapter_driver_info() const override; + virtual void initialize_joypads() override {} virtual DateTime get_datetime(bool p_utc) const override; @@ -174,7 +178,6 @@ public: virtual String get_locale() const override; - virtual int get_processor_count() const override; virtual String get_processor_name() const override; virtual uint64_t get_embedded_pck_offset() const override; diff --git a/platform/windows/platform_config.h b/platform/windows/platform_config.h index 8e80f8cacb..7f0042d76f 100644 --- a/platform/windows/platform_config.h +++ b/platform/windows/platform_config.h @@ -30,4 +30,4 @@ #include <malloc.h> -#define OPENGL_INCLUDE_H "thirdparty/glad/glad/glad.h" +#define OPENGL_INCLUDE_H "thirdparty/glad/glad/gl.h" diff --git a/platform/windows/vulkan_context_win.cpp b/platform/windows/vulkan_context_win.cpp index e62c6c1dc8..ff9318e47e 100644 --- a/platform/windows/vulkan_context_win.cpp +++ b/platform/windows/vulkan_context_win.cpp @@ -41,7 +41,7 @@ const char *VulkanContextWindows::_get_platform_surface_extension() const { return VK_KHR_WIN32_SURFACE_EXTENSION_NAME; } -int VulkanContextWindows::window_create(DisplayServer::WindowID p_window_id, DisplayServer::VSyncMode p_vsync_mode, HWND p_window, HINSTANCE p_instance, int p_width, int p_height) { +Error VulkanContextWindows::window_create(DisplayServer::WindowID p_window_id, DisplayServer::VSyncMode p_vsync_mode, HWND p_window, HINSTANCE p_instance, int p_width, int p_height) { VkWin32SurfaceCreateInfoKHR createInfo; createInfo.sType = VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR; createInfo.pNext = nullptr; @@ -50,7 +50,7 @@ int VulkanContextWindows::window_create(DisplayServer::WindowID p_window_id, Dis createInfo.hwnd = p_window; VkSurfaceKHR surface; VkResult err = vkCreateWin32SurfaceKHR(get_instance(), &createInfo, nullptr, &surface); - ERR_FAIL_COND_V(err, -1); + ERR_FAIL_COND_V(err, ERR_CANT_CREATE); return _window_create(p_window_id, p_vsync_mode, surface, p_width, p_height); } diff --git a/platform/windows/vulkan_context_win.h b/platform/windows/vulkan_context_win.h index d5950a129a..9dedcabb2b 100644 --- a/platform/windows/vulkan_context_win.h +++ b/platform/windows/vulkan_context_win.h @@ -31,6 +31,8 @@ #ifndef VULKAN_CONTEXT_WIN_H #define VULKAN_CONTEXT_WIN_H +#ifdef VULKAN_ENABLED + #include "drivers/vulkan/vulkan_context.h" #define WIN32_LEAN_AND_MEAN @@ -40,10 +42,12 @@ class VulkanContextWindows : public VulkanContext { virtual const char *_get_platform_surface_extension() const; public: - int window_create(DisplayServer::WindowID p_window_id, DisplayServer::VSyncMode p_vsync_mode, HWND p_window, HINSTANCE p_instance, int p_width, int p_height); + Error window_create(DisplayServer::WindowID p_window_id, DisplayServer::VSyncMode p_vsync_mode, HWND p_window, HINSTANCE p_instance, int p_width, int p_height); VulkanContextWindows(); ~VulkanContextWindows(); }; +#endif // VULKAN_ENABLED + #endif // VULKAN_CONTEXT_WIN_H |