summaryrefslogtreecommitdiff
path: root/platform/javascript
diff options
context:
space:
mode:
Diffstat (limited to 'platform/javascript')
-rw-r--r--platform/javascript/SCsub37
-rw-r--r--platform/javascript/audio_driver_javascript.cpp79
-rw-r--r--platform/javascript/audio_driver_javascript.h16
-rw-r--r--platform/javascript/detect.py25
-rw-r--r--platform/javascript/engine.js366
-rw-r--r--platform/javascript/export/export.cpp30
-rw-r--r--platform/javascript/godot_shell.html347
-rw-r--r--platform/javascript/javascript_eval.cpp82
-rw-r--r--platform/javascript/javascript_main.cpp16
-rw-r--r--platform/javascript/os_javascript.cpp80
-rw-r--r--platform/javascript/os_javascript.h13
-rw-r--r--platform/javascript/power_javascript.cpp6
-rw-r--r--platform/javascript/power_javascript.h6
-rw-r--r--platform/javascript/pre_asmjs.js3
-rw-r--r--platform/javascript/pre_wasm.js3
15 files changed, 618 insertions, 491 deletions
diff --git a/platform/javascript/SCsub b/platform/javascript/SCsub
index b804863ee1..f01d9367d2 100644
--- a/platform/javascript/SCsub
+++ b/platform/javascript/SCsub
@@ -20,33 +20,32 @@ for x in javascript_files:
javascript_objects.append(env_javascript.Object(x))
env.Append(LINKFLAGS=["-s", "EXPORTED_FUNCTIONS=\"['_main','_audio_server_mix_function','_main_after_fs_sync','_send_notification']\""])
-env.Append(LINKFLAGS=["--shell-file", '"platform/javascript/godot_shell.html"'])
# output file name without file extension
basename = "godot" + env["PROGSUFFIX"]
target_dir = env.Dir("#bin")
-js_file = target_dir.File(basename + ".js")
-implicit_targets = [js_file]
zip_dir = target_dir.Dir('.javascript_zip')
-zip_files = env.InstallAs([zip_dir.File("godot.js"), zip_dir.File("godotfs.js")], [js_file, "#misc/dist/html_fs/godotfs.js"])
-
-if env['wasm'] == 'yes':
- wasm_file = target_dir.File(basename+'.wasm')
- implicit_targets.append(wasm_file)
- zip_files.append(InstallAs(zip_dir.File('godot.wasm'), wasm_file))
+zip_files = env.InstallAs(zip_dir.File('godot.html'), '#misc/dist/html/default.html')
+
+implicit_targets = []
+if env['wasm']:
+ wasm = target_dir.File(basename + '.wasm')
+ implicit_targets.append(wasm)
+ zip_files.append(InstallAs(zip_dir.File('godot.wasm'), wasm))
+ prejs = env.File('pre_wasm.js')
else:
- asmjs_files = [target_dir.File(basename+'.asm.js'), target_dir.File(basename+'.html.mem')]
- zip_files.append(InstallAs([zip_dir.File('godot.asm.js'), zip_dir.File('godot.mem')], asmjs_files))
+ asmjs_files = [target_dir.File(basename + '.asm.js'), target_dir.File(basename + '.js.mem')]
implicit_targets.extend(asmjs_files)
+ zip_files.append(InstallAs([zip_dir.File('godot.asm.js'), zip_dir.File('godot.mem')], asmjs_files))
+ prejs = env.File('pre_asmjs.js')
-# HTML file must be the first target in the list
-html_file = env.Program(["#bin/godot"] + implicit_targets, javascript_objects, PROGSUFFIX=env["PROGSUFFIX"]+".html")[0]
-Depends(html_file, "godot_shell.html")
+js = env.Program(['#bin/godot'] + implicit_targets, javascript_objects, PROGSUFFIX=env['PROGSUFFIX'] + '.js')[0];
+zip_files.append(InstallAs(zip_dir.File('godot.js'), js))
-# Emscripten hardcodes file names, so replace common base name with
-# placeholder while leaving extension; also change `.html.mem` to just `.mem`
-fixup_html = env.Substfile(html_file, SUBST_DICT=[(basename, '$$GODOT_BASE'), ('.html.mem', '.mem')], SUBSTFILESUFFIX='.fixup.html')
+postjs = env.File('engine.js')
+env.Depends(js, [prejs, postjs])
+env.Append(LINKFLAGS=['--pre-js', prejs.path])
+env.Append(LINKFLAGS=['--post-js', postjs.path])
-zip_files.append(InstallAs(zip_dir.File('godot.html'), fixup_html))
-Zip('#bin/godot', zip_files, ZIPSUFFIX=env['PROGSUFFIX']+env['ZIPSUFFIX'], ZIPROOT=zip_dir, ZIPCOMSTR="Archving $SOURCES as $TARGET")
+Zip('#bin/godot', zip_files, ZIPSUFFIX=env['PROGSUFFIX'] + env['ZIPSUFFIX'], ZIPROOT=zip_dir, ZIPCOMSTR="Archving $SOURCES as $TARGET")
diff --git a/platform/javascript/audio_driver_javascript.cpp b/platform/javascript/audio_driver_javascript.cpp
index 4c0e5fd966..cd3974669f 100644
--- a/platform/javascript/audio_driver_javascript.cpp
+++ b/platform/javascript/audio_driver_javascript.cpp
@@ -29,6 +29,7 @@
/*************************************************************************/
#include "audio_driver_javascript.h"
+#include <emscripten.h>
#include <string.h>
#define MAX_NUMBER_INTERFACES 3
@@ -38,22 +39,91 @@
//AudioDriverJavaScript* AudioDriverJavaScript::s_ad=NULL;
+AudioDriverJavaScript *AudioDriverJavaScript::singleton_js = NULL;
const char *AudioDriverJavaScript::get_name() const {
return "JavaScript";
}
+extern "C" {
+
+void js_audio_driver_mix_function(int p_frames) {
+
+ //print_line("MIXI! "+itos(p_frames));
+ AudioDriverJavaScript::singleton_js->mix_to_js(p_frames);
+}
+}
+
+void AudioDriverJavaScript::mix_to_js(int p_frames) {
+
+ int todo = p_frames;
+ int offset = 0;
+
+ while (todo) {
+
+ int tomix = MIN(todo, INTERNAL_BUFFER_SIZE);
+
+ audio_server_process(p_frames, stream_buffer);
+ for (int i = 0; i < tomix * internal_buffer_channels; i++) {
+ internal_buffer[i] = float(stream_buffer[i] >> 16) * 32768.0;
+ }
+
+ /* clang-format off */
+ EM_ASM_({
+ var data = HEAPF32.subarray($0 / 4, $0 / 4 + $2 * 2);
+
+ for (var channel = 0; channel < _as_output_buffer.numberOfChannels; channel++) {
+ var outputData = _as_output_buffer.getChannelData(channel);
+ // Loop through samples
+ for (var sample = 0; sample < $2; sample++) {
+ // make output equal to the same as the input
+ outputData[sample + $1] = data[sample * 2 + channel];
+ }
+ }
+ }, internal_buffer, offset, tomix);
+ /* clang-format on */
+
+ todo -= tomix;
+ offset += tomix;
+ }
+}
+
Error AudioDriverJavaScript::init() {
return OK;
}
void AudioDriverJavaScript::start() {
+
+ internal_buffer_channels = 2;
+ internal_buffer = memnew_arr(float, INTERNAL_BUFFER_SIZE *internal_buffer_channels);
+ stream_buffer = memnew_arr(int32_t, INTERNAL_BUFFER_SIZE * 4); //max 4 channels
+
+ /* clang-format off */
+ EM_ASM(
+ _as_audioctx = new (window.AudioContext || window.webkitAudioContext)();
+
+ audio_server_mix_function = Module.cwrap('js_audio_driver_mix_function', 'void', ['number']);
+ );
+
+ int buffer_latency = 16384;
+ EM_ASM_( {
+ _as_script_node = _as_audioctx.createScriptProcessor($0, 0, 2);
+ _as_script_node.connect(_as_audioctx.destination);
+ console.log(_as_script_node.bufferSize);
+
+ _as_script_node.onaudioprocess = function(audioProcessingEvent) {
+ // The output buffer contains the samples that will be modified and played
+ _as_output_buffer = audioProcessingEvent.outputBuffer;
+ audio_server_mix_function(_as_output_buffer.getChannelData(0).length);
+ }
+ }, buffer_latency);
+ /* clang-format on */
}
int AudioDriverJavaScript::get_mix_rate() const {
- return 44100;
+ return mix_rate;
}
AudioDriver::SpeakerMode AudioDriverJavaScript::get_speaker_mode() const {
@@ -63,7 +133,7 @@ AudioDriver::SpeakerMode AudioDriverJavaScript::get_speaker_mode() const {
void AudioDriverJavaScript::lock() {
- /*
+ /*no locking, as threads are not supported
if (active && mutex)
mutex->lock();
*/
@@ -71,7 +141,7 @@ void AudioDriverJavaScript::lock() {
void AudioDriverJavaScript::unlock() {
- /*
+ /*no locking, as threads are not supported
if (active && mutex)
mutex->unlock();
*/
@@ -81,4 +151,7 @@ void AudioDriverJavaScript::finish() {
}
AudioDriverJavaScript::AudioDriverJavaScript() {
+
+ mix_rate = 44100;
+ singleton_js = this;
}
diff --git a/platform/javascript/audio_driver_javascript.h b/platform/javascript/audio_driver_javascript.h
index c5cebe800f..c3adeca07b 100644
--- a/platform/javascript/audio_driver_javascript.h
+++ b/platform/javascript/audio_driver_javascript.h
@@ -35,7 +35,23 @@
#include "os/mutex.h"
class AudioDriverJavaScript : public AudioDriver {
+
+ enum {
+ INTERNAL_BUFFER_SIZE = 4096,
+ STREAM_SCALE_BITS = 12
+
+ };
+
+ int mix_rate;
+ float *internal_buffer;
+ int internal_buffer_channels;
+ int internal_buffer_size;
+ int32_t *stream_buffer;
+
public:
+ void mix_to_js(int p_frames);
+ static AudioDriverJavaScript *singleton_js;
+
virtual const char *get_name() const;
virtual Error init();
diff --git a/platform/javascript/detect.py b/platform/javascript/detect.py
index 5f066f1b15..a2988d9c60 100644
--- a/platform/javascript/detect.py
+++ b/platform/javascript/detect.py
@@ -13,22 +13,22 @@ def get_name():
def can_build():
- return ("EMSCRIPTEN_ROOT" in os.environ)
+ return ("EMSCRIPTEN_ROOT" in os.environ or "EMSCRIPTEN" in os.environ)
def get_opts():
-
+ from SCons.Variables import BoolVariable
return [
- ['wasm', 'Compile to WebAssembly', 'no'],
- ['javascript_eval', 'Enable JavaScript eval interface', 'yes'],
+ BoolVariable('wasm', 'Compile to WebAssembly', False),
+ BoolVariable('javascript_eval', 'Enable JavaScript eval interface', True),
]
def get_flags():
return [
- ('tools', 'no'),
- ('module_theora_enabled', 'no'),
+ ('tools', False),
+ ('module_theora_enabled', False),
]
@@ -66,7 +66,10 @@ def configure(env):
## Compiler configuration
env['ENV'] = os.environ
- env.PrependENVPath('PATH', os.environ['EMSCRIPTEN_ROOT'])
+ if ("EMSCRIPTEN_ROOT" in os.environ):
+ env.PrependENVPath('PATH', os.environ['EMSCRIPTEN_ROOT'])
+ elif ("EMSCRIPTEN" in os.environ):
+ env.PrependENVPath('PATH', os.environ['EMSCRIPTEN'])
env['CC'] = 'emcc'
env['CXX'] = 'em++'
env['LINK'] = 'emcc'
@@ -95,14 +98,15 @@ def configure(env):
# These flags help keep the file size down
env.Append(CPPFLAGS=["-fno-exceptions", '-DNO_SAFE_CAST', '-fno-rtti'])
- if env['javascript_eval'] == 'yes':
+ if env['javascript_eval']:
env.Append(CPPFLAGS=['-DJAVASCRIPT_EVAL_ENABLED'])
## Link flags
+ env.Append(LINKFLAGS=['-s', 'EXTRA_EXPORTED_RUNTIME_METHODS="[\'FS\']"'])
env.Append(LINKFLAGS=['-s', 'USE_WEBGL2=1'])
- if (env['wasm'] == 'yes'):
+ if env['wasm']:
env.Append(LINKFLAGS=['-s', 'BINARYEN=1'])
# In contrast to asm.js, enabling memory growth on WebAssembly has no
# major performance impact, and causes only a negligible increase in
@@ -112,7 +116,8 @@ def configure(env):
else:
env.Append(LINKFLAGS=['-s', 'ASM_JS=1'])
env.Append(LINKFLAGS=['--separate-asm'])
+ env.Append(LINKFLAGS=['--memory-init-file', '1'])
# TODO: Move that to opus module's config
- if("module_opus_enabled" in env and env["module_opus_enabled"] != "no"):
+ if 'module_opus_enabled' in env and env['module_opus_enabled']:
env.opus_fixed_point = "yes"
diff --git a/platform/javascript/engine.js b/platform/javascript/engine.js
new file mode 100644
index 0000000000..99d1c20bbd
--- /dev/null
+++ b/platform/javascript/engine.js
@@ -0,0 +1,366 @@
+ return Module;
+ },
+};
+
+(function() {
+ var engine = Engine;
+
+ var USING_WASM = engine.USING_WASM;
+ var DOWNLOAD_ATTEMPTS_MAX = 4;
+
+ var basePath = null;
+ var engineLoadPromise = null;
+
+ var loadingFiles = {};
+
+ function getBasePath(path) {
+
+ if (path.endsWith('/'))
+ path = path.slice(0, -1);
+ if (path.lastIndexOf('.') > path.lastIndexOf('/'))
+ path = path.slice(0, path.lastIndexOf('.'));
+ return path;
+ }
+
+ function getBaseName(path) {
+
+ path = getBasePath(path);
+ return path.slice(path.lastIndexOf('/') + 1);
+ }
+
+ Engine = function Engine() {
+
+ this.rtenv = null;
+
+ var gameInitPromise = null;
+ var unloadAfterInit = true;
+ var memorySize = 268435456;
+
+ var progressFunc = null;
+ var pckProgressTracker = {};
+ var lastProgress = { loaded: 0, total: 0 };
+
+ var canvas = null;
+ var stdout = null;
+ var stderr = null;
+
+ this.initGame = function(mainPack) {
+
+ if (!gameInitPromise) {
+
+ if (mainPack === undefined) {
+ if (basePath !== null) {
+ mainPack = basePath + '.pck';
+ } else {
+ return Promise.reject(new Error("No main pack to load specified"));
+ }
+ }
+ if (basePath === null)
+ basePath = getBasePath(mainPack);
+
+ gameInitPromise = Engine.initEngine().then(
+ instantiate.bind(this)
+ );
+ var gameLoadPromise = loadPromise(mainPack, pckProgressTracker).then(function(xhr) { return xhr.response; });
+ gameInitPromise = Promise.all([gameLoadPromise, gameInitPromise]).then(function(values) {
+ // resolve with pck
+ return new Uint8Array(values[0]);
+ });
+ if (unloadAfterInit)
+ gameInitPromise.then(Engine.unloadEngine);
+ requestAnimationFrame(animateProgress);
+ }
+ return gameInitPromise;
+ };
+
+ function instantiate(initializer) {
+
+ var rtenvOpts = {
+ noInitialRun: true,
+ thisProgram: getBaseName(basePath),
+ engine: this,
+ };
+ if (typeof stdout === 'function')
+ rtenvOpts.print = stdout;
+ if (typeof stderr === 'function')
+ rtenvOpts.printErr = stderr;
+ if (typeof WebAssembly === 'object' && initializer instanceof ArrayBuffer) {
+ rtenvOpts.instantiateWasm = function(imports, onSuccess) {
+ WebAssembly.instantiate(initializer, imports).then(function(result) {
+ onSuccess(result.instance);
+ });
+ return {};
+ };
+ } else if (initializer.asm && initializer.mem) {
+ rtenvOpts.asm = initializer.asm;
+ rtenvOpts.memoryInitializerRequest = initializer.mem;
+ rtenvOpts.TOTAL_MEMORY = memorySize;
+ } else {
+ throw new Error("Invalid initializer");
+ }
+
+ return new Promise(function(resolve, reject) {
+ rtenvOpts.onRuntimeInitialized = resolve;
+ rtenvOpts.onAbort = reject;
+ rtenvOpts.engine.rtenv = Engine.RuntimeEnvironment(rtenvOpts);
+ });
+ }
+
+ this.start = function(mainPack) {
+
+ return this.initGame(mainPack).then(synchronousStart.bind(this));
+ };
+
+ function synchronousStart(pckView) {
+ // TODO don't expect canvas when runninng as cli tool
+ if (canvas instanceof HTMLCanvasElement) {
+ this.rtenv.canvas = canvas;
+ } else {
+ var firstCanvas = document.getElementsByTagName('canvas')[0];
+ if (firstCanvas instanceof HTMLCanvasElement) {
+ this.rtenv.canvas = firstCanvas;
+ } else {
+ throw new Error("No canvas found");
+ }
+ }
+
+ var actualCanvas = this.rtenv.canvas;
+ var context = false;
+ try {
+ context = actualCanvas.getContext('webgl2') || actualCanvas.getContext('experimental-webgl2');
+ } catch (e) {}
+ if (!context) {
+ throw new Error("WebGL 2 not available");
+ }
+
+ // canvas can grab focus on click
+ if (actualCanvas.tabIndex < 0) {
+ actualCanvas.tabIndex = 0;
+ }
+ // necessary to calculate cursor coordinates correctly
+ actualCanvas.style.padding = 0;
+ actualCanvas.style.borderWidth = 0;
+ actualCanvas.style.borderStyle = 'none';
+ // until context restoration is implemented
+ actualCanvas.addEventListener('webglcontextlost', function(ev) {
+ alert("WebGL context lost, please reload the page");
+ ev.preventDefault();
+ }, false);
+
+ this.rtenv.FS.createDataFile('/', this.rtenv.thisProgram + '.pck', pckView, true, true, true);
+ gameInitPromise = null;
+ this.rtenv.callMain();
+ }
+
+ this.setProgressFunc = function(func) {
+ progressFunc = func;
+ };
+
+ function animateProgress() {
+
+ var loaded = 0;
+ var total = 0;
+ var totalIsValid = true;
+ var progressIsFinal = true;
+
+ [loadingFiles, pckProgressTracker].forEach(function(tracker) {
+ Object.keys(tracker).forEach(function(file) {
+ if (!tracker[file].final)
+ progressIsFinal = false;
+ if (!totalIsValid || tracker[file].total === 0) {
+ totalIsValid = false;
+ total = 0;
+ } else {
+ total += tracker[file].total;
+ }
+ loaded += tracker[file].loaded;
+ });
+ });
+ if (loaded !== lastProgress.loaded || total !== lastProgress.total) {
+ lastProgress.loaded = loaded;
+ lastProgress.total = total;
+ if (typeof progressFunc === 'function')
+ progressFunc(loaded, total);
+ }
+ if (!progressIsFinal)
+ requestAnimationFrame(animateProgress);
+ }
+
+ this.setCanvas = function(elem) {
+ canvas = elem;
+ };
+
+ this.setAsmjsMemorySize = function(size) {
+ memorySize = size;
+ };
+
+ this.setUnloadAfterInit = function(enabled) {
+
+ if (enabled && !unloadAfterInit && gameInitPromise) {
+ gameInitPromise.then(Engine.unloadEngine);
+ }
+ unloadAfterInit = enabled;
+ };
+
+ this.setStdoutFunc = function(func) {
+
+ var print = function(text) {
+ if (arguments.length > 1) {
+ text = Array.prototype.slice.call(arguments).join(" ");
+ }
+ func(text);
+ };
+ if (this.rtenv)
+ this.rtenv.print = print;
+ stdout = print;
+ };
+
+ this.setStderrFunc = function(func) {
+
+ var printErr = function(text) {
+ if (arguments.length > 1)
+ text = Array.prototype.slice.call(arguments).join(" ");
+ func(text);
+ };
+ if (this.rtenv)
+ this.rtenv.printErr = printErr;
+ stderr = printErr;
+ };
+
+
+ }; // Engine()
+
+ Engine.RuntimeEnvironment = engine.RuntimeEnvironment;
+
+ Engine.initEngine = function(newBasePath) {
+
+ if (newBasePath !== undefined) basePath = getBasePath(newBasePath);
+ if (engineLoadPromise === null) {
+ if (USING_WASM) {
+ if (typeof WebAssembly !== 'object')
+ return Promise.reject(new Error("Browser doesn't support WebAssembly"));
+ // TODO cache/retrieve module to/from idb
+ engineLoadPromise = loadPromise(basePath + '.wasm').then(function(xhr) {
+ return xhr.response;
+ });
+ } else {
+ var asmjsPromise = loadPromise(basePath + '.asm.js').then(function(xhr) {
+ return asmjsModulePromise(xhr.response);
+ });
+ var memPromise = loadPromise(basePath + '.mem');
+ engineLoadPromise = Promise.all([asmjsPromise, memPromise]).then(function(values) {
+ return { asm: values[0], mem: values[1] };
+ });
+ }
+ engineLoadPromise = engineLoadPromise.catch(function(err) {
+ engineLoadPromise = null;
+ throw err;
+ });
+ }
+ return engineLoadPromise;
+ };
+
+ function asmjsModulePromise(module) {
+ var elem = document.createElement('script');
+ var script = new Blob([
+ 'Engine.asm = (function() { var Module = {};',
+ module,
+ 'return Module.asm; })();'
+ ]);
+ var url = URL.createObjectURL(script);
+ elem.src = url;
+ return new Promise(function(resolve, reject) {
+ elem.addEventListener('load', function() {
+ URL.revokeObjectURL(url);
+ var asm = Engine.asm;
+ Engine.asm = undefined;
+ setTimeout(function() {
+ // delay to reclaim compilation memory
+ resolve(asm);
+ }, 1);
+ });
+ elem.addEventListener('error', function() {
+ URL.revokeObjectURL(url);
+ reject("asm.js faiilure");
+ });
+ document.body.appendChild(elem);
+ });
+ }
+
+ Engine.unloadEngine = function() {
+ engineLoadPromise = null;
+ };
+
+ function loadPromise(file, tracker) {
+ if (tracker === undefined)
+ tracker = loadingFiles;
+ return new Promise(function(resolve, reject) {
+ loadXHR(resolve, reject, file, tracker);
+ });
+ }
+
+ function loadXHR(resolve, reject, file, tracker) {
+
+ var xhr = new XMLHttpRequest;
+ xhr.open('GET', file);
+ if (!file.endsWith('.js')) {
+ xhr.responseType = 'arraybuffer';
+ }
+ ['loadstart', 'progress', 'load', 'error', 'timeout', 'abort'].forEach(function(ev) {
+ xhr.addEventListener(ev, onXHREvent.bind(xhr, resolve, reject, file, tracker));
+ });
+ xhr.send();
+ }
+
+ function onXHREvent(resolve, reject, file, tracker, ev) {
+
+ if (this.status >= 400) {
+
+ if (this.status < 500 || ++tracker[file].attempts >= DOWNLOAD_ATTEMPTS_MAX) {
+ reject(new Error("Failed loading file '" + file + "': " + this.statusText));
+ this.abort();
+ return;
+ } else {
+ loadXHR(resolve, reject, file);
+ }
+ }
+
+ switch (ev.type) {
+ case 'loadstart':
+ if (tracker[file] === undefined) {
+ tracker[file] = {
+ total: ev.total,
+ loaded: ev.loaded,
+ attempts: 0,
+ final: false,
+ };
+ }
+ break;
+
+ case 'progress':
+ tracker[file].loaded = ev.loaded;
+ tracker[file].total = ev.total;
+ break;
+
+ case 'load':
+ tracker[file].final = true;
+ resolve(this);
+ break;
+
+ case 'error':
+ case 'timeout':
+ if (++tracker[file].attempts >= DOWNLOAD_ATTEMPTS_MAX) {
+ tracker[file].final = true;
+ reject(new Error("Failed loading file '" + file + "'"));
+ } else {
+ loadXHR(resolve, reject, file);
+ }
+ break;
+
+ case 'abort':
+ tracker[file].final = true;
+ reject(new Error("Loading file '" + file + "' was aborted."));
+ break;
+ }
+ }
+})();
diff --git a/platform/javascript/export/export.cpp b/platform/javascript/export/export.cpp
index 5a161dd0ab..4a97bf4c32 100644
--- a/platform/javascript/export/export.cpp
+++ b/platform/javascript/export/export.cpp
@@ -100,8 +100,8 @@ void EditorExportPlatformJavaScript::_fix_html(Vector<uint8_t> &p_html, const Re
for (int i = 0; i < lines.size(); i++) {
String current_line = lines[i];
- current_line = current_line.replace("$GODOT_TMEM", itos(memory_mb * 1024 * 1024));
- current_line = current_line.replace("$GODOT_BASE", p_name);
+ current_line = current_line.replace("$GODOT_TOTAL_MEMORY", itos(memory_mb * 1024 * 1024));
+ current_line = current_line.replace("$GODOT_BASENAME", p_name);
current_line = current_line.replace("$GODOT_HEAD_INCLUDE", p_preset->get("html/head_include"));
current_line = current_line.replace("$GODOT_DEBUG_ENABLED", p_debug ? "true" : "false");
str_export += current_line + "\n";
@@ -114,28 +114,6 @@ void EditorExportPlatformJavaScript::_fix_html(Vector<uint8_t> &p_html, const Re
}
}
-void EditorExportPlatformJavaScript::_fix_fsloader_js(Vector<uint8_t> &p_js, const String &p_pack_name, uint64_t p_pack_size) {
-
- String str_template = String::utf8(reinterpret_cast<const char *>(p_js.ptr()), p_js.size());
- String str_export;
- Vector<String> lines = str_template.split("\n");
- for (int i = 0; i < lines.size(); i++) {
- if (lines[i].find("$GODOT_PACK_NAME") != -1) {
- str_export += lines[i].replace("$GODOT_PACK_NAME", p_pack_name);
- } else if (lines[i].find("$GODOT_PACK_SIZE") != -1) {
- str_export += lines[i].replace("$GODOT_PACK_SIZE", itos(p_pack_size));
- } else {
- str_export += lines[i] + "\n";
- }
- }
-
- CharString cs = str_export.utf8();
- p_js.resize(cs.length());
- for (int i = 0; i < cs.length(); i++) {
- p_js[i] = cs[i];
- }
-}
-
void EditorExportPlatformJavaScript::get_preset_features(const Ref<EditorExportPreset> &p_preset, List<String> *r_features) {
if (p_preset->get("texture_format/s3tc")) {
@@ -286,10 +264,6 @@ Error EditorExportPlatformJavaScript::export_project(const Ref<EditorExportPrese
_fix_html(data, p_preset, p_path.get_file().get_basename(), p_debug);
file = p_path.get_file();
- } else if (file == "godotfs.js") {
-
- _fix_fsloader_js(data, pck_path.get_file(), pack_size);
- file = p_path.get_file().get_basename() + "fs.js";
} else if (file == "godot.js") {
file = p_path.get_file().get_basename() + ".js";
diff --git a/platform/javascript/godot_shell.html b/platform/javascript/godot_shell.html
deleted file mode 100644
index ee7399a129..0000000000
--- a/platform/javascript/godot_shell.html
+++ /dev/null
@@ -1,347 +0,0 @@
-<!DOCTYPE html>
-<html xmlns="http://www.w3.org/1999/xhtml" lang="" xml:lang="">
-<head>
- <meta charset="utf-8" />
- <title></title>
- <style type="text/css">
- body {
- margin: 0;
- border: 0 none;
- padding: 0;
- text-align: center;
- background-color: #222226;
- font-family: 'Droid Sans', Arial, sans-serif;
- }
-
-
- /* Godot Engine default theme style
- * ================================ */
-
- .godot {
- color: #e0e0e0;
- background-color: #3b3943;
- background-image: linear-gradient(to bottom, #403e48, #35333c);
- border: 1px solid #45434e;
- box-shadow: 0 0 1px 1px #2f2d35;
- }
-
- button.godot {
- font-family: 'Droid Sans', Arial, sans-serif; /* override user agent style */
- padding: 1px 5px;
- background-color: #37353f;
- background-image: linear-gradient(to bottom, #413e49, #3a3842);
- border: 1px solid #514f5d;
- border-radius: 1px;
- box-shadow: 0 0 1px 1px #2a2930;
- }
-
- button.godot:hover {
- color: #f0f0f0;
- background-color: #44414e;
- background-image: linear-gradient(to bottom, #494652, #423f4c);
- border: 1px solid #5a5667;
- box-shadow: 0 0 1px 1px #26252b;
- }
-
- button.godot:active {
- color: #fff;
- background-color: #3e3b46;
- background-image: linear-gradient(to bottom, #36343d, #413e49);
- border: 1px solid #4f4c59;
- box-shadow: 0 0 1px 1px #26252b;
- }
-
- button.godot:disabled {
- color: rgba(230, 230, 230, 0.2);
- background-color: #3d3d3d;
- background-image: linear-gradient(to bottom, #434343, #393939);
- border: 1px solid #474747;
- box-shadow: 0 0 1px 1px #2d2b33;
- }
-
-
- /* Canvas / wrapper
- * ================ */
-
- #container {
- display: inline-block; /* scale with canvas */
- vertical-align: top; /* prevent extra height */
- position: relative; /* root for absolutely positioned overlay */
- margin: 0;
- border: 0 none;
- padding: 0;
- background-color: #0c0c0c;
- }
-
- #canvas {
- display: block;
- margin: 0 auto;
- /* canvas must have border and padding set to zero to
- * calculate cursor coordinates correctly */
- border: 0 none;
- padding: 0;
- color: white;
- }
-
- #canvas:focus {
- outline: none;
- }
-
-
- /* Status display
- * ============== */
-
- #status-container {
- position: absolute;
- left: 0;
- top: 0;
- right: 0;
- bottom: 0;
- display: flex;
- justify-content: center;
- align-items: center;
- /* don't consume click events - make children visible explicitly */
- visibility: hidden;
- }
-
- #status {
- line-height: 1.3;
- cursor: pointer;
- visibility: visible;
- padding: 4px 6px;
- }
-
-
- /* Debug output
- * ============ */
-
- #output-panel {
- display: none;
- max-width: 700px;
- font-size: small;
- margin: 6px auto 0;
- padding: 0 4px 4px;
- text-align: left;
- line-height: 2.2;
- }
-
- #output-header {
- display: flex;
- justify-content: space-between;
- align-items: center;
- }
-
- #output-container {
- padding: 6px;
- background-color: #2c2a32;
- box-shadow: inset 0 0 1px 1px #232127;
- color: #bbb;
- }
-
- #output-scroll {
- line-height: 1;
- height: 12em;
- overflow-y: scroll;
- white-space: pre-wrap;
- font-size: small;
- font-family: "Lucida Console", Monaco, monospace;
- }
- </style>
-$GODOT_HEAD_INCLUDE
-</head>
-<body>
- <div id="container">
- <canvas id="canvas" width="640" height="480" tabindex="0" oncontextmenu="event.preventDefault();">
- HTML5 canvas appears to be unsupported in the current browser.<br />
- Please try updating or use a different browser.
- </canvas>
- <div id="status-container">
- <span id="status" class="godot" onclick="this.style.visibility='hidden';">Downloading page...</span>
- </div>
- </div>
- <div id="output-panel" class="godot">
- <div id="output-header">
- Output:
- <button class="godot" type="button" autocomplete="off" onclick="Presentation.clearOutput();">Clear</button>
- </div>
- <div id="output-container"><div id="output-scroll"></div></div>
- </div>
-
- <!-- Scripts -->
- <script type="text/javascript">//<![CDATA[
- var Presentation = (function() {
- var statusElement = document.getElementById("status");
- var canvasElement = document.getElementById("canvas");
-
- var presentation = {
- postRun: [],
- setStatusVisible: function setStatusVisible(visible) {
- statusElement.style.visibility = (visible ? "visible" : "hidden");
- },
- setStatus: function setStatus(text) {
- if (text.length === 0) {
- // emscripten sets empty string as status after "Running..."
- // per timeout, but another status may have been set by then
- if (Presentation.setStatus.lastText === "Running...")
- Presentation.setStatusVisible(false);
- return;
- }
- Presentation.setStatus.lastText = text;
- while (statusElement.lastChild) {
- statusElement.removeChild(statusElement.lastChild);
- }
- var lines = text.split("\n");
- lines.forEach(function(line, index) {
- statusElement.appendChild(document.createTextNode(line));
- statusElement.appendChild(document.createElement("br"));
- });
- var closeNote = document.createElement("span");
- closeNote.style.fontSize = "small";
- closeNote.textContent = "click to close";
- statusElement.appendChild(closeNote);
- Presentation.setStatusVisible(true);
- },
- isWebGL2Available: function isWebGL2Available() {
- var context;
- try {
- context = canvasElement.getContext("webgl2") || canvasElement.getContext("experimental-webgl2");
- } catch (e) {}
- return !!context;
- },
- };
-
- window.onerror = function(event) { presentation.setStatus("Failure during start-up\nSee JavaScript console") };
-
- if ($GODOT_DEBUG_ENABLED) { // debugging enabled
- var outputRoot = document.getElementById("output-panel");
- var outputElement = document.getElementById("output-scroll");
- const maxOutputMessages = 400;
-
- presentation.setOutputVisible = function setOutputVisible(visible) {
- outputRoot.style.display = (visible ? "block" : "none");
- };
- presentation.clearOutput = function clearOutput() {
- while (outputElement.firstChild) {
- outputElement.firstChild.remove();
- }
- };
-
- presentation.setOutputVisible(true);
-
- presentation.print = function print(text) {
- if (arguments.length > 1) {
- text = Array.prototype.slice.call(arguments).join(" ");
- }
- if (text.length <= 0) return;
- while (outputElement.childElementCount >= maxOutputMessages) {
- outputElement.firstChild.remove();
- }
- var msg = document.createElement("div");
- if (String.prototype.trim.call(text).startsWith("**ERROR**")
- || String.prototype.trim.call(text).startsWith("**EXCEPTION**")) {
- msg.style.color = "#d44";
- } else if (String.prototype.trim.call(text).startsWith("**WARNING**")) {
- msg.style.color = "#ccc000";
- } else if (String.prototype.trim.call(text).startsWith("**SCRIPT ERROR**")) {
- msg.style.color = "#c6d";
- }
- msg.textContent = text;
- var scrollToBottom = outputElement.scrollHeight - (outputElement.clientHeight + outputElement.scrollTop) < 10;
- outputElement.appendChild(msg);
- if (scrollToBottom) {
- outputElement.scrollTop = outputElement.scrollHeight;
- }
- };
-
- presentation.postRun.push(function() {
- window.onerror = function(event) { presentation.print("**EXCEPTION**:", event) };
- });
-
- } else {
- presentation.postRun.push(function() { window.onerror = null; });
- }
-
- return presentation;
- })();
-
- // Emscripten interface
- var Module = (function() {
- const BASE_NAME = '$GODOT_BASE';
- var module = {
- thisProgram: BASE_NAME,
- wasmBinaryFile: BASE_NAME + '.wasm',
- TOTAL_MEMORY: $GODOT_TMEM,
- print: function print(text) {
- if (arguments.length > 1) {
- text = Array.prototype.slice.call(arguments).join(" ");
- }
- console.log(text);
- if (typeof Presentation !== "undefined" && typeof Presentation.print === "function") {
- Presentation.print(text);
- }
- },
- printErr: function printErr(text) {
- if (arguments.length > 1) {
- text = Array.prototype.slice.call(arguments).join(" ");
- }
- console.error(text);
- if (typeof Presentation !== "undefined" && typeof Presentation.print === "function") {
- Presentation.print("**ERROR**:", text)
- }
- },
- canvas: document.getElementById("canvas"),
- setStatus: function setStatus(text) {
- var m = text.match(/([^(]+)\((\d+(\.\d+)?)\/(\d+)\)/);
- var now = Date.now();
- if (m) {
- if (now - Date.now() < 30) // if this is a progress update, skip it if too soon
- return;
- text = m[1];
- }
- if (typeof Presentation !== "undefined" && typeof Presentation.setStatus == "function") {
- Presentation.setStatus(text);
- }
- }
- };
-
- // As a default initial behavior, pop up an alert when WebGL context is lost. To make your
- // application robust, you may want to override this behavior before shipping!
- // See http://www.khronos.org/registry/webgl/specs/latest/1.0/#5.15.2
- module.canvas.addEventListener("webglcontextlost", function(e) { alert("WebGL context lost. Plase reload the page."); e.preventDefault(); }, false);
-
- if (typeof Presentation !== "undefined" && Presentation.postRun instanceof Array) {
- module.postRun = Presentation.postRun;
- }
-
- return module;
- })();
-
- if (!Presentation.isWebGL2Available()) {
- Presentation.setStatus("WebGL 2 appears to be unsupported.\nPlease update browser and drivers.");
- Presentation.preventLoading = true;
- } else {
- Presentation.setStatus("Downloading...");
- }
-
- if (Presentation.preventLoading) {
- // prevent *fs.js and Emscripten's SCRIPT placeholder from loading any files
- Presentation._XHR_send = XMLHttpRequest.prototype.send;
- XMLHttpRequest.prototype.send = function() {};
- Presentation._Node_appendChild = Node.prototype.appendChild;
- Node.prototype.appendChild = function(node) {
- if (!(node instanceof HTMLScriptElement)) {
- return Presentation._Node_appendChild.call(this, node);
- }
- }
- }
- //]]></script>
- <script type="text/javascript" src="$GODOT_BASEfs.js"></script>
-{{{ SCRIPT }}}
- <script type="text/javascript">
- if (Presentation.preventLoading) {
- XMLHttpRequest.prototype.send = Presentation._XHR_send;
- Node.prototype.appendChild = Presentation._Node_appendChild;
- }
- </script>
-</body>
-</html>
diff --git a/platform/javascript/javascript_eval.cpp b/platform/javascript/javascript_eval.cpp
index 74f8d80a76..1d737879f6 100644
--- a/platform/javascript/javascript_eval.cpp
+++ b/platform/javascript/javascript_eval.cpp
@@ -39,24 +39,41 @@ JavaScript *JavaScript::get_singleton() {
return singleton;
}
+extern "C" EMSCRIPTEN_KEEPALIVE uint8_t *resize_poolbytearray_and_open_write(PoolByteArray *p_arr, PoolByteArray::Write *r_write, int p_len) {
+
+ p_arr->resize(p_len);
+ *r_write = p_arr->write();
+ return r_write->ptr();
+}
+
Variant JavaScript::eval(const String &p_code, bool p_use_global_exec_context) {
union {
- int i;
+ bool b;
double d;
char *s;
} js_data[4];
+
+ PoolByteArray arr;
+ PoolByteArray::Write arr_write;
+
/* clang-format off */
Variant::Type return_type = static_cast<Variant::Type>(EM_ASM_INT({
+ const CODE = $0;
+ const USE_GLOBAL_EXEC_CONTEXT = $1;
+ const PTR = $2;
+ const ELEM_LEN = $3;
+ const BYTEARRAY_PTR = $4;
+ const BYTEARRAY_WRITE_PTR = $5;
var eval_ret;
try {
- if ($3) { // p_use_global_exec_context
+ if (USE_GLOBAL_EXEC_CONTEXT) {
// indirect eval call grants global execution context
var global_eval = eval;
- eval_ret = global_eval(UTF8ToString($2));
+ eval_ret = global_eval(UTF8ToString(CODE));
} else {
- eval_ret = eval(UTF8ToString($2));
+ eval_ret = eval(UTF8ToString(CODE));
}
} catch (e) {
Module.printErr(e);
@@ -66,16 +83,11 @@ Variant JavaScript::eval(const String &p_code, bool p_use_global_exec_context) {
switch (typeof eval_ret) {
case 'boolean':
- // bitwise op yields 32-bit int
- setValue($0, eval_ret|0, 'i32');
+ setValue(PTR, eval_ret, 'i32');
return 1; // BOOL
case 'number':
- if ((eval_ret|0)===eval_ret) {
- setValue($0, eval_ret|0, 'i32');
- return 2; // INT
- }
- setValue($0, eval_ret, 'double');
+ setValue(PTR, eval_ret, 'double');
return 3; // REAL
case 'string':
@@ -85,7 +97,7 @@ Variant JavaScript::eval(const String &p_code, bool p_use_global_exec_context) {
if (array_ptr===0) {
throw new Error('String allocation failed (probably out of memory)');
}
- setValue($0, array_ptr|0 , '*');
+ setValue(PTR, array_ptr , '*');
stringToUTF8(eval_ret, array_ptr, array_len);
return 4; // STRING
} catch (e) {
@@ -102,41 +114,50 @@ Variant JavaScript::eval(const String &p_code, bool p_use_global_exec_context) {
break;
}
- else if (typeof eval_ret.x==='number' && typeof eval_ret.y==='number') {
- setValue($0, eval_ret.x, 'double');
- setValue($0+$1, eval_ret.y, 'double');
+ if (ArrayBuffer.isView(eval_ret) && !(eval_ret instanceof Uint8Array)) {
+ eval_ret = new Uint8Array(eval_ret.buffer);
+ }
+ else if (eval_ret instanceof ArrayBuffer) {
+ eval_ret = new Uint8Array(eval_ret);
+ }
+ if (eval_ret instanceof Uint8Array) {
+ var bytes_ptr = ccall('resize_poolbytearray_and_open_write', 'number', ['number', 'number' ,'number'], [BYTEARRAY_PTR, BYTEARRAY_WRITE_PTR, eval_ret.length]);
+ HEAPU8.set(eval_ret, bytes_ptr);
+ return 20; // POOL_BYTE_ARRAY
+ }
+
+ if (typeof eval_ret.x==='number' && typeof eval_ret.y==='number') {
+ setValue(PTR, eval_ret.x, 'double');
+ setValue(PTR + ELEM_LEN, eval_ret.y, 'double');
if (typeof eval_ret.z==='number') {
- setValue($0+$1*2, eval_ret.z, 'double');
+ setValue(PTR + ELEM_LEN*2, eval_ret.z, 'double');
return 7; // VECTOR3
}
else if (typeof eval_ret.width==='number' && typeof eval_ret.height==='number') {
- setValue($0+$1*2, eval_ret.width, 'double');
- setValue($0+$1*3, eval_ret.height, 'double');
+ setValue(PTR + ELEM_LEN*2, eval_ret.width, 'double');
+ setValue(PTR + ELEM_LEN*3, eval_ret.height, 'double');
return 6; // RECT2
}
return 5; // VECTOR2
}
- else if (typeof eval_ret.r==='number' && typeof eval_ret.g==='number' && typeof eval_ret.b==='number') {
- // assume 8-bit rgb components since we're on the web
- setValue($0, eval_ret.r, 'double');
- setValue($0+$1, eval_ret.g, 'double');
- setValue($0+$1*2, eval_ret.b, 'double');
- setValue($0+$1*3, typeof eval_ret.a==='number' ? eval_ret.a : 1, 'double');
+ if (typeof eval_ret.r === 'number' && typeof eval_ret.g === 'number' && typeof eval_ret.b === 'number') {
+ setValue(PTR, eval_ret.r, 'double');
+ setValue(PTR + ELEM_LEN, eval_ret.g, 'double');
+ setValue(PTR + ELEM_LEN*2, eval_ret.b, 'double');
+ setValue(PTR + ELEM_LEN*3, typeof eval_ret.a === 'number' ? eval_ret.a : 1, 'double');
return 14; // COLOR
}
break;
}
return 0; // NIL
- }, js_data, sizeof *js_data, p_code.utf8().get_data(), p_use_global_exec_context));
+ }, p_code.utf8().get_data(), p_use_global_exec_context, js_data, sizeof *js_data, &arr, &arr_write));
/* clang-format on */
switch (return_type) {
case Variant::BOOL:
- return !!js_data->i;
- case Variant::INT:
- return js_data->i;
+ return js_data->b;
case Variant::REAL:
return js_data->d;
case Variant::STRING: {
@@ -153,7 +174,10 @@ Variant JavaScript::eval(const String &p_code, bool p_use_global_exec_context) {
case Variant::RECT2:
return Rect2(js_data[0].d, js_data[1].d, js_data[2].d, js_data[3].d);
case Variant::COLOR:
- return Color(js_data[0].d / 255., js_data[1].d / 255., js_data[2].d / 255., js_data[3].d);
+ return Color(js_data[0].d, js_data[1].d, js_data[2].d, js_data[3].d);
+ case Variant::POOL_BYTE_ARRAY:
+ arr_write = PoolByteArray::Write();
+ return arr;
}
return Variant();
}
diff --git a/platform/javascript/javascript_main.cpp b/platform/javascript/javascript_main.cpp
index 4c948bf181..ed4f416cfd 100644
--- a/platform/javascript/javascript_main.cpp
+++ b/platform/javascript/javascript_main.cpp
@@ -39,8 +39,13 @@ static void main_loop() {
os->main_loop_iterate();
}
-extern "C" void main_after_fs_sync() {
+extern "C" void main_after_fs_sync(char *p_idbfs_err) {
+ String idbfs_err = String::utf8(p_idbfs_err);
+ if (!idbfs_err.empty()) {
+ print_line("IndexedDB not available: " + idbfs_err);
+ }
+ os->set_idbfs_available(idbfs_err.empty());
// Ease up compatibility
ResourceLoader::set_abort_on_missing_resources(false);
Main::start();
@@ -60,14 +65,7 @@ int main(int argc, char *argv[]) {
FS.mkdir('/userfs');
FS.mount(IDBFS, {}, '/userfs');
FS.syncfs(true, function(err) {
- if (err) {
- Module.setStatus('Failed to load persistent data\nPlease allow (third-party) cookies');
- Module.printErr('Failed to populate IDB file system: ' + err.message);
- Module.noExitRuntime = false;
- } else {
- Module.print('Successfully populated IDB file system');
- ccall('main_after_fs_sync', null);
- }
+ Module['ccall']('main_after_fs_sync', null, ['string'], [err ? err.message : ""])
});
);
/* clang-format on */
diff --git a/platform/javascript/os_javascript.cpp b/platform/javascript/os_javascript.cpp
index a0e5610591..f6446e77da 100644
--- a/platform/javascript/os_javascript.cpp
+++ b/platform/javascript/os_javascript.cpp
@@ -85,6 +85,10 @@ void OS_JavaScript::initialize_core() {
FileAccess::make_default<FileAccessBufferedFA<FileAccessUnix> >(FileAccess::ACCESS_RESOURCES);
}
+void OS_JavaScript::initialize_logger() {
+ _set_logger(memnew(StdLogger));
+}
+
void OS_JavaScript::set_opengl_extensions(const char *p_gl_extensions) {
ERR_FAIL_COND(!p_gl_extensions);
@@ -172,14 +176,14 @@ static EM_BOOL _mousebutton_callback(int event_type, const EmscriptenMouseEvent
if (!is_canvas_focused()) {
focus_canvas();
}
- mask |= 1 << ev->get_button_index();
- } else if (mask & (1 << ev->get_button_index())) {
- mask &= ~(1 << ev->get_button_index());
+ mask |= ev->get_button_index();
+ } else if (mask & ev->get_button_index()) {
+ mask &= ~ev->get_button_index();
} else {
// release event, but press was outside the canvas, so ignore
return false;
}
- ev->set_button_mask(mask >> 1);
+ ev->set_button_mask(mask);
_input->parse_input_event(ev);
// prevent selection dragging
@@ -200,7 +204,7 @@ static EM_BOOL _mousemove_callback(int event_type, const EmscriptenMouseEvent *m
Ref<InputEventMouseMotion> ev;
ev.instance();
dom2godot_mod(mouse_event, ev);
- ev->set_button_mask(input_mask >> 1);
+ ev->set_button_mask(input_mask);
ev->set_position(pos);
ev->set_global_position(ev->get_position());
@@ -227,7 +231,7 @@ static EM_BOOL _wheel_callback(int event_type, const EmscriptenWheelEvent *wheel
Ref<InputEventMouseButton> ev;
ev.instance();
- ev->set_button_mask(_input->get_mouse_button_mask() >> 1);
+ ev->set_button_mask(_input->get_mouse_button_mask());
ev->set_position(_input->get_mouse_position());
ev->set_global_position(ev->get_position());
@@ -291,7 +295,7 @@ static EM_BOOL _touchpress_callback(int event_type, const EmscriptenTouchEvent *
Ref<InputEventMouseButton> ev_mouse;
ev_mouse.instance();
- ev_mouse->set_button_mask(_input->get_mouse_button_mask() >> 1);
+ ev_mouse->set_button_mask(_input->get_mouse_button_mask());
dom2godot_mod(touch_event, ev_mouse);
const EmscriptenTouchPoint &first_touch = touch_event->touches[lowest_id_index];
@@ -334,7 +338,7 @@ static EM_BOOL _touchmove_callback(int event_type, const EmscriptenTouchEvent *t
Ref<InputEventMouseMotion> ev_mouse;
ev_mouse.instance();
dom2godot_mod(touch_event, ev_mouse);
- ev_mouse->set_button_mask(_input->get_mouse_button_mask() >> 1);
+ ev_mouse->set_button_mask(_input->get_mouse_button_mask());
const EmscriptenTouchPoint &first_touch = touch_event->touches[lowest_id_index];
ev_mouse->set_position(Point2(first_touch.canvasX, first_touch.canvasY));
@@ -464,11 +468,7 @@ void OS_JavaScript::initialize(const VideoMode &p_desired, int p_video_driver, i
print_line("Init Audio");
AudioDriverManager::add_driver(&audio_driver_javascript);
- audio_driver_javascript.set_singleton();
- if (audio_driver_javascript.init() != OK) {
-
- ERR_PRINT("Initializing audio failed.");
- }
+ AudioDriverManager::initialize(p_audio_driver);
RasterizerGLES3::register_config();
RasterizerGLES3::make_current();
@@ -521,21 +521,6 @@ void OS_JavaScript::initialize(const VideoMode &p_desired, int p_video_driver, i
#undef SET_EM_CALLBACK
#undef EM_CHECK
- /* clang-format off */
- EM_ASM_ARGS({
- const send_notification = Module.cwrap('send_notification', null, ['number']);
- const notifs = arguments;
- (['mouseover', 'mouseleave', 'focus', 'blur']).forEach(function(event, i) {
- Module.canvas.addEventListener(event, send_notification.bind(this, notifs[i]));
- });
- },
- MainLoop::NOTIFICATION_WM_MOUSE_ENTER,
- MainLoop::NOTIFICATION_WM_MOUSE_EXIT,
- MainLoop::NOTIFICATION_WM_FOCUS_IN,
- MainLoop::NOTIFICATION_WM_FOCUS_OUT
- );
-/* clang-format on */
-
#ifdef JAVASCRIPT_EVAL_ENABLED
javascript_eval = memnew(JavaScript);
ProjectSettings::get_singleton()->add_singleton(ProjectSettings::Singleton("JavaScript", javascript_eval));
@@ -818,13 +803,29 @@ void OS_JavaScript::main_loop_begin() {
if (main_loop)
main_loop->init();
+
+ /* clang-format off */
+ EM_ASM_ARGS({
+ const send_notification = Module.cwrap('send_notification', null, ['number']);
+ const notifs = arguments;
+ (['mouseover', 'mouseleave', 'focus', 'blur']).forEach(function(event, i) {
+ Module.canvas.addEventListener(event, send_notification.bind(null, notifs[i]));
+ });
+ },
+ MainLoop::NOTIFICATION_WM_MOUSE_ENTER,
+ MainLoop::NOTIFICATION_WM_MOUSE_EXIT,
+ MainLoop::NOTIFICATION_WM_FOCUS_IN,
+ MainLoop::NOTIFICATION_WM_FOCUS_OUT
+ );
+ /* clang-format on */
}
+
bool OS_JavaScript::main_loop_iterate() {
if (!main_loop)
return false;
- if (time_to_save_sync >= 0) {
+ if (idbfs_available && time_to_save_sync >= 0) {
int64_t newtime = get_ticks_msec();
int64_t elapsed = newtime - last_sync_time;
last_sync_time = newtime;
@@ -914,10 +915,10 @@ String OS_JavaScript::get_executable_path() const {
void OS_JavaScript::_close_notification_funcs(const String &p_file, int p_flags) {
- print_line("close " + p_file + " flags " + itos(p_flags));
- if (p_file.begins_with("/userfs") && p_flags & FileAccess::WRITE) {
- static_cast<OS_JavaScript *>(get_singleton())->last_sync_time = OS::get_singleton()->get_ticks_msec();
- static_cast<OS_JavaScript *>(get_singleton())->time_to_save_sync = 5000; //five seconds since last save
+ OS_JavaScript *os = static_cast<OS_JavaScript *>(get_singleton());
+ if (os->idbfs_available && p_file.begins_with("/userfs") && p_flags & FileAccess::WRITE) {
+ os->last_sync_time = OS::get_singleton()->get_ticks_msec();
+ os->time_to_save_sync = 5000; //five seconds since last save
}
}
@@ -975,7 +976,7 @@ String OS_JavaScript::get_joy_guid(int p_device) const {
return input->get_joy_guid_remapped(p_device);
}
-PowerState OS_JavaScript::get_power_state() {
+OS::PowerState OS_JavaScript::get_power_state() {
return power_manager->get_power_state();
}
@@ -992,6 +993,16 @@ bool OS_JavaScript::_check_internal_feature_support(const String &p_feature) {
return p_feature == "web" || p_feature == "s3tc"; // TODO check for these features really being available
}
+void OS_JavaScript::set_idbfs_available(bool p_idbfs_available) {
+
+ idbfs_available = p_idbfs_available;
+}
+
+bool OS_JavaScript::is_userfs_persistent() const {
+
+ return idbfs_available;
+}
+
OS_JavaScript::OS_JavaScript(const char *p_execpath, GetDataDirFunc p_get_data_dir_func) {
set_cmdline(p_execpath, get_cmdline_args());
main_loop = NULL;
@@ -1003,6 +1014,7 @@ OS_JavaScript::OS_JavaScript(const char *p_execpath, GetDataDirFunc p_get_data_d
get_data_dir_func = p_get_data_dir_func;
FileAccessUnix::close_notification_func = _close_notification_funcs;
+ idbfs_available = false;
time_to_save_sync = -1;
}
diff --git a/platform/javascript/os_javascript.h b/platform/javascript/os_javascript.h
index f78a3f2768..1c939d3fd5 100644
--- a/platform/javascript/os_javascript.h
+++ b/platform/javascript/os_javascript.h
@@ -49,6 +49,7 @@ typedef String (*GetDataDirFunc)();
class OS_JavaScript : public OS_Unix {
+ bool idbfs_available;
int64_t time_to_save_sync;
int64_t last_sync_time;
@@ -92,6 +93,7 @@ public:
virtual int get_audio_driver_count() const;
virtual const char *get_audio_driver_name(int p_driver) const;
+ virtual void initialize_logger();
virtual void initialize_core();
virtual void initialize(const VideoMode &p_desired, int p_video_driver, int p_audio_driver);
@@ -104,11 +106,6 @@ public:
//static OS* get_singleton();
- virtual void print_error(const char *p_function, const char *p_file, int p_line, const char *p_code, const char *p_rationale, ErrorType p_type) {
-
- OS::print_error(p_function, p_file, p_line, p_code, p_rationale, p_type);
- }
-
virtual void alert(const String &p_alert, const String &p_title = "ALERT!");
virtual void set_mouse_mode(MouseMode p_mode);
@@ -140,6 +137,8 @@ public:
virtual bool can_draw() const;
+ virtual bool is_userfs_persistent() const;
+
virtual void set_cursor_shape(CursorShape p_shape);
void main_loop_begin();
@@ -165,12 +164,14 @@ public:
virtual String get_joy_guid(int p_device) const;
bool joy_connection_changed(int p_type, const EmscriptenGamepadEvent *p_event);
- virtual PowerState get_power_state();
+ virtual OS::PowerState get_power_state();
virtual int get_power_seconds_left();
virtual int get_power_percent_left();
virtual bool _check_internal_feature_support(const String &p_feature);
+ void set_idbfs_available(bool p_idbfs_available);
+
OS_JavaScript(const char *p_execpath, GetDataDirFunc p_get_data_dir_func);
~OS_JavaScript();
};
diff --git a/platform/javascript/power_javascript.cpp b/platform/javascript/power_javascript.cpp
index 3d54146595..10964502d4 100644
--- a/platform/javascript/power_javascript.cpp
+++ b/platform/javascript/power_javascript.cpp
@@ -36,12 +36,12 @@ bool PowerJavascript::UpdatePowerInfo() {
return false;
}
-PowerState PowerJavascript::get_power_state() {
+OS::PowerState PowerJavascript::get_power_state() {
if (UpdatePowerInfo()) {
return power_state;
} else {
WARN_PRINT("Power management is not implemented on this platform, defaulting to POWERSTATE_UNKNOWN");
- return POWERSTATE_UNKNOWN;
+ return OS::POWERSTATE_UNKNOWN;
}
}
@@ -64,7 +64,7 @@ int PowerJavascript::get_power_percent_left() {
}
PowerJavascript::PowerJavascript()
- : nsecs_left(-1), percent_left(-1), power_state(POWERSTATE_UNKNOWN) {
+ : nsecs_left(-1), percent_left(-1), power_state(OS::POWERSTATE_UNKNOWN) {
}
PowerJavascript::~PowerJavascript() {
diff --git a/platform/javascript/power_javascript.h b/platform/javascript/power_javascript.h
index 834d765557..8454c5d728 100644
--- a/platform/javascript/power_javascript.h
+++ b/platform/javascript/power_javascript.h
@@ -31,13 +31,13 @@
#ifndef PLATFORM_JAVASCRIPT_POWER_JAVASCRIPT_H_
#define PLATFORM_JAVASCRIPT_POWER_JAVASCRIPT_H_
-#include "os/power.h"
+#include "os/os.h"
class PowerJavascript {
private:
int nsecs_left;
int percent_left;
- PowerState power_state;
+ OS::PowerState power_state;
bool UpdatePowerInfo();
@@ -45,7 +45,7 @@ public:
PowerJavascript();
virtual ~PowerJavascript();
- PowerState get_power_state();
+ OS::PowerState get_power_state();
int get_power_seconds_left();
int get_power_percent_left();
};
diff --git a/platform/javascript/pre_asmjs.js b/platform/javascript/pre_asmjs.js
new file mode 100644
index 0000000000..3c497721b6
--- /dev/null
+++ b/platform/javascript/pre_asmjs.js
@@ -0,0 +1,3 @@
+var Engine = {
+ USING_WASM: false,
+ RuntimeEnvironment: function(Module) {
diff --git a/platform/javascript/pre_wasm.js b/platform/javascript/pre_wasm.js
new file mode 100644
index 0000000000..be4383c8c9
--- /dev/null
+++ b/platform/javascript/pre_wasm.js
@@ -0,0 +1,3 @@
+var Engine = {
+ USING_WASM: true,
+ RuntimeEnvironment: function(Module) {