diff options
Diffstat (limited to 'platform/javascript')
-rw-r--r-- | platform/javascript/api/api.cpp | 6 | ||||
-rw-r--r-- | platform/javascript/api/api.h | 4 | ||||
-rw-r--r-- | platform/javascript/api/javascript_eval.h | 6 | ||||
-rw-r--r-- | platform/javascript/audio_driver_javascript.cpp | 117 | ||||
-rw-r--r-- | platform/javascript/audio_driver_javascript.h | 12 | ||||
-rw-r--r-- | platform/javascript/detect.py | 5 | ||||
-rw-r--r-- | platform/javascript/dom_keys.inc | 6 | ||||
-rw-r--r-- | platform/javascript/engine.js | 6 | ||||
-rw-r--r-- | platform/javascript/export/export.cpp | 55 | ||||
-rw-r--r-- | platform/javascript/export/export.h | 4 | ||||
-rw-r--r-- | platform/javascript/http_client.h.inc | 4 | ||||
-rw-r--r-- | platform/javascript/http_client_javascript.cpp | 12 | ||||
-rw-r--r-- | platform/javascript/http_request.h | 4 | ||||
-rw-r--r-- | platform/javascript/http_request.js | 8 | ||||
-rw-r--r-- | platform/javascript/javascript_eval.cpp | 11 | ||||
-rw-r--r-- | platform/javascript/javascript_main.cpp | 6 | ||||
-rw-r--r-- | platform/javascript/os_javascript.cpp | 318 | ||||
-rw-r--r-- | platform/javascript/os_javascript.h | 19 | ||||
-rw-r--r-- | platform/javascript/platform_config.h | 4 | ||||
-rw-r--r-- | platform/javascript/pre.js | 3 |
20 files changed, 492 insertions, 118 deletions
diff --git a/platform/javascript/api/api.cpp b/platform/javascript/api/api.cpp index b377ca4e52..d4dc43d57c 100644 --- a/platform/javascript/api/api.cpp +++ b/platform/javascript/api/api.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 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 */ @@ -29,7 +29,7 @@ /*************************************************************************/ #include "api.h" -#include "engine.h" +#include "core/engine.h" #include "javascript_eval.h" static JavaScript *javascript_eval; diff --git a/platform/javascript/api/api.h b/platform/javascript/api/api.h index 5f22f082e9..52d87528f6 100644 --- a/platform/javascript/api/api.h +++ b/platform/javascript/api/api.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 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 */ diff --git a/platform/javascript/api/javascript_eval.h b/platform/javascript/api/javascript_eval.h index 05f7c9f38a..49e460fffd 100644 --- a/platform/javascript/api/javascript_eval.h +++ b/platform/javascript/api/javascript_eval.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 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 */ @@ -31,7 +31,7 @@ #ifndef JAVASCRIPT_EVAL_H #define JAVASCRIPT_EVAL_H -#include "object.h" +#include "core/object.h" class JavaScript : public Object { private: diff --git a/platform/javascript/audio_driver_javascript.cpp b/platform/javascript/audio_driver_javascript.cpp index 7a6613bb32..11104007e2 100644 --- a/platform/javascript/audio_driver_javascript.cpp +++ b/platform/javascript/audio_driver_javascript.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 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 */ @@ -44,6 +44,11 @@ extern "C" EMSCRIPTEN_KEEPALIVE void audio_driver_js_mix() { AudioDriverJavaScript::singleton->mix_to_js(); } +extern "C" EMSCRIPTEN_KEEPALIVE void audio_driver_process_capture(float sample) { + + AudioDriverJavaScript::singleton->process_capture(sample); +} + void AudioDriverJavaScript::mix_to_js() { int channel_count = get_total_channels_by_speaker_mode(get_speaker_mode()); @@ -51,31 +56,39 @@ void AudioDriverJavaScript::mix_to_js() { int32_t *stream_buffer = reinterpret_cast<int32_t *>(internal_buffer); audio_server_process(sample_count, stream_buffer); for (int i = 0; i < sample_count * channel_count; i++) { - internal_buffer[i] = float(stream_buffer[i] >> 16) / 32768.0; + internal_buffer[i] = float(stream_buffer[i] >> 16) / 32768.f; } } +void AudioDriverJavaScript::process_capture(float sample) { + + int32_t sample32 = int32_t(sample * 32768.f) * (1U << 16); + input_buffer_write(sample32); +} + Error AudioDriverJavaScript::init() { /* clang-format off */ EM_ASM({ _audioDriver_audioContext = new (window.AudioContext || window.webkitAudioContext); + _audioDriver_audioInput = null; + _audioDriver_inputStream = null; _audioDriver_scriptNode = null; }); /* clang-format on */ int channel_count = get_total_channels_by_speaker_mode(get_speaker_mode()); /* clang-format off */ - int buffer_length = EM_ASM_INT({ + buffer_length = EM_ASM_INT({ var CHANNEL_COUNT = $0; var channelCount = _audioDriver_audioContext.destination.channelCount; try { // Try letting the browser recommend a buffer length. - _audioDriver_scriptNode = _audioDriver_audioContext.createScriptProcessor(0, 0, channelCount); + _audioDriver_scriptNode = _audioDriver_audioContext.createScriptProcessor(0, 2, channelCount); } catch (e) { // ...otherwise, default to 4096. - _audioDriver_scriptNode = _audioDriver_audioContext.createScriptProcessor(4096, 0, channelCount); + _audioDriver_scriptNode = _audioDriver_audioContext.createScriptProcessor(4096, 2, channelCount); } _audioDriver_scriptNode.connect(_audioDriver_audioContext.destination); @@ -91,6 +104,7 @@ Error AudioDriverJavaScript::init() { memdelete_arr(internal_buffer); internal_buffer = memnew_arr(float, buffer_length *channel_count); } + return internal_buffer ? OK : ERR_OUT_OF_MEMORY; } @@ -101,11 +115,13 @@ void AudioDriverJavaScript::start() { var INTERNAL_BUFFER_PTR = $0; var audioDriverMixFunction = cwrap('audio_driver_js_mix'); + var audioDriverProcessCapture = cwrap('audio_driver_process_capture', null, ['number']); _audioDriver_scriptNode.onaudioprocess = function(audioProcessingEvent) { audioDriverMixFunction(); - // The output buffer contains the samples that will be modified and played. + + var input = audioProcessingEvent.inputBuffer; var output = audioProcessingEvent.outputBuffer; - var input = HEAPF32.subarray( + var internalBuffer = HEAPF32.subarray( INTERNAL_BUFFER_PTR / HEAPF32.BYTES_PER_ELEMENT, INTERNAL_BUFFER_PTR / HEAPF32.BYTES_PER_ELEMENT + output.length * output.numberOfChannels); @@ -113,8 +129,16 @@ void AudioDriverJavaScript::start() { var outputData = output.getChannelData(channel); // Loop through samples. for (var sample = 0; sample < outputData.length; sample++) { - // Set output equal to input. - outputData[sample] = input[sample * output.numberOfChannels + channel]; + outputData[sample] = internalBuffer[sample * output.numberOfChannels + channel]; + } + } + + if (_audioDriver_audioInput) { + var inputDataL = input.getChannelData(0); + var inputDataR = input.getChannelData(1); + for (var i = 0; i < inputDataL.length; i++) { + audioDriverProcessCapture(inputDataL[i]); + audioDriverProcessCapture(inputDataR[i]); } } }; @@ -122,6 +146,15 @@ void AudioDriverJavaScript::start() { /* clang-format on */ } +void AudioDriverJavaScript::resume() { + /* clang-format off */ + EM_ASM({ + if (_audioDriver_audioContext.resume) + _audioDriver_audioContext.resume(); + }); + /* clang-format on */ +} + int AudioDriverJavaScript::get_mix_rate() const { /* clang-format off */ @@ -152,14 +185,74 @@ void AudioDriverJavaScript::finish() { /* clang-format off */ EM_ASM({ _audioDriver_audioContext = null; + _audioDriver_audioInput = null; _audioDriver_scriptNode = null; }); /* clang-format on */ - memdelete_arr(internal_buffer); - internal_buffer = NULL; + + if (internal_buffer) { + memdelete_arr(internal_buffer); + internal_buffer = NULL; + } +} + +Error AudioDriverJavaScript::capture_start() { + + input_buffer_init(buffer_length); + + /* clang-format off */ + EM_ASM({ + function gotMediaInput(stream) { + _audioDriver_inputStream = stream; + _audioDriver_audioInput = _audioDriver_audioContext.createMediaStreamSource(stream); + _audioDriver_audioInput.connect(_audioDriver_scriptNode); + } + + function gotMediaInputError(e) { + out(e); + } + + if (navigator.mediaDevices.getUserMedia) { + navigator.mediaDevices.getUserMedia({"audio": true}).then(gotMediaInput, gotMediaInputError); + } else { + if (!navigator.getUserMedia) + navigator.getUserMedia = navigator.webkitGetUserMedia || navigator.mozGetUserMedia; + navigator.getUserMedia({"audio": true}, gotMediaInput, gotMediaInputError); + } + }); + /* clang-format on */ + + return OK; +} + +Error AudioDriverJavaScript::capture_stop() { + + /* clang-format off */ + EM_ASM({ + if (_audioDriver_inputStream) { + const tracks = _audioDriver_inputStream.getTracks(); + for (var i = 0; i < tracks.length; i++) { + tracks[i].stop(); + } + _audioDriver_inputStream = null; + } + + if (_audioDriver_audioInput) { + _audioDriver_audioInput.disconnect(); + _audioDriver_audioInput = null; + } + + }); + /* clang-format on */ + + input_buffer.clear(); + + return OK; } AudioDriverJavaScript::AudioDriverJavaScript() { + internal_buffer = NULL; + singleton = this; } diff --git a/platform/javascript/audio_driver_javascript.h b/platform/javascript/audio_driver_javascript.h index a65a8ec29f..9dcba02c96 100644 --- a/platform/javascript/audio_driver_javascript.h +++ b/platform/javascript/audio_driver_javascript.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 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 */ @@ -37,20 +37,28 @@ class AudioDriverJavaScript : public AudioDriver { float *internal_buffer; + int buffer_length; + public: void mix_to_js(); + void process_capture(float sample); + static AudioDriverJavaScript *singleton; virtual const char *get_name() const; virtual Error init(); virtual void start(); + void resume(); virtual int get_mix_rate() const; virtual SpeakerMode get_speaker_mode() const; virtual void lock(); virtual void unlock(); virtual void finish(); + virtual Error capture_start(); + virtual Error capture_stop(); + AudioDriverJavaScript(); }; diff --git a/platform/javascript/detect.py b/platform/javascript/detect.py index 17b31f8d73..47da8de5df 100644 --- a/platform/javascript/detect.py +++ b/platform/javascript/detect.py @@ -122,6 +122,7 @@ def configure(env): ## Link flags env.Append(LINKFLAGS=['-s', 'BINARYEN=1']) + env.Append(LINKFLAGS=['-s', 'BINARYEN_TRAP_MODE=\'clamp\'']) # Allow increasing memory buffer size during runtime. This is efficient # when using WebAssembly (in comparison to asm.js) and works well for @@ -135,7 +136,3 @@ def configure(env): # TODO: Reevaluate usage of this setting now that engine.js manages engine runtime. env.Append(LINKFLAGS=['-s', 'NO_EXIT_RUNTIME=1']) - - # TODO: Move that to opus module's config. - if 'module_opus_enabled' in env and env['module_opus_enabled']: - env.opus_fixed_point = 'yes' diff --git a/platform/javascript/dom_keys.inc b/platform/javascript/dom_keys.inc index dc8d67d52b..bf99ea5d42 100644 --- a/platform/javascript/dom_keys.inc +++ b/platform/javascript/dom_keys.inc @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 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 */ @@ -28,7 +28,7 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#include "os/keyboard.h" +#include "core/os/keyboard.h" // https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/keyCode#Constants_for_keyCode_value #define DOM_VK_CANCEL 0x03 diff --git a/platform/javascript/engine.js b/platform/javascript/engine.js index c3ef5bbbb5..860d6707ff 100644 --- a/platform/javascript/engine.js +++ b/platform/javascript/engine.js @@ -1,3 +1,6 @@ + // The following is concatenated with generated code, and acts as the end + // of a wrapper for said code. See pre.js for the other part of the + // wrapper. exposedLibs['PATH'] = PATH; exposedLibs['FS'] = FS; return Module; @@ -196,7 +199,8 @@ } LIBS.FS.mkdirTree(dir); } - LIBS.FS.createDataFile('/', file.path, new Uint8Array(file.buffer), true, true, true); + // With memory growth, canOwn should be false. + LIBS.FS.createDataFile(file.path, null, new Uint8Array(file.buffer), true, true, false); }, this); preloadedFiles = null; diff --git a/platform/javascript/export/export.cpp b/platform/javascript/export/export.cpp index a7f0084562..871a8769d9 100644 --- a/platform/javascript/export/export.cpp +++ b/platform/javascript/export/export.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 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 */ @@ -28,9 +28,9 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ +#include "core/io/zip_io.h" #include "editor/editor_node.h" #include "editor_export.h" -#include "io/zip_io.h" #include "main/splash.gen.h" #include "platform/javascript/logo.gen.h" #include "platform/javascript/run_icon.gen.h" @@ -58,7 +58,7 @@ public: virtual Ref<Texture> get_logo() const; virtual bool can_export(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates) const; - virtual String get_binary_extension(const Ref<EditorExportPreset> &p_preset) const; + virtual List<String> get_binary_extensions(const Ref<EditorExportPreset> &p_preset) const; virtual Error export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags = 0); virtual bool poll_devices(); @@ -104,22 +104,24 @@ void EditorExportPlatformJavaScript::_fix_html(Vector<uint8_t> &p_html, const Re void EditorExportPlatformJavaScript::get_preset_features(const Ref<EditorExportPreset> &p_preset, List<String> *r_features) { - if (p_preset->get("texture_format/s3tc")) { + if (p_preset->get("vram_texture_compression/for_desktop")) { r_features->push_back("s3tc"); } - if (p_preset->get("texture_format/etc")) { - r_features->push_back("etc"); - } - if (p_preset->get("texture_format/etc2")) { - r_features->push_back("etc2"); + + if (p_preset->get("vram_texture_compression/for_mobile")) { + String driver = ProjectSettings::get_singleton()->get("rendering/quality/driver/driver_name"); + if (driver == "GLES2") { + r_features->push_back("etc"); + } else if (driver == "GLES3") { + r_features->push_back("etc2"); + } } } void EditorExportPlatformJavaScript::get_export_options(List<ExportOption> *r_options) { - r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "texture_format/s3tc"), true)); - r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "texture_format/etc"), false)); - r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "texture_format/etc2"), true)); + r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "vram_texture_compression/for_desktop"), true)); // S3TC + r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "vram_texture_compression/for_mobile"), false)); // ETC or ETC2, depending on renderer r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "html/custom_html_shell", PROPERTY_HINT_FILE, "*.html"), "")); r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "html/head_include", PROPERTY_HINT_MULTILINE_TEXT), "")); r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "custom_template/release", PROPERTY_HINT_GLOBAL_FILE, "*.zip"), "")); @@ -155,7 +157,7 @@ bool EditorExportPlatformJavaScript::can_export(const Ref<EditorExportPreset> &p if (FileAccess::exists(p_preset->get("custom_template/debug"))) { valid = true; } else { - err += "Custom debug template not found.\n"; + err += TTR("Custom debug template not found.") + "\n"; } } @@ -163,20 +165,31 @@ bool EditorExportPlatformJavaScript::can_export(const Ref<EditorExportPreset> &p if (FileAccess::exists(p_preset->get("custom_template/release"))) { valid = true; } else { - err += "Custom release template not found.\n"; + err += TTR("Custom release template not found.") + "\n"; + } + } + + r_missing_templates = !valid; + + if (p_preset->get("vram_texture_compression/for_mobile")) { + String etc_error = test_etc2(); + if (etc_error != String()) { + valid = false; + err += etc_error; } } if (!err.empty()) r_error = err; - r_missing_templates = !valid; return valid; } -String EditorExportPlatformJavaScript::get_binary_extension(const Ref<EditorExportPreset> &p_preset) const { +List<String> EditorExportPlatformJavaScript::get_binary_extensions(const Ref<EditorExportPreset> &p_preset) const { - return "html"; + List<String> list; + list.push_back("html"); + return list; } Error EditorExportPlatformJavaScript::export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags) { @@ -198,6 +211,10 @@ Error EditorExportPlatformJavaScript::export_project(const Ref<EditorExportPrese template_path = find_export_template(EXPORT_TEMPLATE_WEBASSEMBLY_RELEASE); } + if (!DirAccess::exists(p_path.get_base_dir())) { + return ERR_FILE_BAD_PATH; + } + if (template_path != String() && !FileAccess::exists(template_path)) { EditorNode::get_singleton()->show_warning(TTR("Template file not found:") + "\n" + template_path); return ERR_FILE_NOT_FOUND; @@ -347,7 +364,7 @@ Error EditorExportPlatformJavaScript::run(const Ref<EditorExportPreset> &p_prese if (err) { return err; } - OS::get_singleton()->shell_open(path); + OS::get_singleton()->shell_open(String("file://") + path); return OK; } diff --git a/platform/javascript/export/export.h b/platform/javascript/export/export.h index 2835d0723f..7ebbcd6f00 100644 --- a/platform/javascript/export/export.h +++ b/platform/javascript/export/export.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 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 */ diff --git a/platform/javascript/http_client.h.inc b/platform/javascript/http_client.h.inc index d75d33a33a..d707d623ab 100644 --- a/platform/javascript/http_client.h.inc +++ b/platform/javascript/http_client.h.inc @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 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 */ diff --git a/platform/javascript/http_client_javascript.cpp b/platform/javascript/http_client_javascript.cpp index 8d90e01ae1..b4bab9a999 100644 --- a/platform/javascript/http_client_javascript.cpp +++ b/platform/javascript/http_client_javascript.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 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 */ @@ -28,8 +28,8 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ +#include "core/io/http_client.h" #include "http_request.h" -#include "io/http_client.h" Error HTTPClient::connect_to_host(const String &p_host, int p_port, bool p_ssl, bool p_verify_host) { @@ -237,7 +237,7 @@ Error HTTPClient::poll() { case STATUS_CONNECTION_ERROR: return ERR_CONNECTION_ERROR; - case STATUS_REQUESTING: + case STATUS_REQUESTING: { #ifdef DEBUG_ENABLED if (!has_polled) { @@ -281,6 +281,10 @@ Error HTTPClient::poll() { godot_xhr_get_response(xhr_id, write.ptr(), polled_response.size()); write = PoolByteArray::Write(); break; + } + + default: + ERR_FAIL_V(ERR_BUG); } return OK; } diff --git a/platform/javascript/http_request.h b/platform/javascript/http_request.h index b5ff46d7fe..4a596057da 100644 --- a/platform/javascript/http_request.h +++ b/platform/javascript/http_request.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 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 */ diff --git a/platform/javascript/http_request.js b/platform/javascript/http_request.js index ee1c06c623..66dacfc3d4 100644 --- a/platform/javascript/http_request.js +++ b/platform/javascript/http_request.js @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -82,7 +82,7 @@ var GodotHTTPRequest = { godot_xhr_send_string: function(xhrId, strPtr) { if (!strPtr) { - console.warn("Failed to send string per XHR: null pointer"); + err("Failed to send string per XHR: null pointer"); return; } GodotHTTPRequest.requests[xhrId].send(UTF8ToString(strPtr)); @@ -90,11 +90,11 @@ var GodotHTTPRequest = { godot_xhr_send_data: function(xhrId, ptr, len) { if (!ptr) { - console.warn("Failed to send data per XHR: null pointer"); + err("Failed to send data per XHR: null pointer"); return; } if (len < 0) { - console.warn("Failed to send data per XHR: buffer length less than 0"); + err("Failed to send data per XHR: buffer length less than 0"); return; } GodotHTTPRequest.requests[xhrId].send(HEAPU8.subarray(ptr, ptr + len)); diff --git a/platform/javascript/javascript_eval.cpp b/platform/javascript/javascript_eval.cpp index 07b4c192e6..dd3eba74e4 100644 --- a/platform/javascript/javascript_eval.cpp +++ b/platform/javascript/javascript_eval.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 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 */ @@ -69,7 +69,7 @@ Variant JavaScript::eval(const String &p_code, bool p_use_global_exec_context) { eval_ret = eval(UTF8ToString(CODE)); } } catch (e) { - console.warn(e); + err(e); eval_ret = null; } @@ -97,7 +97,7 @@ Variant JavaScript::eval(const String &p_code, bool p_use_global_exec_context) { if (array_ptr!==0) { _free(array_ptr) } - console.warn(e); + err(e); // fall through } break; @@ -140,8 +140,9 @@ Variant JavaScript::eval(const String &p_code, bool p_use_global_exec_context) { case Variant::POOL_BYTE_ARRAY: arr_write = PoolByteArray::Write(); return arr; + default: + return Variant(); } - return Variant(); } #endif // JAVASCRIPT_EVAL_ENABLED diff --git a/platform/javascript/javascript_main.cpp b/platform/javascript/javascript_main.cpp index 3829e8d406..bdf9dfe18f 100644 --- a/platform/javascript/javascript_main.cpp +++ b/platform/javascript/javascript_main.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 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 */ @@ -28,7 +28,7 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#include "io/resource_loader.h" +#include "core/io/resource_loader.h" #include "main/main.h" #include "os_javascript.h" diff --git a/platform/javascript/os_javascript.cpp b/platform/javascript/os_javascript.cpp index 80699b0d32..34781ce365 100644 --- a/platform/javascript/os_javascript.cpp +++ b/platform/javascript/os_javascript.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 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 */ @@ -30,15 +30,16 @@ #include "os_javascript.h" -#include "gles2/rasterizer_gles2.h" -#include "gles3/rasterizer_gles3.h" -#include "io/file_access_buffered_fa.h" +#include "core/io/file_access_buffered_fa.h" +#include "drivers/gles2/rasterizer_gles2.h" +#include "drivers/gles3/rasterizer_gles3.h" +#include "drivers/unix/dir_access_unix.h" +#include "drivers/unix/file_access_unix.h" #include "main/main.h" #include "servers/visual/visual_server_raster.h" -#include "unix/dir_access_unix.h" -#include "unix/file_access_unix.h" #include <emscripten.h> +#include <png.h> #include <stdlib.h> #include "dom_keys.inc" @@ -120,14 +121,14 @@ void OS_JavaScript::set_window_size(const Size2 p_size) { emscripten_exit_soft_fullscreen(); window_maximized = false; } - emscripten_set_canvas_size(p_size.x, p_size.y); + emscripten_set_canvas_element_size(NULL, p_size.x, p_size.y); } } Size2 OS_JavaScript::get_window_size() const { - int canvas[3]; - emscripten_get_canvas_size(canvas, canvas + 1, canvas + 2); + int canvas[2]; + emscripten_get_canvas_element_size(NULL, canvas, canvas + 1); return Size2(canvas[0], canvas[1]); } @@ -244,6 +245,8 @@ EM_BOOL OS_JavaScript::keydown_callback(int p_event_type, const EmscriptenKeyboa return false; } os->input->parse_input_event(ev); + // Resume audio context after input in case autoplay was denied. + os->audio_driver_javascript.resume(); return true; } @@ -294,6 +297,30 @@ EM_BOOL OS_JavaScript::mouse_button_callback(int p_event_type, const EmscriptenM default: return false; } + if (ev->is_pressed()) { + + uint64_t diff = p_event->timestamp - os->last_click_ms; + + if (ev->get_button_index() == os->last_click_button_index) { + + if (diff < 400 && Point2(os->last_click_pos).distance_to(ev->get_position()) < 5) { + + os->last_click_ms = 0; + os->last_click_pos = Point2(-100, -100); + os->last_click_button_index = -1; + ev->set_doubleclick(true); + } + + } else { + os->last_click_button_index = ev->get_button_index(); + } + + if (!ev->is_doubleclick()) { + os->last_click_ms += diff; + os->last_click_pos = ev->get_position(); + } + } + int mask = os->input->get_mouse_button_mask(); int button_flag = 1 << (ev->get_button_index() - 1); if (ev->is_pressed()) { @@ -310,6 +337,8 @@ EM_BOOL OS_JavaScript::mouse_button_callback(int p_event_type, const EmscriptenM ev->set_button_mask(mask); os->input->parse_input_event(ev); + // Resume audio context after input in case autoplay was denied. + os->audio_driver_javascript.resume(); // Prevent multi-click text selection and wheel-click scrolling anchor. // Context menu is prevented through contextmenu event. return true; @@ -377,27 +406,147 @@ static void set_css_cursor(const char *p_cursor) { /* clang-format on */ } -static const char *get_css_cursor() { +static bool is_css_cursor_hidden() { - char cursor[16]; /* clang-format off */ - EM_ASM_INT({ - stringToUTF8(Module.canvas.style.cursor ? Module.canvas.style.cursor : 'auto', $0, 16); - }, cursor); + return EM_ASM_INT({ + return Module.canvas.style.cursor === 'none'; + }); /* clang-format on */ - return cursor; } void OS_JavaScript::set_cursor_shape(CursorShape p_shape) { ERR_FAIL_INDEX(p_shape, CURSOR_MAX); + if (get_mouse_mode() == MOUSE_MODE_VISIBLE) { + if (cursors[p_shape] != "") { + Vector<String> url = cursors[p_shape].split("?"); + set_css_cursor(("url(\"" + url[0] + "\") " + url[1] + ", auto").utf8()); + } else { + set_css_cursor(godot2dom_cursor(p_shape)); + } + } + cursor_shape = p_shape; - if (get_mouse_mode() != MOUSE_MODE_HIDDEN) - set_css_cursor(godot2dom_cursor(cursor_shape)); } void OS_JavaScript::set_custom_mouse_cursor(const RES &p_cursor, CursorShape p_shape, const Vector2 &p_hotspot) { + + if (p_cursor.is_valid()) { + Ref<Texture> texture = p_cursor; + Ref<AtlasTexture> atlas_texture = p_cursor; + Ref<Image> image; + Size2 texture_size; + Rect2 atlas_rect; + + if (texture.is_valid()) { + image = texture->get_data(); + if (image.is_valid()) { + image->duplicate(); + } + } + + if (!image.is_valid() && atlas_texture.is_valid()) { + texture = atlas_texture->get_atlas(); + + atlas_rect.size.width = texture->get_width(); + atlas_rect.size.height = texture->get_height(); + atlas_rect.position.x = atlas_texture->get_region().position.x; + atlas_rect.position.y = atlas_texture->get_region().position.y; + + texture_size.width = atlas_texture->get_region().size.x; + texture_size.height = atlas_texture->get_region().size.y; + } else if (image.is_valid()) { + 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_data(); + + ERR_FAIL_COND(!image.is_valid()); + + image = image->duplicate(); + + if (atlas_texture.is_valid()) + image->crop_from_point( + atlas_rect.position.x, + atlas_rect.position.y, + texture_size.width, + texture_size.height); + + if (image->get_format() != Image::FORMAT_RGBA8) { + image->convert(Image::FORMAT_RGBA8); + } + + png_image png_meta; + memset(&png_meta, 0, sizeof png_meta); + png_meta.version = PNG_IMAGE_VERSION; + png_meta.width = texture_size.width; + png_meta.height = texture_size.height; + png_meta.format = PNG_FORMAT_RGBA; + + PoolByteArray png; + size_t len; + PoolByteArray::Read r = image->get_data().read(); + ERR_FAIL_COND(!png_image_write_get_memory_size(png_meta, len, 0, r.ptr(), 0, NULL)); + + png.resize(len); + PoolByteArray::Write w = png.write(); + ERR_FAIL_COND(!png_image_write_to_memory(&png_meta, w.ptr(), &len, 0, r.ptr(), 0, NULL)); + w = PoolByteArray::Write(); + + r = png.read(); + + char *object_url; + /* clang-format off */ + EM_ASM({ + var PNG_PTR = $0; + var PNG_LEN = $1; + var PTR = $2; + + var png = new Blob([HEAPU8.slice(PNG_PTR, PNG_PTR + PNG_LEN)], { type: 'image/png' }); + var url = URL.createObjectURL(png); + var length_bytes = lengthBytesUTF8(url) + 1; + var string_on_wasm_heap = _malloc(length_bytes); + setValue(PTR, string_on_wasm_heap, '*'); + stringToUTF8(url, string_on_wasm_heap, length_bytes); + }, r.ptr(), len, &object_url); + /* clang-format on */ + r = PoolByteArray::Read(); + + String url = String::utf8(object_url) + "?" + itos(p_hotspot.x) + " " + itos(p_hotspot.y); + + /* clang-format off */ + EM_ASM({ _free($0); }, object_url); + /* clang-format on */ + + if (cursors[p_shape] != "") { + /* clang-format off */ + EM_ASM({ + URL.revokeObjectURL(UTF8ToString($0).split('?')[0]); + }, cursors[p_shape].utf8().get_data()); + /* clang-format on */ + cursors[p_shape] = ""; + } + + cursors[p_shape] = url; + + } else if (cursors[p_shape] != "") { + /* clang-format off */ + EM_ASM({ + URL.revokeObjectURL(UTF8ToString($0).split('?')[0]); + }, cursors[p_shape].utf8().get_data()); + /* clang-format on */ + cursors[p_shape] = ""; + } + + set_cursor_shape(cursor_shape); } void OS_JavaScript::set_mouse_mode(OS::MouseMode p_mode) { @@ -409,7 +558,9 @@ void OS_JavaScript::set_mouse_mode(OS::MouseMode p_mode) { if (p_mode == MOUSE_MODE_VISIBLE) { + // set_css_cursor must be called before set_cursor_shape to make the cursor visible set_css_cursor(godot2dom_cursor(cursor_shape)); + set_cursor_shape(cursor_shape); emscripten_exit_pointerlock(); } else if (p_mode == MOUSE_MODE_HIDDEN) { @@ -423,13 +574,15 @@ void OS_JavaScript::set_mouse_mode(OS::MouseMode p_mode) { ERR_EXPLAIN("MOUSE_MODE_CAPTURED can only be entered from within an appropriate input callback"); ERR_FAIL_COND(result == EMSCRIPTEN_RESULT_FAILED_NOT_DEFERRED); ERR_FAIL_COND(result != EMSCRIPTEN_RESULT_SUCCESS); + // set_css_cursor must be called before set_cursor_shape to make the cursor visible set_css_cursor(godot2dom_cursor(cursor_shape)); + set_cursor_shape(cursor_shape); } } OS::MouseMode OS_JavaScript::get_mouse_mode() const { - if (String::utf8(get_css_cursor()) == "none") + if (is_css_cursor_hidden()) return MOUSE_MODE_HIDDEN; EmscriptenPointerlockChangeEvent ev; @@ -453,7 +606,6 @@ EM_BOOL OS_JavaScript::wheel_callback(int p_event_type, const EmscriptenWheelEve InputDefault *input = get_singleton()->input; Ref<InputEventMouseButton> ev; ev.instance(); - ev->set_button_mask(input->get_mouse_button_mask()); ev->set_position(input->get_mouse_position()); ev->set_global_position(ev->get_position()); @@ -476,10 +628,14 @@ EM_BOOL OS_JavaScript::wheel_callback(int p_event_type, const EmscriptenWheelEve // Different browsers give wildly different delta values, and we can't // interpret deltaMode, so use default value for wheel events' factor. + int button_flag = 1 << (ev->get_button_index() - 1); + ev->set_pressed(true); + ev->set_button_mask(input->get_mouse_button_mask() | button_flag); input->parse_input_event(ev); ev->set_pressed(false); + ev->set_button_mask(input->get_mouse_button_mask() & ~button_flag); input->parse_input_event(ev); return true; @@ -516,6 +672,8 @@ EM_BOOL OS_JavaScript::touch_press_callback(int p_event_type, const EmscriptenTo os->input->parse_input_event(ev); } + // Resume audio context after input in case autoplay was denied. + os->audio_driver_javascript.resume(); return true; } @@ -565,8 +723,11 @@ void OS_JavaScript::process_joypads() { int joypad_count = emscripten_get_num_gamepads(); for (int joypad = 0; joypad < joypad_count; joypad++) { EmscriptenGamepadEvent state; - emscripten_get_gamepad_status(joypad, &state); - if (state.connected) { + EMSCRIPTEN_RESULT query_result = emscripten_get_gamepad_status(joypad, &state); + // Chromium reserves gamepads slots, so NO_DATA is an expected result. + ERR_CONTINUE(query_result != EMSCRIPTEN_RESULT_SUCCESS && + query_result != EMSCRIPTEN_RESULT_NO_DATA); + if (query_result == EMSCRIPTEN_RESULT_SUCCESS && state.connected) { int button_count = MIN(state.numButtons, 18); int axis_count = MIN(state.numAxes, 8); @@ -668,7 +829,7 @@ Error OS_JavaScript::initialize(const VideoMode &p_desired, int p_video_driver, RasterizerGLES3::make_current(); break; } else { - if (GLOBAL_GET("rendering/quality/driver/driver_fallback") == "Best") { + if (GLOBAL_GET("rendering/quality/driver/fallback_to_gles2")) { p_video_driver = VIDEO_DRIVER_GLES2; gles3 = false; continue; @@ -705,8 +866,21 @@ Error OS_JavaScript::initialize(const VideoMode &p_desired, int p_video_driver, video_driver_index = p_video_driver; video_mode = p_desired; - // Can't fulfil fullscreen request during start-up due to browser security. + // fullscreen_change_callback will correct this if the request is successful. video_mode.fullscreen = false; + // Emscripten only attempts fullscreen requests if the user input callback + // was registered through one its own functions, so request manually for + // start-up fullscreen. + if (p_desired.fullscreen) { + /* clang-format off */ + EM_ASM({ + (canvas.requestFullscreen || canvas.msRequestFullscreen || + canvas.mozRequestFullScreen || canvas.mozRequestFullscreen || + canvas.webkitRequestFullscreen + ).call(canvas); + }); + /* clang-format on */ + } /* clang-format off */ if (EM_ASM_INT_V({ return Module.resizeCanvasOnStart })) { /* clang-format on */ @@ -812,15 +986,16 @@ bool OS_JavaScript::main_loop_iterate() { if (sync_wait_time < 0) { /* clang-format off */ EM_ASM( - FS.syncfs(function(err) { - if (err) { console.warn('Failed to save IDB file system: ' + err.message); } + FS.syncfs(function(error) { + if (error) { err('Failed to save IDB file system: ' + error.message); } }); ); /* clang-format on */ } } - process_joypads(); + if (emscripten_sample_gamepad_data() == EMSCRIPTEN_RESULT_SUCCESS) + process_joypads(); if (just_exited_fullscreen) { if (window_maximized) { @@ -831,13 +1006,13 @@ bool OS_JavaScript::main_loop_iterate() { strategy.canvasResizedCallback = NULL; emscripten_enter_soft_fullscreen(NULL, &strategy); } else { - emscripten_set_canvas_size(windowed_size.width, windowed_size.height); + emscripten_set_canvas_element_size(NULL, windowed_size.width, windowed_size.height); } just_exited_fullscreen = false; } - int canvas[3]; - emscripten_get_canvas_size(canvas, canvas + 1, canvas + 2); + int canvas[2]; + emscripten_get_canvas_element_size(NULL, canvas, canvas + 1); video_mode.width = canvas[0]; video_mode.height = canvas[1]; if (!window_maximized && !video_mode.fullscreen && !just_exited_fullscreen && !entering_fullscreen) { @@ -860,6 +1035,24 @@ void OS_JavaScript::finalize() { // Miscellaneous +Error OS_JavaScript::execute(const String &p_path, const List<String> &p_arguments, bool p_blocking, ProcessID *r_child_id, String *r_pipe, int *r_exitcode, bool read_stderr) { + + ERR_EXPLAIN("OS::execute() is not available on the HTML5 platform"); + ERR_FAIL_V(ERR_UNAVAILABLE); +} + +Error OS_JavaScript::kill(const ProcessID &p_pid) { + + ERR_EXPLAIN("OS::kill() is not available on the HTML5 platform"); + ERR_FAIL_V(ERR_UNAVAILABLE); +} + +int OS_JavaScript::get_process_id() const { + + ERR_EXPLAIN("OS::get_process_id() is not available on the HTML5 platform"); + ERR_FAIL_V(0); +} + extern "C" EMSCRIPTEN_KEEPALIVE void send_notification(int p_notification) { if (p_notification == MainLoop::NOTIFICATION_WM_MOUSE_ENTER || p_notification == MainLoop::NOTIFICATION_WM_MOUSE_EXIT) { @@ -878,16 +1071,6 @@ bool OS_JavaScript::_check_internal_feature_support(const String &p_feature) { return true; #endif - EMSCRIPTEN_WEBGL_CONTEXT_HANDLE ctx = emscripten_webgl_get_current_context(); - // All extensions are already automatically enabled, this function allows - // checking WebGL extension support without inline JavaScript - if (p_feature == "s3tc") - return emscripten_webgl_enable_extension(ctx, "WEBGL_compressed_texture_s3tc_srgb"); - if (p_feature == "etc") - return emscripten_webgl_enable_extension(ctx, "WEBGL_compressed_texture_etc1"); - if (p_feature == "etc2") - return emscripten_webgl_enable_extension(ctx, "WEBGL_compressed_texture_etc"); - return false; } @@ -909,6 +1092,57 @@ void OS_JavaScript::set_window_title(const String &p_title) { /* clang-format on */ } +void OS_JavaScript::set_icon(const Ref<Image> &p_icon) { + + ERR_FAIL_COND(p_icon.is_null()); + Ref<Image> icon = p_icon; + if (icon->is_compressed()) { + icon = icon->duplicate(); + ERR_FAIL_COND(icon->decompress() != OK) + } + if (icon->get_format() != Image::FORMAT_RGBA8) { + if (icon == p_icon) + icon = icon->duplicate(); + icon->convert(Image::FORMAT_RGBA8); + } + + png_image png_meta; + memset(&png_meta, 0, sizeof png_meta); + png_meta.version = PNG_IMAGE_VERSION; + png_meta.width = icon->get_width(); + png_meta.height = icon->get_height(); + png_meta.format = PNG_FORMAT_RGBA; + + PoolByteArray png; + size_t len; + PoolByteArray::Read r = icon->get_data().read(); + ERR_FAIL_COND(!png_image_write_get_memory_size(png_meta, len, 0, r.ptr(), 0, NULL)); + + png.resize(len); + PoolByteArray::Write w = png.write(); + ERR_FAIL_COND(!png_image_write_to_memory(&png_meta, w.ptr(), &len, 0, r.ptr(), 0, NULL)); + w = PoolByteArray::Write(); + + r = png.read(); + /* clang-format off */ + EM_ASM_ARGS({ + var PNG_PTR = $0; + var PNG_LEN = $1; + + var png = new Blob([HEAPU8.slice(PNG_PTR, PNG_PTR + PNG_LEN)], { type: "image/png" }); + var url = URL.createObjectURL(png); + var link = document.getElementById('-gd-engine-icon'); + if (link === null) { + link = document.createElement('link'); + link.rel = 'icon'; + link.id = '-gd-engine-icon'; + document.head.appendChild(link); + } + link.href = url; + }, r.ptr(), len); + /* clang-format on */ +} + String OS_JavaScript::get_executable_path() const { return OS::get_executable_path(); @@ -996,6 +1230,10 @@ OS_JavaScript::OS_JavaScript(int p_argc, char *p_argv[]) { } set_cmdline(p_argv[0], arguments); + last_click_button_index = -1; + last_click_ms = 0; + last_click_pos = Point2(-100, -100); + window_maximized = false; entering_fullscreen = false; just_exited_fullscreen = false; diff --git a/platform/javascript/os_javascript.h b/platform/javascript/os_javascript.h index f40fb8fc7e..a9f9e23463 100644 --- a/platform/javascript/os_javascript.h +++ b/platform/javascript/os_javascript.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 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 */ @@ -32,10 +32,10 @@ #define OS_JAVASCRIPT_H #include "audio_driver_javascript.h" +#include "drivers/unix/os_unix.h" #include "main/input_default.h" #include "servers/audio_server.h" #include "servers/visual/rasterizer.h" -#include "unix/os_unix.h" #include <emscripten/html5.h> @@ -50,9 +50,15 @@ class OS_JavaScript : public OS_Unix { InputDefault *input; Ref<InputEventKey> deferred_key_event; CursorShape cursor_shape; + String cursors[CURSOR_MAX]; Point2 touches[32]; + Point2i last_click_pos; + uint64_t last_click_ms; + int last_click_button_index; + MainLoop *main_loop; + int video_driver_index; AudioDriverJavaScript audio_driver_javascript; bool idb_available; @@ -80,8 +86,6 @@ class OS_JavaScript : public OS_Unix { static void file_access_close_callback(const String &p_file, int p_flags); - int video_driver_index; - protected: virtual int get_current_video_driver() const; @@ -133,8 +137,13 @@ public: void run_async(); bool main_loop_iterate(); + virtual Error execute(const String &p_path, const List<String> &p_arguments, bool p_blocking, ProcessID *r_child_id = NULL, String *r_pipe = NULL, int *r_exitcode = NULL, bool read_stderr = false); + virtual Error kill(const ProcessID &p_pid); + virtual int get_process_id() const; + virtual void alert(const String &p_alert, const String &p_title = "ALERT!"); virtual void set_window_title(const String &p_title); + virtual void set_icon(const Ref<Image> &p_icon); String get_executable_path() const; virtual Error shell_open(String p_uri); virtual String get_name(); diff --git a/platform/javascript/platform_config.h b/platform/javascript/platform_config.h index af4cf07393..baba6325b3 100644 --- a/platform/javascript/platform_config.h +++ b/platform/javascript/platform_config.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 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 */ diff --git a/platform/javascript/pre.js b/platform/javascript/pre.js index 02194bc75e..a870e676ea 100644 --- a/platform/javascript/pre.js +++ b/platform/javascript/pre.js @@ -1,2 +1,5 @@ var Engine = { RuntimeEnvironment: function(Module, exposedLibs) { + // The above is concatenated with generated code, and acts as the start of + // a wrapper for said code. See engine.js for the other part of the + // wrapper. |