summaryrefslogtreecommitdiff
path: root/platform/javascript/js
diff options
context:
space:
mode:
Diffstat (limited to 'platform/javascript/js')
-rw-r--r--platform/javascript/js/engine/config.js51
-rw-r--r--platform/javascript/js/engine/engine.js3
-rw-r--r--platform/javascript/js/libs/library_godot_display.js91
-rw-r--r--platform/javascript/js/libs/library_godot_os.js59
4 files changed, 182 insertions, 22 deletions
diff --git a/platform/javascript/js/engine/config.js b/platform/javascript/js/engine/config.js
index 5628ddbab5..2e5e1ed0d1 100644
--- a/platform/javascript/js/engine/config.js
+++ b/platform/javascript/js/engine/config.js
@@ -107,6 +107,13 @@ const InternalConfig = function (initConfig) { // eslint-disable-line no-unused-
*/
experimentalVK: false,
/**
+ * The progressive web app service worker to install.
+ * @memberof EngineConfig
+ * @default
+ * @type {string}
+ */
+ serviceWorker: '',
+ /**
* @ignore
* @type {Array.<string>}
*/
@@ -225,34 +232,36 @@ const InternalConfig = function (initConfig) { // eslint-disable-line no-unused-
*/
Config.prototype.update = function (opts) {
const config = opts || {};
- const me = this;
- function parse(key) {
+ // NOTE: We must explicitly pass the default, accessing it via
+ // the key will fail due to closure compiler renames.
+ function parse(key, def) {
if (typeof (config[key]) === 'undefined') {
- return me[key];
+ return def;
}
return config[key];
}
// Module config
- this.unloadAfterInit = parse('unloadAfterInit');
- this.onPrintError = parse('onPrintError');
- this.onPrint = parse('onPrint');
- this.onProgress = parse('onProgress');
+ this.unloadAfterInit = parse('unloadAfterInit', this.unloadAfterInit);
+ this.onPrintError = parse('onPrintError', this.onPrintError);
+ this.onPrint = parse('onPrint', this.onPrint);
+ this.onProgress = parse('onProgress', this.onProgress);
// Godot config
- this.canvas = parse('canvas');
- this.executable = parse('executable');
- this.mainPack = parse('mainPack');
- this.locale = parse('locale');
- this.canvasResizePolicy = parse('canvasResizePolicy');
- this.persistentPaths = parse('persistentPaths');
- this.persistentDrops = parse('persistentDrops');
- this.experimentalVK = parse('experimentalVK');
- this.focusCanvas = parse('focusCanvas');
- this.gdnativeLibs = parse('gdnativeLibs');
- this.fileSizes = parse('fileSizes');
- this.args = parse('args');
- this.onExecute = parse('onExecute');
- this.onExit = parse('onExit');
+ this.canvas = parse('canvas', this.canvas);
+ this.executable = parse('executable', this.executable);
+ this.mainPack = parse('mainPack', this.mainPack);
+ this.locale = parse('locale', this.locale);
+ this.canvasResizePolicy = parse('canvasResizePolicy', this.canvasResizePolicy);
+ this.persistentPaths = parse('persistentPaths', this.persistentPaths);
+ this.persistentDrops = parse('persistentDrops', this.persistentDrops);
+ this.experimentalVK = parse('experimentalVK', this.experimentalVK);
+ this.focusCanvas = parse('focusCanvas', this.focusCanvas);
+ this.serviceWorker = parse('serviceWorker', this.serviceWorker);
+ this.gdnativeLibs = parse('gdnativeLibs', this.gdnativeLibs);
+ this.fileSizes = parse('fileSizes', this.fileSizes);
+ this.args = parse('args', this.args);
+ this.onExecute = parse('onExecute', this.onExecute);
+ this.onExit = parse('onExit', this.onExit);
};
/**
diff --git a/platform/javascript/js/engine/engine.js b/platform/javascript/js/engine/engine.js
index 17a8df9e29..d2ba595083 100644
--- a/platform/javascript/js/engine/engine.js
+++ b/platform/javascript/js/engine/engine.js
@@ -189,6 +189,9 @@ const Engine = (function () {
preloader.preloadedFiles.length = 0; // Clear memory
me.rtenv['callMain'](me.config.args);
initPromise = null;
+ if (me.config.serviceWorker && 'serviceWorker' in navigator) {
+ navigator.serviceWorker.register(me.config.serviceWorker);
+ }
resolve();
});
});
diff --git a/platform/javascript/js/libs/library_godot_display.js b/platform/javascript/js/libs/library_godot_display.js
index 2bdf4e56ad..5997631bf8 100644
--- a/platform/javascript/js/libs/library_godot_display.js
+++ b/platform/javascript/js/libs/library_godot_display.js
@@ -330,6 +330,91 @@ const GodotDisplay = {
return 0;
},
+ godot_js_tts_is_speaking__sig: 'i',
+ godot_js_tts_is_speaking: function () {
+ return window.speechSynthesis.speaking;
+ },
+
+ godot_js_tts_is_paused__sig: 'i',
+ godot_js_tts_is_paused: function () {
+ return window.speechSynthesis.paused;
+ },
+
+ godot_js_tts_get_voices__sig: 'vi',
+ godot_js_tts_get_voices: function (p_callback) {
+ const func = GodotRuntime.get_func(p_callback);
+ try {
+ const arr = [];
+ const voices = window.speechSynthesis.getVoices();
+ for (let i = 0; i < voices.length; i++) {
+ arr.push(`${voices[i].lang};${voices[i].name}`);
+ }
+ const c_ptr = GodotRuntime.allocStringArray(arr);
+ func(arr.length, c_ptr);
+ GodotRuntime.freeStringArray(c_ptr, arr.length);
+ } catch (e) {
+ // Fail graciously.
+ }
+ },
+
+ godot_js_tts_speak__sig: 'viiiffii',
+ godot_js_tts_speak: function (p_text, p_voice, p_volume, p_pitch, p_rate, p_utterance_id, p_callback) {
+ const func = GodotRuntime.get_func(p_callback);
+
+ function listener_end(evt) {
+ evt.currentTarget.cb(1 /*TTS_UTTERANCE_ENDED*/, evt.currentTarget.id, 0);
+ }
+
+ function listener_start(evt) {
+ evt.currentTarget.cb(0 /*TTS_UTTERANCE_STARTED*/, evt.currentTarget.id, 0);
+ }
+
+ function listener_error(evt) {
+ evt.currentTarget.cb(2 /*TTS_UTTERANCE_CANCELED*/, evt.currentTarget.id, 0);
+ }
+
+ function listener_bound(evt) {
+ evt.currentTarget.cb(3 /*TTS_UTTERANCE_BOUNDARY*/, evt.currentTarget.id, evt.charIndex);
+ }
+
+ const utterance = new SpeechSynthesisUtterance(GodotRuntime.parseString(p_text));
+ utterance.rate = p_rate;
+ utterance.pitch = p_pitch;
+ utterance.volume = p_volume / 100.0;
+ utterance.addEventListener('end', listener_end);
+ utterance.addEventListener('start', listener_start);
+ utterance.addEventListener('error', listener_error);
+ utterance.addEventListener('boundary', listener_bound);
+ utterance.id = p_utterance_id;
+ utterance.cb = func;
+ const voice = GodotRuntime.parseString(p_voice);
+ const voices = window.speechSynthesis.getVoices();
+ for (let i = 0; i < voices.length; i++) {
+ if (voices[i].name === voice) {
+ utterance.voice = voices[i];
+ break;
+ }
+ }
+ window.speechSynthesis.resume();
+ window.speechSynthesis.speak(utterance);
+ },
+
+ godot_js_tts_pause__sig: 'v',
+ godot_js_tts_pause: function () {
+ window.speechSynthesis.pause();
+ },
+
+ godot_js_tts_resume__sig: 'v',
+ godot_js_tts_resume: function () {
+ window.speechSynthesis.resume();
+ },
+
+ godot_js_tts_stop__sig: 'v',
+ godot_js_tts_stop: function () {
+ window.speechSynthesis.cancel();
+ window.speechSynthesis.resume();
+ },
+
godot_js_display_alert__sig: 'vi',
godot_js_display_alert: function (p_text) {
window.alert(GodotRuntime.parseString(p_text)); // eslint-disable-line no-alert
@@ -377,6 +462,7 @@ const GodotDisplay = {
GodotRuntime.setHeapValue(height, window.screen.height * scale, 'i32');
},
+ godot_js_display_window_size_get__sig: 'vii',
godot_js_display_window_size_get: function (p_width, p_height) {
GodotRuntime.setHeapValue(p_width, GodotConfig.canvas.width, 'i32');
GodotRuntime.setHeapValue(p_height, GodotConfig.canvas.height, 'i32');
@@ -625,6 +711,11 @@ const GodotDisplay = {
return GodotDisplayVK.available();
},
+ godot_js_display_tts_available__sig: 'i',
+ godot_js_display_tts_available: function () {
+ return 'speechSynthesis' in window;
+ },
+
godot_js_display_vk_cb__sig: 'vi',
godot_js_display_vk_cb: function (p_input_cb) {
const input_cb = GodotRuntime.get_func(p_input_cb);
diff --git a/platform/javascript/js/libs/library_godot_os.js b/platform/javascript/js/libs/library_godot_os.js
index 662d215443..377eec3234 100644
--- a/platform/javascript/js/libs/library_godot_os.js
+++ b/platform/javascript/js/libs/library_godot_os.js
@@ -305,7 +305,9 @@ const GodotOS = {
godot_js_os_hw_concurrency_get__sig: 'i',
godot_js_os_hw_concurrency_get: function () {
- return navigator.hardwareConcurrency || 1;
+ // TODO Godot core needs fixing to avoid spawning too many threads (> 24).
+ const concurrency = navigator.hardwareConcurrency || 1;
+ return concurrency < 2 ? concurrency : 2;
},
godot_js_os_download_buffer__sig: 'viiii',
@@ -368,3 +370,58 @@ const GodotEventListeners = {
},
};
mergeInto(LibraryManager.library, GodotEventListeners);
+
+const GodotPWA = {
+
+ $GodotPWA__deps: ['$GodotRuntime', '$GodotEventListeners'],
+ $GodotPWA: {
+ hasUpdate: false,
+
+ updateState: function (cb, reg) {
+ if (!reg) {
+ return;
+ }
+ if (!reg.active) {
+ return;
+ }
+ if (reg.waiting) {
+ GodotPWA.hasUpdate = true;
+ cb();
+ }
+ GodotEventListeners.add(reg, 'updatefound', function () {
+ const installing = reg.installing;
+ GodotEventListeners.add(installing, 'statechange', function () {
+ if (installing.state === 'installed') {
+ GodotPWA.hasUpdate = true;
+ cb();
+ }
+ });
+ });
+ },
+ },
+
+ godot_js_pwa_cb__sig: 'vi',
+ godot_js_pwa_cb: function (p_update_cb) {
+ if ('serviceWorker' in navigator) {
+ const cb = GodotRuntime.get_func(p_update_cb);
+ navigator.serviceWorker.getRegistration().then(GodotPWA.updateState.bind(null, cb));
+ }
+ },
+
+ godot_js_pwa_update__sig: 'i',
+ godot_js_pwa_update: function () {
+ if ('serviceWorker' in navigator && GodotPWA.hasUpdate) {
+ navigator.serviceWorker.getRegistration().then(function (reg) {
+ if (!reg || !reg.waiting) {
+ return;
+ }
+ reg.waiting.postMessage('update');
+ });
+ return 0;
+ }
+ return 1;
+ },
+};
+
+autoAddDeps(GodotPWA, '$GodotPWA');
+mergeInto(LibraryManager.library, GodotPWA);