diff options
Diffstat (limited to 'platform/web')
-rw-r--r-- | platform/web/api/web_tools_editor_plugin.cpp | 2 | ||||
-rw-r--r-- | platform/web/audio_driver_web.cpp | 2 | ||||
-rw-r--r-- | platform/web/detect.py | 10 | ||||
-rw-r--r-- | platform/web/display_server_web.cpp | 36 | ||||
-rw-r--r-- | platform/web/display_server_web.h | 5 | ||||
-rw-r--r-- | platform/web/doc_classes/EditorExportPlatformWeb.xml | 54 | ||||
-rw-r--r-- | platform/web/dom_keys.inc | 2 | ||||
-rw-r--r-- | platform/web/export/export.cpp | 5 | ||||
-rw-r--r-- | platform/web/export/export.h | 1 | ||||
-rw-r--r-- | platform/web/export/export_plugin.cpp | 41 | ||||
-rw-r--r-- | platform/web/export/export_plugin.h | 2 | ||||
-rw-r--r-- | platform/web/godot_js.h | 1 | ||||
-rw-r--r-- | platform/web/js/libs/library_godot_fetch.js | 5 | ||||
-rw-r--r-- | platform/web/js/libs/library_godot_os.js | 22 | ||||
-rw-r--r-- | platform/web/os_web.cpp | 3 |
15 files changed, 159 insertions, 32 deletions
diff --git a/platform/web/api/web_tools_editor_plugin.cpp b/platform/web/api/web_tools_editor_plugin.cpp index 146a48db81..213204ff33 100644 --- a/platform/web/api/web_tools_editor_plugin.cpp +++ b/platform/web/api/web_tools_editor_plugin.cpp @@ -75,7 +75,7 @@ void WebToolsEditorPlugin::_download_zip() { const String project_name_safe = project_name.to_lower().replace(" ", "_"); const String datetime_safe = Time::get_singleton()->get_datetime_string_from_system(false, true).replace(" ", "_"); - const String output_name = OS::get_singleton()->get_safe_dir_name(vformat("%s_%s.zip")); + const String output_name = OS::get_singleton()->get_safe_dir_name(vformat("%s_%s.zip", project_name_safe, datetime_safe)); const String output_path = String("/tmp").path_join(output_name); zipFile zip = zipOpen2(output_path.utf8().get_data(), APPEND_STATUS_CREATE, nullptr, &io); diff --git a/platform/web/audio_driver_web.cpp b/platform/web/audio_driver_web.cpp index 1d7b96d707..c6c67db3de 100644 --- a/platform/web/audio_driver_web.cpp +++ b/platform/web/audio_driver_web.cpp @@ -103,7 +103,7 @@ void AudioDriverWeb::_audio_driver_capture(int p_from, int p_samples) { Error AudioDriverWeb::init() { int latency = GLOBAL_GET("audio/driver/output_latency"); if (!audio_context.inited) { - audio_context.mix_rate = GLOBAL_GET("audio/driver/mix_rate"); + audio_context.mix_rate = _get_configured_mix_rate(); audio_context.channel_count = godot_audio_init(&audio_context.mix_rate, latency, &_state_change_callback, &_latency_update_callback); audio_context.inited = true; } diff --git a/platform/web/detect.py b/platform/web/detect.py index 08c1ff7b4a..419d8918f2 100644 --- a/platform/web/detect.py +++ b/platform/web/detect.py @@ -48,6 +48,16 @@ def get_opts(): ] +def get_doc_classes(): + return [ + "EditorExportPlatformWeb", + ] + + +def get_doc_path(): + return "doc_classes" + + def get_flags(): return [ ("arch", "wasm32"), diff --git a/platform/web/display_server_web.cpp b/platform/web/display_server_web.cpp index 565d439a92..e870f0da29 100644 --- a/platform/web/display_server_web.cpp +++ b/platform/web/display_server_web.cpp @@ -30,6 +30,7 @@ #include "display_server_web.h" +#include "core/config/project_settings.h" #ifdef GLES3_ENABLED #include "drivers/gles3/rasterizer_gles3.h" #endif @@ -298,10 +299,12 @@ const char *DisplayServerWeb::godot2dom_cursor(DisplayServer::CursorShape p_shap } bool DisplayServerWeb::tts_is_speaking() const { + ERR_FAIL_COND_V_MSG(!tts, false, "Enable the \"audio/general/text_to_speech\" project setting to use text-to-speech."); return godot_js_tts_is_speaking(); } bool DisplayServerWeb::tts_is_paused() const { + ERR_FAIL_COND_V_MSG(!tts, false, "Enable the \"audio/general/text_to_speech\" project setting to use text-to-speech."); return godot_js_tts_is_paused(); } @@ -320,11 +323,13 @@ void DisplayServerWeb::update_voices_callback(int p_size, const char **p_voice) } TypedArray<Dictionary> DisplayServerWeb::tts_get_voices() const { + ERR_FAIL_COND_V_MSG(!tts, TypedArray<Dictionary>(), "Enable the \"audio/general/text_to_speech\" project setting to use text-to-speech."); godot_js_tts_get_voices(update_voices_callback); return voices; } void DisplayServerWeb::tts_speak(const String &p_text, const String &p_voice, int p_volume, float p_pitch, float p_rate, int p_utterance_id, bool p_interrupt) { + ERR_FAIL_COND_MSG(!tts, "Enable the \"audio/general/text_to_speech\" project setting to use text-to-speech."); if (p_interrupt) { tts_stop(); } @@ -341,14 +346,17 @@ void DisplayServerWeb::tts_speak(const String &p_text, const String &p_voice, in } void DisplayServerWeb::tts_pause() { + ERR_FAIL_COND_MSG(!tts, "Enable the \"audio/general/text_to_speech\" project setting to use text-to-speech."); godot_js_tts_pause(); } void DisplayServerWeb::tts_resume() { + ERR_FAIL_COND_MSG(!tts, "Enable the \"audio/general/text_to_speech\" project setting to use text-to-speech."); godot_js_tts_resume(); } void DisplayServerWeb::tts_stop() { + ERR_FAIL_COND_MSG(!tts, "Enable the \"audio/general/text_to_speech\" project setting to use text-to-speech."); for (const KeyValue<int, CharString> &E : utterance_ids) { tts_post_utterance_event(DisplayServer::TTS_UTTERANCE_CANCELED, E.key); } @@ -398,16 +406,12 @@ void DisplayServerWeb::cursor_set_custom_image(const Ref<Resource> &p_cursor, Cu ERR_FAIL_INDEX(p_shape, CURSOR_MAX); if (p_cursor.is_valid()) { Ref<Texture2D> texture = p_cursor; + ERR_FAIL_COND(!texture.is_valid()); Ref<AtlasTexture> atlas_texture = p_cursor; - Ref<Image> image; Size2 texture_size; Rect2 atlas_rect; - if (texture.is_valid()) { - image = texture->get_image(); - } - - if (!image.is_valid() && atlas_texture.is_valid()) { + if (atlas_texture.is_valid()) { texture = atlas_texture->get_atlas(); atlas_rect.size.width = texture->get_width(); @@ -417,21 +421,25 @@ void DisplayServerWeb::cursor_set_custom_image(const Ref<Resource> &p_cursor, Cu texture_size.width = atlas_texture->get_region().size.x; texture_size.height = atlas_texture->get_region().size.y; - } else if (image.is_valid()) { + } else { texture_size.width = texture->get_width(); texture_size.height = texture->get_height(); } - ERR_FAIL_COND(!texture.is_valid()); ERR_FAIL_COND(p_hotspot.x < 0 || p_hotspot.y < 0); ERR_FAIL_COND(texture_size.width > 256 || texture_size.height > 256); ERR_FAIL_COND(p_hotspot.x > texture_size.width || p_hotspot.y > texture_size.height); - image = texture->get_image(); + Ref<Image> image = texture->get_image(); ERR_FAIL_COND(!image.is_valid()); - image = image->duplicate(); + image = image->duplicate(true); + + if (image->is_compressed()) { + Error err = image->decompress(); + ERR_FAIL_COND_MSG(err != OK, "Couldn't decompress VRAM-compressed custom mouse cursor image. Switch to a lossless compression mode in the Import dock."); + } if (atlas_texture.is_valid()) { image->crop_from_point( @@ -771,6 +779,8 @@ DisplayServer *DisplayServerWeb::create_func(const String &p_rendering_driver, W 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, int p_screen, Error &r_error) { r_error = OK; // Always succeeds for now. + tts = GLOBAL_GET("audio/general/text_to_speech"); + // Ensure the canvas ID. godot_js_config_canvas_id_get(canvas_id, 256); @@ -866,7 +876,7 @@ bool DisplayServerWeb::has_feature(Feature p_feature) const { case FEATURE_VIRTUAL_KEYBOARD: return godot_js_display_vk_available() != 0; case FEATURE_TEXT_TO_SPEECH: - return godot_js_display_tts_available() != 0; + return tts && (godot_js_display_tts_available() != 0); default: return false; } @@ -1070,6 +1080,10 @@ bool DisplayServerWeb::can_any_window_draw() const { return true; } +DisplayServer::VSyncMode DisplayServerWeb::window_get_vsync_mode(WindowID p_vsync_mode) const { + return DisplayServer::VSYNC_ENABLED; +} + void DisplayServerWeb::process_events() { Input::get_singleton()->flush_buffered_events(); if (godot_js_input_gamepad_sample() == OK) { diff --git a/platform/web/display_server_web.h b/platform/web/display_server_web.h index 2e50a6bbc8..3b03b102cd 100644 --- a/platform/web/display_server_web.h +++ b/platform/web/display_server_web.h @@ -37,6 +37,8 @@ #include <emscripten/html5.h> class DisplayServerWeb : public DisplayServer { + // No need to register with GDCLASS, it's platform-specific and nothing is added. + private: struct JSTouchEvent { uint32_t identifier[32] = { 0 }; @@ -79,6 +81,7 @@ private: MouseButton last_click_button_index = MouseButton::NONE; bool swap_cancel_ok = false; + bool tts = false; // utilities static void dom2godot_mod(Ref<InputEventWithModifiers> ev, int p_mod, Key p_keycode); @@ -213,6 +216,8 @@ public: virtual bool can_any_window_draw() const override; + virtual DisplayServer::VSyncMode window_get_vsync_mode(WindowID p_vsync_mode) const override; + // events virtual void process_events() override; diff --git a/platform/web/doc_classes/EditorExportPlatformWeb.xml b/platform/web/doc_classes/EditorExportPlatformWeb.xml new file mode 100644 index 0000000000..6e5a2ac078 --- /dev/null +++ b/platform/web/doc_classes/EditorExportPlatformWeb.xml @@ -0,0 +1,54 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<class name="EditorExportPlatformWeb" inherits="EditorExportPlatform" version="4.1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd"> + <brief_description> + Exporter for the Web. + </brief_description> + <description> + </description> + <tutorials> + <link title="Exporting for the Web">$DOCS_URL/tutorials/export/exporting_for_web.html</link> + </tutorials> + <members> + <member name="custom_template/debug" type="String" setter="" getter=""> + Path to the custom export template. If left empty, default template is used. + </member> + <member name="custom_template/release" type="String" setter="" getter=""> + Path to the custom export template. If left empty, default template is used. + </member> + <member name="html/canvas_resize_policy" type="int" setter="" getter=""> + The canvas resize policy determines how the canvas should be resized by Godot. + </member> + <member name="html/custom_html_shell" type="String" setter="" getter=""> + </member> + <member name="html/experimental_virtual_keyboard" type="bool" setter="" getter=""> + </member> + <member name="html/export_icon" type="bool" setter="" getter=""> + </member> + <member name="html/focus_canvas_on_start" type="bool" setter="" getter=""> + </member> + <member name="html/head_include" type="String" setter="" getter=""> + </member> + <member name="progressive_web_app/background_color" type="Color" setter="" getter=""> + </member> + <member name="progressive_web_app/display" type="int" setter="" getter=""> + </member> + <member name="progressive_web_app/enabled" type="bool" setter="" getter=""> + </member> + <member name="progressive_web_app/icon_144x144" type="String" setter="" getter=""> + </member> + <member name="progressive_web_app/icon_180x180" type="String" setter="" getter=""> + </member> + <member name="progressive_web_app/icon_512x512" type="String" setter="" getter=""> + </member> + <member name="progressive_web_app/offline_page" type="String" setter="" getter=""> + </member> + <member name="progressive_web_app/orientation" type="int" setter="" getter=""> + </member> + <member name="variant/extensions_support" type="bool" setter="" getter=""> + </member> + <member name="vram_texture_compression/for_desktop" type="bool" setter="" getter=""> + </member> + <member name="vram_texture_compression/for_mobile" type="bool" setter="" getter=""> + </member> + </members> +</class> diff --git a/platform/web/dom_keys.inc b/platform/web/dom_keys.inc index ae3b2fc1a5..cd94b779c0 100644 --- a/platform/web/dom_keys.inc +++ b/platform/web/dom_keys.inc @@ -33,7 +33,7 @@ // See https://w3c.github.io/uievents-code/#code-value-tables Key dom_code2godot_scancode(EM_UTF8 const p_code[32], EM_UTF8 const p_key[32], bool p_physical) { #define DOM2GODOT(p_str, p_godot_code) \ - if (memcmp((const void *)p_str, (void *)(p_physical ? p_key : p_code), strlen(p_str) + 1) == 0) { \ + if (memcmp((const void *)p_str, (void *)(p_physical ? p_code : p_key), strlen(p_str) + 1) == 0) { \ return Key::p_godot_code; \ } diff --git a/platform/web/export/export.cpp b/platform/web/export/export.cpp index 11e728ea16..80c29024a8 100644 --- a/platform/web/export/export.cpp +++ b/platform/web/export/export.cpp @@ -31,8 +31,13 @@ #include "export.h" #include "editor/editor_settings.h" +#include "editor/export/editor_export.h" #include "export_plugin.h" +void register_web_exporter_types() { + GDREGISTER_VIRTUAL_CLASS(EditorExportPlatformWeb); +} + void register_web_exporter() { #ifndef ANDROID_ENABLED EDITOR_DEF("export/web/http_host", "localhost"); diff --git a/platform/web/export/export.h b/platform/web/export/export.h index 8d2bbfff26..da02bd8d93 100644 --- a/platform/web/export/export.h +++ b/platform/web/export/export.h @@ -31,6 +31,7 @@ #ifndef WEB_EXPORT_H #define WEB_EXPORT_H +void register_web_exporter_types(); void register_web_exporter(); #endif // WEB_EXPORT_H diff --git a/platform/web/export/export_plugin.cpp b/platform/web/export/export_plugin.cpp index d8e04904c7..2fff628c85 100644 --- a/platform/web/export/export_plugin.cpp +++ b/platform/web/export/export_plugin.cpp @@ -33,6 +33,7 @@ #include "core/config/project_settings.h" #include "editor/editor_scale.h" #include "editor/editor_settings.h" +#include "editor/export/editor_export.h" #include "platform/web/logo_svg.gen.h" #include "platform/web/run_icon_svg.gen.h" @@ -320,7 +321,7 @@ void EditorExportPlatformWeb::get_preset_features(const Ref<EditorExportPreset> r_features->push_back("wasm32"); } -void EditorExportPlatformWeb::get_export_options(List<ExportOption> *r_options) { +void EditorExportPlatformWeb::get_export_options(List<ExportOption> *r_options) const { r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "custom_template/debug", PROPERTY_HINT_GLOBAL_FILE, "*.zip"), "")); r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "custom_template/release", PROPERTY_HINT_GLOBAL_FILE, "*.zip"), "")); @@ -656,31 +657,37 @@ void EditorExportPlatformWeb::_server_thread_poll(void *data) { } EditorExportPlatformWeb::EditorExportPlatformWeb() { - server.instantiate(); - server_thread.start(_server_thread_poll, this); + if (EditorNode::get_singleton()) { + server.instantiate(); + server_thread.start(_server_thread_poll, this); #ifdef MODULE_SVG_ENABLED - Ref<Image> img = memnew(Image); - const bool upsample = !Math::is_equal_approx(Math::round(EDSCALE), EDSCALE); + Ref<Image> img = memnew(Image); + const bool upsample = !Math::is_equal_approx(Math::round(EDSCALE), EDSCALE); - ImageLoaderSVG img_loader; - img_loader.create_image_from_string(img, _web_logo_svg, EDSCALE, upsample, false); - logo = ImageTexture::create_from_image(img); + ImageLoaderSVG img_loader; + img_loader.create_image_from_string(img, _web_logo_svg, EDSCALE, upsample, false); + logo = ImageTexture::create_from_image(img); - img_loader.create_image_from_string(img, _web_run_icon_svg, EDSCALE, upsample, false); - run_icon = ImageTexture::create_from_image(img); + img_loader.create_image_from_string(img, _web_run_icon_svg, EDSCALE, upsample, false); + run_icon = ImageTexture::create_from_image(img); #endif - Ref<Theme> theme = EditorNode::get_singleton()->get_editor_theme(); - if (theme.is_valid()) { - stop_icon = theme->get_icon(SNAME("Stop"), SNAME("EditorIcons")); - } else { - stop_icon.instantiate(); + Ref<Theme> theme = EditorNode::get_singleton()->get_editor_theme(); + if (theme.is_valid()) { + stop_icon = theme->get_icon(SNAME("Stop"), SNAME("EditorIcons")); + } else { + stop_icon.instantiate(); + } } } EditorExportPlatformWeb::~EditorExportPlatformWeb() { - server->stop(); + if (server.is_valid()) { + server->stop(); + } server_quit = true; - server_thread.wait_to_finish(); + if (server_thread.is_started()) { + server_thread.wait_to_finish(); + } } diff --git a/platform/web/export/export_plugin.h b/platform/web/export/export_plugin.h index e74c945837..334f12d64d 100644 --- a/platform/web/export/export_plugin.h +++ b/platform/web/export/export_plugin.h @@ -99,7 +99,7 @@ class EditorExportPlatformWeb : public EditorExportPlatform { public: virtual void get_preset_features(const Ref<EditorExportPreset> &p_preset, List<String> *r_features) const override; - virtual void get_export_options(List<ExportOption> *r_options) override; + virtual void get_export_options(List<ExportOption> *r_options) const override; virtual String get_name() const override; virtual String get_os_name() const override; diff --git a/platform/web/godot_js.h b/platform/web/godot_js.h index 3a41f63fa3..660822e291 100644 --- a/platform/web/godot_js.h +++ b/platform/web/godot_js.h @@ -49,6 +49,7 @@ extern void godot_js_os_fs_sync(void (*p_callback)()); extern int godot_js_os_execute(const char *p_json); extern void godot_js_os_shell_open(const char *p_uri); extern int godot_js_os_hw_concurrency_get(); +extern int godot_js_os_has_feature(const char *p_ftr); extern int godot_js_pwa_cb(void (*p_callback)()); extern int godot_js_pwa_update(); diff --git a/platform/web/js/libs/library_godot_fetch.js b/platform/web/js/libs/library_godot_fetch.js index b50012c1e2..1bb48bfd6a 100644 --- a/platform/web/js/libs/library_godot_fetch.js +++ b/platform/web/js/libs/library_godot_fetch.js @@ -50,17 +50,22 @@ const GodotFetch = { return; } let chunked = false; + let bodySize = -1; response.headers.forEach(function (value, header) { const v = value.toLowerCase().trim(); const h = header.toLowerCase().trim(); if (h === 'transfer-encoding' && v === 'chunked') { chunked = true; } + if (h === 'content-length') { + bodySize = parseInt(v, 10); + } }); obj.status = response.status; obj.response = response; obj.reader = response.body.getReader(); obj.chunked = chunked; + obj.bodySize = bodySize; }, onerror: function (id, err) { diff --git a/platform/web/js/libs/library_godot_os.js b/platform/web/js/libs/library_godot_os.js index c4c45a3036..00ae399583 100644 --- a/platform/web/js/libs/library_godot_os.js +++ b/platform/web/js/libs/library_godot_os.js @@ -291,6 +291,28 @@ const GodotOS = { }); }, + godot_js_os_has_feature__sig: 'ii', + godot_js_os_has_feature: function (p_ftr) { + const ftr = GodotRuntime.parseString(p_ftr); + const ua = navigator.userAgent; + if (ftr === 'web_macos') { + return (ua.indexOf('Mac') !== -1) ? 1 : 0; + } + if (ftr === 'web_windows') { + return (ua.indexOf('Windows') !== -1) ? 1 : 0; + } + if (ftr === 'web_android') { + return (ua.indexOf('Android') !== -1) ? 1 : 0; + } + if (ftr === 'web_ios') { + return ((ua.indexOf('iPhone') !== -1) || (ua.indexOf('iPad') !== -1) || (ua.indexOf('iPod') !== -1)) ? 1 : 0; + } + if (ftr === 'web_linuxbsd') { + return ((ua.indexOf('CrOS') !== -1) || (ua.indexOf('BSD') !== -1) || (ua.indexOf('Linux') !== -1) || (ua.indexOf('X11') !== -1)) ? 1 : 0; + } + return 0; + }, + godot_js_os_execute__sig: 'ii', godot_js_os_execute: function (p_json) { const json_args = GodotRuntime.parseString(p_json); diff --git a/platform/web/os_web.cpp b/platform/web/os_web.cpp index 964bce01da..0f84e7e841 100644 --- a/platform/web/os_web.cpp +++ b/platform/web/os_web.cpp @@ -136,6 +136,9 @@ bool OS_Web::_check_internal_feature_support(const String &p_feature) { if (p_feature == "web") { return true; } + if (godot_js_os_has_feature(p_feature.utf8().get_data())) { + return true; + } return false; } |