summaryrefslogtreecommitdiff
path: root/platform/javascript
diff options
context:
space:
mode:
Diffstat (limited to 'platform/javascript')
-rw-r--r--platform/javascript/SCsub2
-rw-r--r--platform/javascript/api/api.cpp2
-rw-r--r--platform/javascript/api/javascript_eval.h2
-rw-r--r--platform/javascript/audio_driver_javascript.cpp104
-rw-r--r--platform/javascript/audio_driver_javascript.h7
-rw-r--r--platform/javascript/detect.py6
-rw-r--r--platform/javascript/dom_keys.inc2
-rw-r--r--platform/javascript/engine.js3
-rw-r--r--platform/javascript/export/export.cpp54
-rw-r--r--platform/javascript/http_client_javascript.cpp8
-rw-r--r--platform/javascript/http_request.js6
-rw-r--r--platform/javascript/javascript_eval.cpp7
-rw-r--r--platform/javascript/javascript_main.cpp2
-rw-r--r--platform/javascript/os_javascript.cpp289
-rw-r--r--platform/javascript/os_javascript.h16
-rw-r--r--platform/javascript/pre.js3
16 files changed, 384 insertions, 129 deletions
diff --git a/platform/javascript/SCsub b/platform/javascript/SCsub
index 98988d97fd..a93c98a89f 100644
--- a/platform/javascript/SCsub
+++ b/platform/javascript/SCsub
@@ -32,6 +32,6 @@ zip_files = env.InstallAs([
], [
js_wrapped,
wasm,
- '#misc/dist/html/default.html'
+ '#misc/dist/html/full-size.html'
])
env.Zip('#bin/godot', zip_files, ZIPROOT=zip_dir, ZIPSUFFIX='${PROGSUFFIX}${ZIPSUFFIX}', ZIPCOMSTR='Archving $SOURCES as $TARGET')
diff --git a/platform/javascript/api/api.cpp b/platform/javascript/api/api.cpp
index b377ca4e52..c7a6d53561 100644
--- a/platform/javascript/api/api.cpp
+++ b/platform/javascript/api/api.cpp
@@ -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/javascript_eval.h b/platform/javascript/api/javascript_eval.h
index 05f7c9f38a..49d5309737 100644
--- a/platform/javascript/api/javascript_eval.h
+++ b/platform/javascript/api/javascript_eval.h
@@ -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..a5b627b8dc 100644
--- a/platform/javascript/audio_driver_javascript.cpp
+++ b/platform/javascript/audio_driver_javascript.cpp
@@ -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]);
}
}
};
@@ -152,14 +176,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) {
+ console.log(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..c8aeb0b446 100644
--- a/platform/javascript/audio_driver_javascript.h
+++ b/platform/javascript/audio_driver_javascript.h
@@ -37,8 +37,12 @@ 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;
@@ -51,6 +55,9 @@ public:
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 fc909f6619..22b5f1f87a 100644
--- a/platform/javascript/detect.py
+++ b/platform/javascript/detect.py
@@ -25,7 +25,6 @@ def get_opts():
def get_flags():
return [
('tools', False),
- ('module_theora_enabled', False),
# Disabling the mbedtls module reduces file size.
# The module has little use due to the limited networking functionality
# in this platform. For the available networking methods, the browser
@@ -123,12 +122,17 @@ 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
# us since we don't know requirements at compile-time.
env.Append(LINKFLAGS=['-s', 'ALLOW_MEMORY_GROWTH=1'])
+ # Since we use both memory growth and MEMFS preloading,
+ # this avoids unecessary copying on start-up.
+ env.Append(LINKFLAGS=['--no-heap-copy'])
+
# This setting just makes WebGL 2 APIs available, it does NOT disable WebGL 1.
env.Append(LINKFLAGS=['-s', 'USE_WEBGL2=1'])
diff --git a/platform/javascript/dom_keys.inc b/platform/javascript/dom_keys.inc
index dc8d67d52b..a30818decc 100644
--- a/platform/javascript/dom_keys.inc
+++ b/platform/javascript/dom_keys.inc
@@ -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..91458eb4c3 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;
diff --git a/platform/javascript/export/export.cpp b/platform/javascript/export/export.cpp
index 9591850662..7a325e81dd 100644
--- a/platform/javascript/export/export.cpp
+++ b/platform/javascript/export/export.cpp
@@ -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();
@@ -74,6 +74,9 @@ public:
r_features->push_back(get_os_name());
}
+ virtual void resolve_platform_feature_priorities(const Ref<EditorExportPreset> &p_preset, Set<String> &p_features) {
+ }
+
EditorExportPlatformJavaScript();
};
@@ -95,7 +98,7 @@ void EditorExportPlatformJavaScript::_fix_html(Vector<uint8_t> &p_html, const Re
CharString cs = str_export.utf8();
p_html.resize(cs.length());
for (int i = 0; i < cs.length(); i++) {
- p_html[i] = cs[i];
+ p_html.write[i] = cs[i];
}
}
@@ -117,10 +120,10 @@ void EditorExportPlatformJavaScript::get_export_options(List<ExportOption> *r_op
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::STRING, "html/custom_html_shell", PROPERTY_HINT_FILE, "html"), ""));
+ 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"), ""));
- r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "custom_template/debug", PROPERTY_HINT_GLOBAL_FILE, "zip"), ""));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "custom_template/release", PROPERTY_HINT_GLOBAL_FILE, "*.zip"), ""));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "custom_template/debug", PROPERTY_HINT_GLOBAL_FILE, "*.zip"), ""));
}
String EditorExportPlatformJavaScript::get_name() const {
@@ -140,19 +143,42 @@ Ref<Texture> EditorExportPlatformJavaScript::get_logo() const {
bool EditorExportPlatformJavaScript::can_export(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates) const {
- r_missing_templates = false;
+ bool valid = false;
+ String err;
+
+ if (find_export_template(EXPORT_TEMPLATE_WEBASSEMBLY_RELEASE) != "")
+ valid = true;
+ else if (find_export_template(EXPORT_TEMPLATE_WEBASSEMBLY_DEBUG) != "")
+ valid = true;
+
+ if (p_preset->get("custom_template/debug") != "") {
+ if (FileAccess::exists(p_preset->get("custom_template/debug"))) {
+ valid = true;
+ } else {
+ err += "Custom debug template not found.\n";
+ }
+ }
+
+ if (p_preset->get("custom_template/release") != "") {
+ if (FileAccess::exists(p_preset->get("custom_template/release"))) {
+ valid = true;
+ } else {
+ err += "Custom release template not found.\n";
+ }
+ }
- if (find_export_template(EXPORT_TEMPLATE_WEBASSEMBLY_RELEASE) == String())
- r_missing_templates = true;
- else if (find_export_template(EXPORT_TEMPLATE_WEBASSEMBLY_DEBUG) == String())
- r_missing_templates = true;
+ if (!err.empty())
+ r_error = err;
- return !r_missing_templates;
+ 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) {
diff --git a/platform/javascript/http_client_javascript.cpp b/platform/javascript/http_client_javascript.cpp
index 8d90e01ae1..ccf4f8a11b 100644
--- a/platform/javascript/http_client_javascript.cpp
+++ b/platform/javascript/http_client_javascript.cpp
@@ -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.js b/platform/javascript/http_request.js
index c420052e54..ee1c06c623 100644
--- a/platform/javascript/http_request.js
+++ b/platform/javascript/http_request.js
@@ -82,7 +82,7 @@ var GodotHTTPRequest = {
godot_xhr_send_string: function(xhrId, strPtr) {
if (!strPtr) {
- Module.printErr("Failed to send string per XHR: null pointer");
+ console.warn("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) {
- Module.printErr("Failed to send data per XHR: null pointer");
+ console.warn("Failed to send data per XHR: null pointer");
return;
}
if (len < 0) {
- Module.printErr("Failed to send data per XHR: buffer length less than 0");
+ console.warn("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 2ef88345f6..9b8174cc71 100644
--- a/platform/javascript/javascript_eval.cpp
+++ b/platform/javascript/javascript_eval.cpp
@@ -69,7 +69,7 @@ Variant JavaScript::eval(const String &p_code, bool p_use_global_exec_context) {
eval_ret = eval(UTF8ToString(CODE));
}
} catch (e) {
- Module.printErr(e);
+ console.warn(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)
}
- Module.printErr(e);
+ console.warn(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..ec60571402 100644
--- a/platform/javascript/javascript_main.cpp
+++ b/platform/javascript/javascript_main.cpp
@@ -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 6d34b0da1f..2068fee9e0 100644
--- a/platform/javascript/os_javascript.cpp
+++ b/platform/javascript/os_javascript.cpp
@@ -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"
@@ -71,14 +72,6 @@ static bool is_canvas_focused() {
static bool cursor_inside_canvas = true;
-EM_BOOL OS_JavaScript::browser_resize_callback(int p_event_type, const EmscriptenUiEvent *p_event, void *p_user_data) {
-
- // The order of the fullscreen change event and the window size change
- // event varies, even within just one browser, so defer handling.
- get_singleton()->canvas_size_adjustment_requested = true;
- return false;
-}
-
EM_BOOL OS_JavaScript::fullscreen_change_callback(int p_event_type, const EmscriptenFullscreenChangeEvent *p_event, void *p_user_data) {
OS_JavaScript *os = get_singleton();
@@ -88,7 +81,13 @@ EM_BOOL OS_JavaScript::fullscreen_change_callback(int p_event_type, const Emscri
// This event property is the only reliable data on
// browser fullscreen state.
os->video_mode.fullscreen = p_event->isFullscreen;
- os->canvas_size_adjustment_requested = true;
+ if (os->video_mode.fullscreen) {
+ os->entering_fullscreen = false;
+ } else {
+ // Restoring maximized window now will cause issues,
+ // so delay until main_loop_iterate.
+ os->just_exited_fullscreen = true;
+ }
}
return false;
}
@@ -114,52 +113,43 @@ Size2 OS_JavaScript::get_screen_size(int p_screen) const {
void OS_JavaScript::set_window_size(const Size2 p_size) {
windowed_size = p_size;
- if (is_window_fullscreen()) {
+ if (video_mode.fullscreen) {
window_maximized = false;
set_window_fullscreen(false);
- } else if (is_window_maximized()) {
- set_window_maximized(false);
} else {
- video_mode.width = p_size.x;
- video_mode.height = p_size.y;
- emscripten_set_canvas_size(p_size.x, p_size.y);
+ if (window_maximized) {
+ emscripten_exit_soft_fullscreen();
+ window_maximized = false;
+ }
+ 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]);
}
void OS_JavaScript::set_window_maximized(bool p_enabled) {
- window_maximized = p_enabled;
- if (is_window_fullscreen()) {
+ if (video_mode.fullscreen) {
+ window_maximized = p_enabled;
set_window_fullscreen(false);
- return;
- }
- // Calling emscripten_enter_soft_fullscreen mutltiple times hides all
- // page elements except the canvas permanently, so track state.
- if (p_enabled && !soft_fullscreen_enabled) {
-
+ } else if (!p_enabled) {
+ emscripten_exit_soft_fullscreen();
+ window_maximized = false;
+ } else if (!window_maximized) {
+ // Prevent calling emscripten_enter_soft_fullscreen mutltiple times,
+ // this would hide page elements permanently.
EmscriptenFullscreenStrategy strategy;
strategy.scaleMode = EMSCRIPTEN_FULLSCREEN_SCALE_STRETCH;
strategy.canvasResolutionScaleMode = EMSCRIPTEN_FULLSCREEN_CANVAS_SCALE_STDDEF;
strategy.filteringMode = EMSCRIPTEN_FULLSCREEN_FILTERING_DEFAULT;
strategy.canvasResizedCallback = NULL;
emscripten_enter_soft_fullscreen(NULL, &strategy);
- soft_fullscreen_enabled = true;
- video_mode.width = get_window_size().width;
- video_mode.height = get_window_size().height;
- } else if (!p_enabled) {
-
- emscripten_exit_soft_fullscreen();
- soft_fullscreen_enabled = false;
- video_mode.width = windowed_size.width;
- video_mode.height = windowed_size.height;
- emscripten_set_canvas_size(video_mode.width, video_mode.height);
+ window_maximized = p_enabled;
}
}
@@ -170,30 +160,33 @@ bool OS_JavaScript::is_window_maximized() const {
void OS_JavaScript::set_window_fullscreen(bool p_enabled) {
- if (p_enabled == is_window_fullscreen()) {
+ if (p_enabled == video_mode.fullscreen) {
return;
}
- // Just request changes here, if successful, canvas is resized in
- // _browser_resize_callback or _fullscreen_change_callback.
- EMSCRIPTEN_RESULT result;
+ // Just request changes here, if successful, logic continues in
+ // fullscreen_change_callback.
if (p_enabled) {
if (window_maximized) {
- // Soft fullsreen during real fulllscreen can cause issues.
- set_window_maximized(false);
- window_maximized = true;
+ // Soft fullsreen during real fullscreen can cause issues, so exit.
+ // This must be called before requesting full screen.
+ emscripten_exit_soft_fullscreen();
}
EmscriptenFullscreenStrategy strategy;
strategy.scaleMode = EMSCRIPTEN_FULLSCREEN_SCALE_STRETCH;
strategy.canvasResolutionScaleMode = EMSCRIPTEN_FULLSCREEN_CANVAS_SCALE_STDDEF;
strategy.filteringMode = EMSCRIPTEN_FULLSCREEN_FILTERING_DEFAULT;
strategy.canvasResizedCallback = NULL;
- emscripten_request_fullscreen_strategy(NULL, false, &strategy);
+ EMSCRIPTEN_RESULT result = emscripten_request_fullscreen_strategy(NULL, false, &strategy);
+ ERR_EXPLAIN("Enabling fullscreen is only possible from an input callback for the HTML5 platform");
+ ERR_FAIL_COND(result == EMSCRIPTEN_RESULT_FAILED_NOT_DEFERRED);
+ ERR_FAIL_COND(result != EMSCRIPTEN_RESULT_SUCCESS);
+ // Not fullscreen yet, so prevent "windowed" canvas dimensions from
+ // being overwritten.
+ entering_fullscreen = true;
} else {
- result = emscripten_exit_fullscreen();
- if (result != EMSCRIPTEN_RESULT_SUCCESS) {
- ERR_PRINTS("Failed to exit fullscreen: Code " + itos(result));
- }
+ // No logic allowed here, since exiting w/ ESC key won't use this function.
+ ERR_FAIL_COND(emscripten_exit_fullscreen() != EMSCRIPTEN_RESULT_SUCCESS);
}
}
@@ -385,15 +378,13 @@ 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) {
@@ -437,7 +428,7 @@ void OS_JavaScript::set_mouse_mode(OS::MouseMode p_mode) {
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;
@@ -576,8 +567,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);
@@ -646,6 +640,9 @@ const char *OS_JavaScript::get_audio_driver_name(int p_driver) const {
}
// Lifecycle
+int OS_JavaScript::get_current_video_driver() const {
+ return video_driver_index;
+}
void OS_JavaScript::initialize_core() {
@@ -660,24 +657,60 @@ Error OS_JavaScript::initialize(const VideoMode &p_desired, int p_video_driver,
attributes.alpha = false;
attributes.antialias = false;
ERR_FAIL_INDEX_V(p_video_driver, VIDEO_DRIVER_MAX, ERR_INVALID_PARAMETER);
- switch (p_video_driver) {
- case VIDEO_DRIVER_GLES3:
- attributes.majorVersion = 2;
- RasterizerGLES3::register_config();
- RasterizerGLES3::make_current();
- break;
- case VIDEO_DRIVER_GLES2:
- attributes.majorVersion = 1;
- RasterizerGLES2::register_config();
- RasterizerGLES2::make_current();
- break;
+
+ bool gles3 = true;
+ if (p_video_driver == VIDEO_DRIVER_GLES2) {
+ gles3 = false;
}
+
+ bool gl_initialization_error = false;
+
+ while (true) {
+ if (gles3) {
+ if (RasterizerGLES3::is_viable() == OK) {
+ attributes.majorVersion = 2;
+ RasterizerGLES3::register_config();
+ RasterizerGLES3::make_current();
+ break;
+ } else {
+ if (GLOBAL_GET("rendering/quality/driver/driver_fallback") == "Best") {
+ p_video_driver = VIDEO_DRIVER_GLES2;
+ gles3 = false;
+ continue;
+ } else {
+ gl_initialization_error = true;
+ break;
+ }
+ }
+ } else {
+ if (RasterizerGLES2::is_viable() == OK) {
+ attributes.majorVersion = 1;
+ RasterizerGLES2::register_config();
+ RasterizerGLES2::make_current();
+ break;
+ } else {
+ gl_initialization_error = true;
+ break;
+ }
+ }
+ }
+
EMSCRIPTEN_WEBGL_CONTEXT_HANDLE ctx = emscripten_webgl_create_context(NULL, &attributes);
- ERR_EXPLAIN("WebGL " + itos(attributes.majorVersion) + ".0 not available");
- ERR_FAIL_COND_V(emscripten_webgl_make_context_current(ctx) != EMSCRIPTEN_RESULT_SUCCESS, ERR_UNAVAILABLE);
+ if (emscripten_webgl_make_context_current(ctx) != EMSCRIPTEN_RESULT_SUCCESS) {
+ gl_initialization_error = true;
+ }
+
+ if (gl_initialization_error) {
+ OS::get_singleton()->alert("Your browser does not support any of the supported WebGL versions.\n"
+ "Please update your browser version.",
+ "Unable to initialize Video driver");
+ return ERR_UNAVAILABLE;
+ }
+
+ video_driver_index = p_video_driver;
video_mode = p_desired;
- // Can't fulfil fullscreen request during start-up due to browser security.
+ // Can't fulfill fullscreen request during start-up due to browser security.
video_mode.fullscreen = false;
/* clang-format off */
if (EM_ASM_INT_V({ return Module.resizeCanvasOnStart })) {
@@ -723,7 +756,6 @@ Error OS_JavaScript::initialize(const VideoMode &p_desired, int p_video_driver,
SET_EM_CALLBACK("#canvas", keydown, keydown_callback)
SET_EM_CALLBACK("#canvas", keypress, keypress_callback)
SET_EM_CALLBACK("#canvas", keyup, keyup_callback)
- SET_EM_CALLBACK(NULL, resize, browser_resize_callback)
SET_EM_CALLBACK(NULL, fullscreenchange, fullscreen_change_callback)
SET_EM_CALLBACK_NOTARGET(gamepadconnected, gamepad_change_callback)
SET_EM_CALLBACK_NOTARGET(gamepaddisconnected, gamepad_change_callback)
@@ -786,24 +818,38 @@ bool OS_JavaScript::main_loop_iterate() {
/* clang-format off */
EM_ASM(
FS.syncfs(function(err) {
- if (err) { Module.printErr('Failed to save IDB file system: ' + err.message); }
+ if (err) { console.warn('Failed to save IDB file system: ' + err.message); }
});
);
/* clang-format on */
}
}
+
process_joypads();
- if (canvas_size_adjustment_requested) {
- if (video_mode.fullscreen || window_maximized) {
- video_mode.width = get_window_size().width;
- video_mode.height = get_window_size().height;
- }
- if (!video_mode.fullscreen) {
- set_window_maximized(window_maximized);
+ if (just_exited_fullscreen) {
+ if (window_maximized) {
+ EmscriptenFullscreenStrategy strategy;
+ strategy.scaleMode = EMSCRIPTEN_FULLSCREEN_SCALE_STRETCH;
+ strategy.canvasResolutionScaleMode = EMSCRIPTEN_FULLSCREEN_CANVAS_SCALE_STDDEF;
+ strategy.filteringMode = EMSCRIPTEN_FULLSCREEN_FILTERING_DEFAULT;
+ strategy.canvasResizedCallback = NULL;
+ emscripten_enter_soft_fullscreen(NULL, &strategy);
+ } else {
+ emscripten_set_canvas_element_size(NULL, windowed_size.width, windowed_size.height);
}
- canvas_size_adjustment_requested = false;
+ just_exited_fullscreen = false;
}
+
+ 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) {
+ windowed_size.width = canvas[0];
+ windowed_size.height = canvas[1];
+ }
+
return Main::iteration();
}
@@ -819,6 +865,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) {
@@ -868,6 +932,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();
@@ -956,8 +1071,8 @@ OS_JavaScript::OS_JavaScript(int p_argc, char *p_argv[]) {
set_cmdline(p_argv[0], arguments);
window_maximized = false;
- soft_fullscreen_enabled = false;
- canvas_size_adjustment_requested = false;
+ entering_fullscreen = false;
+ just_exited_fullscreen = false;
main_loop = NULL;
diff --git a/platform/javascript/os_javascript.h b/platform/javascript/os_javascript.h
index 503c92585b..79dac5940f 100644
--- a/platform/javascript/os_javascript.h
+++ b/platform/javascript/os_javascript.h
@@ -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>
@@ -44,8 +44,8 @@ class OS_JavaScript : public OS_Unix {
VideoMode video_mode;
Vector2 windowed_size;
bool window_maximized;
- bool soft_fullscreen_enabled;
- bool canvas_size_adjustment_requested;
+ bool entering_fullscreen;
+ bool just_exited_fullscreen;
InputDefault *input;
Ref<InputEventKey> deferred_key_event;
@@ -59,7 +59,6 @@ class OS_JavaScript : public OS_Unix {
int64_t sync_wait_time;
int64_t last_sync_check_time;
- static EM_BOOL browser_resize_callback(int p_event_type, const EmscriptenUiEvent *p_event, void *p_user_data);
static EM_BOOL fullscreen_change_callback(int p_event_type, const EmscriptenFullscreenChangeEvent *p_event, void *p_user_data);
static EM_BOOL keydown_callback(int p_event_type, const EmscriptenKeyboardEvent *p_event, void *p_user_data);
@@ -81,7 +80,11 @@ 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;
+
virtual void initialize_core();
virtual Error initialize(const VideoMode &p_desired, int p_video_driver, int p_audio_driver);
@@ -130,8 +133,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/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.