diff options
author | Fabio Alessandrelli <fabio.alessandrelli@gmail.com> | 2022-08-28 20:27:45 +0200 |
---|---|---|
committer | Fabio Alessandrelli <fabio.alessandrelli@gmail.com> | 2022-08-29 11:52:00 +0200 |
commit | d20b32186fc192f5e527a1211291b0cb293f4e66 (patch) | |
tree | 20f5e9e84e10b68c318f576344a10a9fc63d235f /platform/javascript/js | |
parent | 223e083d36ac1ca3f7aa46898d8870e476132f7a (diff) |
[Web] Rename JavaScript platform to Web.
Also rename export name from "HTML5" to "Web".
Diffstat (limited to 'platform/javascript/js')
-rw-r--r-- | platform/javascript/js/engine/config.js | 358 | ||||
-rw-r--r-- | platform/javascript/js/engine/engine.externs.js | 4 | ||||
-rw-r--r-- | platform/javascript/js/engine/engine.js | 281 | ||||
-rw-r--r-- | platform/javascript/js/engine/preloader.js | 133 | ||||
-rw-r--r-- | platform/javascript/js/jsdoc2rst/publish.js | 354 | ||||
-rw-r--r-- | platform/javascript/js/libs/audio.worklet.js | 211 | ||||
-rw-r--r-- | platform/javascript/js/libs/library_godot_audio.js | 484 | ||||
-rw-r--r-- | platform/javascript/js/libs/library_godot_display.js | 754 | ||||
-rw-r--r-- | platform/javascript/js/libs/library_godot_fetch.js | 247 | ||||
-rw-r--r-- | platform/javascript/js/libs/library_godot_input.js | 549 | ||||
-rw-r--r-- | platform/javascript/js/libs/library_godot_javascript_singleton.js | 346 | ||||
-rw-r--r-- | platform/javascript/js/libs/library_godot_os.js | 427 | ||||
-rw-r--r-- | platform/javascript/js/libs/library_godot_runtime.js | 134 |
13 files changed, 0 insertions, 4282 deletions
diff --git a/platform/javascript/js/engine/config.js b/platform/javascript/js/engine/config.js deleted file mode 100644 index 9c4b6c2012..0000000000 --- a/platform/javascript/js/engine/config.js +++ /dev/null @@ -1,358 +0,0 @@ -/** - * An object used to configure the Engine instance based on godot export options, and to override those in custom HTML - * templates if needed. - * - * @header Engine configuration - * @summary The Engine configuration object. This is just a typedef, create it like a regular object, e.g.: - * - * ``const MyConfig = { executable: 'godot', unloadAfterInit: false }`` - * - * @typedef {Object} EngineConfig - */ -const EngineConfig = {}; // eslint-disable-line no-unused-vars - -/** - * @struct - * @constructor - * @ignore - */ -const InternalConfig = function (initConfig) { // eslint-disable-line no-unused-vars - const cfg = /** @lends {InternalConfig.prototype} */ { - /** - * Whether the unload the engine automatically after the instance is initialized. - * - * @memberof EngineConfig - * @default - * @type {boolean} - */ - unloadAfterInit: true, - /** - * The HTML DOM Canvas object to use. - * - * By default, the first canvas element in the document will be used is none is specified. - * - * @memberof EngineConfig - * @default - * @type {?HTMLCanvasElement} - */ - canvas: null, - /** - * The name of the WASM file without the extension. (Set by Godot Editor export process). - * - * @memberof EngineConfig - * @default - * @type {string} - */ - executable: '', - /** - * An alternative name for the game pck to load. The executable name is used otherwise. - * - * @memberof EngineConfig - * @default - * @type {?string} - */ - mainPack: null, - /** - * Specify a language code to select the proper localization for the game. - * - * The browser locale will be used if none is specified. See complete list of - * :ref:`supported locales <doc_locales>`. - * - * @memberof EngineConfig - * @type {?string} - * @default - */ - locale: null, - /** - * The canvas resize policy determines how the canvas should be resized by Godot. - * - * ``0`` means Godot won't do any resizing. This is useful if you want to control the canvas size from - * javascript code in your template. - * - * ``1`` means Godot will resize the canvas on start, and when changing window size via engine functions. - * - * ``2`` means Godot will adapt the canvas size to match the whole browser window. - * - * @memberof EngineConfig - * @type {number} - * @default - */ - canvasResizePolicy: 2, - /** - * The arguments to be passed as command line arguments on startup. - * - * See :ref:`command line tutorial <doc_command_line_tutorial>`. - * - * **Note**: :js:meth:`startGame <Engine.prototype.startGame>` will always add the ``--main-pack`` argument. - * - * @memberof EngineConfig - * @type {Array<string>} - * @default - */ - args: [], - /** - * When enabled, the game canvas will automatically grab the focus when the engine starts. - * - * @memberof EngineConfig - * @type {boolean} - * @default - */ - focusCanvas: true, - /** - * When enabled, this will turn on experimental virtual keyboard support on mobile. - * - * @memberof EngineConfig - * @type {boolean} - * @default - */ - experimentalVK: false, - /** - * The progressive web app service worker to install. - * @memberof EngineConfig - * @default - * @type {string} - */ - serviceWorker: '', - /** - * @ignore - * @type {Array.<string>} - */ - persistentPaths: ['/userfs'], - /** - * @ignore - * @type {boolean} - */ - persistentDrops: false, - /** - * @ignore - * @type {Array.<string>} - */ - gdnativeLibs: [], - /** - * @ignore - * @type {Array.<string>} - */ - fileSizes: [], - /** - * A callback function for handling Godot's ``OS.execute`` calls. - * - * This is for example used in the Web Editor template to switch between project manager and editor, and for running the game. - * - * @callback EngineConfig.onExecute - * @param {string} path The path that Godot's wants executed. - * @param {Array.<string>} args The arguments of the "command" to execute. - */ - /** - * @ignore - * @type {?function(string, Array.<string>)} - */ - onExecute: null, - /** - * A callback function for being notified when the Godot instance quits. - * - * **Note**: This function will not be called if the engine crashes or become unresponsive. - * - * @callback EngineConfig.onExit - * @param {number} status_code The status code returned by Godot on exit. - */ - /** - * @ignore - * @type {?function(number)} - */ - onExit: null, - /** - * A callback function for displaying download progress. - * - * The function is called once per frame while downloading files, so the usage of ``requestAnimationFrame()`` - * is not necessary. - * - * If the callback function receives a total amount of bytes as 0, this means that it is impossible to calculate. - * Possible reasons include: - * - * - Files are delivered with server-side chunked compression - * - Files are delivered with server-side compression on Chromium - * - Not all file downloads have started yet (usually on servers without multi-threading) - * - * @callback EngineConfig.onProgress - * @param {number} current The current amount of downloaded bytes so far. - * @param {number} total The total amount of bytes to be downloaded. - */ - /** - * @ignore - * @type {?function(number, number)} - */ - onProgress: null, - /** - * A callback function for handling the standard output stream. This method should usually only be used in debug pages. - * - * By default, ``console.log()`` is used. - * - * @callback EngineConfig.onPrint - * @param {...*} [var_args] A variadic number of arguments to be printed. - */ - /** - * @ignore - * @type {?function(...*)} - */ - onPrint: function () { - console.log.apply(console, Array.from(arguments)); // eslint-disable-line no-console - }, - /** - * A callback function for handling the standard error stream. This method should usually only be used in debug pages. - * - * By default, ``console.error()`` is used. - * - * @callback EngineConfig.onPrintError - * @param {...*} [var_args] A variadic number of arguments to be printed as errors. - */ - /** - * @ignore - * @type {?function(...*)} - */ - onPrintError: function (var_args) { - console.error.apply(console, Array.from(arguments)); // eslint-disable-line no-console - }, - }; - - /** - * @ignore - * @struct - * @constructor - * @param {EngineConfig} opts - */ - function Config(opts) { - this.update(opts); - } - - Config.prototype = cfg; - - /** - * @ignore - * @param {EngineConfig} opts - */ - Config.prototype.update = function (opts) { - const config = opts || {}; - // 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 def; - } - return config[key]; - } - // Module config - 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.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); - }; - - /** - * @ignore - * @param {string} loadPath - * @param {Response} response - */ - Config.prototype.getModuleConfig = function (loadPath, response) { - let r = response; - return { - 'print': this.onPrint, - 'printErr': this.onPrintError, - 'thisProgram': this.executable, - 'noExitRuntime': true, - 'dynamicLibraries': [`${loadPath}.side.wasm`], - 'instantiateWasm': function (imports, onSuccess) { - function done(result) { - onSuccess(result['instance'], result['module']); - } - if (typeof (WebAssembly.instantiateStreaming) !== 'undefined') { - WebAssembly.instantiateStreaming(Promise.resolve(r), imports).then(done); - } else { - r.arrayBuffer().then(function (buffer) { - WebAssembly.instantiate(buffer, imports).then(done); - }); - } - r = null; - return {}; - }, - 'locateFile': function (path) { - if (path.endsWith('.worker.js')) { - return `${loadPath}.worker.js`; - } else if (path.endsWith('.audio.worklet.js')) { - return `${loadPath}.audio.worklet.js`; - } else if (path.endsWith('.js')) { - return `${loadPath}.js`; - } else if (path.endsWith('.side.wasm')) { - return `${loadPath}.side.wasm`; - } else if (path.endsWith('.wasm')) { - return `${loadPath}.wasm`; - } - return path; - }, - }; - }; - - /** - * @ignore - * @param {function()} cleanup - */ - Config.prototype.getGodotConfig = function (cleanup) { - // Try to find a canvas - if (!(this.canvas instanceof HTMLCanvasElement)) { - const nodes = document.getElementsByTagName('canvas'); - if (nodes.length && nodes[0] instanceof HTMLCanvasElement) { - this.canvas = nodes[0]; - } - if (!this.canvas) { - throw new Error('No canvas found in page'); - } - } - // Canvas can grab focus on click, or key events won't work. - if (this.canvas.tabIndex < 0) { - this.canvas.tabIndex = 0; - } - - // Browser locale, or custom one if defined. - let locale = this.locale; - if (!locale) { - locale = navigator.languages ? navigator.languages[0] : navigator.language; - locale = locale.split('.')[0]; - } - locale = locale.replace('-', '_'); - const onExit = this.onExit; - - // Godot configuration. - return { - 'canvas': this.canvas, - 'canvasResizePolicy': this.canvasResizePolicy, - 'locale': locale, - 'persistentDrops': this.persistentDrops, - 'virtualKeyboard': this.experimentalVK, - 'focusCanvas': this.focusCanvas, - 'onExecute': this.onExecute, - 'onExit': function (p_code) { - cleanup(); // We always need to call the cleanup callback to free memory. - if (typeof (onExit) === 'function') { - onExit(p_code); - } - }, - }; - }; - return new Config(initConfig); -}; diff --git a/platform/javascript/js/engine/engine.externs.js b/platform/javascript/js/engine/engine.externs.js deleted file mode 100644 index 35a66a93ae..0000000000 --- a/platform/javascript/js/engine/engine.externs.js +++ /dev/null @@ -1,4 +0,0 @@ -var Godot; -var WebAssembly = {}; -WebAssembly.instantiate = function(buffer, imports) {}; -WebAssembly.instantiateStreaming = function(response, imports) {}; diff --git a/platform/javascript/js/engine/engine.js b/platform/javascript/js/engine/engine.js deleted file mode 100644 index d2ba595083..0000000000 --- a/platform/javascript/js/engine/engine.js +++ /dev/null @@ -1,281 +0,0 @@ -/** - * Projects exported for the Web expose the :js:class:`Engine` class to the JavaScript environment, that allows - * fine control over the engine's start-up process. - * - * This API is built in an asynchronous manner and requires basic understanding - * of `Promises <https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Using_promises>`__. - * - * @module Engine - * @header HTML5 shell class reference - */ -const Engine = (function () { - const preloader = new Preloader(); - - let loadPromise = null; - let loadPath = ''; - let initPromise = null; - - /** - * @classdesc The ``Engine`` class provides methods for loading and starting exported projects on the Web. For default export - * settings, this is already part of the exported HTML page. To understand practical use of the ``Engine`` class, - * see :ref:`Custom HTML page for Web export <doc_customizing_html5_shell>`. - * - * @description Create a new Engine instance with the given configuration. - * - * @global - * @constructor - * @param {EngineConfig} initConfig The initial config for this instance. - */ - function Engine(initConfig) { // eslint-disable-line no-shadow - this.config = new InternalConfig(initConfig); - this.rtenv = null; - } - - /** - * Load the engine from the specified base path. - * - * @param {string} basePath Base path of the engine to load. - * @param {number=} [size=0] The file size if known. - * @returns {Promise} A Promise that resolves once the engine is loaded. - * - * @function Engine.load - */ - Engine.load = function (basePath, size) { - if (loadPromise == null) { - loadPath = basePath; - loadPromise = preloader.loadPromise(`${loadPath}.wasm`, size, true); - requestAnimationFrame(preloader.animateProgress); - } - return loadPromise; - }; - - /** - * Unload the engine to free memory. - * - * This method will be called automatically depending on the configuration. See :js:attr:`unloadAfterInit`. - * - * @function Engine.unload - */ - Engine.unload = function () { - loadPromise = null; - }; - - /** - * Check whether WebGL is available. Optionally, specify a particular version of WebGL to check for. - * - * @param {number=} [majorVersion=1] The major WebGL version to check for. - * @returns {boolean} If the given major version of WebGL is available. - * @function Engine.isWebGLAvailable - */ - Engine.isWebGLAvailable = function (majorVersion = 1) { - try { - return !!document.createElement('canvas').getContext(['webgl', 'webgl2'][majorVersion - 1]); - } catch (e) { /* Not available */ } - return false; - }; - - /** - * Safe Engine constructor, creates a new prototype for every new instance to avoid prototype pollution. - * @ignore - * @constructor - */ - function SafeEngine(initConfig) { - const proto = /** @lends Engine.prototype */ { - /** - * Initialize the engine instance. Optionally, pass the base path to the engine to load it, - * if it hasn't been loaded yet. See :js:meth:`Engine.load`. - * - * @param {string=} basePath Base path of the engine to load. - * @return {Promise} A ``Promise`` that resolves once the engine is loaded and initialized. - */ - init: function (basePath) { - if (initPromise) { - return initPromise; - } - if (loadPromise == null) { - if (!basePath) { - initPromise = Promise.reject(new Error('A base path must be provided when calling `init` and the engine is not loaded.')); - return initPromise; - } - Engine.load(basePath, this.config.fileSizes[`${basePath}.wasm`]); - } - const me = this; - function doInit(promise) { - // Care! Promise chaining is bogus with old emscripten versions. - // This caused a regression with the Mono build (which uses an older emscripten version). - // Make sure to test that when refactoring. - return new Promise(function (resolve, reject) { - promise.then(function (response) { - const cloned = new Response(response.clone().body, { 'headers': [['content-type', 'application/wasm']] }); - Godot(me.config.getModuleConfig(loadPath, cloned)).then(function (module) { - const paths = me.config.persistentPaths; - module['initFS'](paths).then(function (err) { - me.rtenv = module; - if (me.config.unloadAfterInit) { - Engine.unload(); - } - resolve(); - }); - }); - }); - }); - } - preloader.setProgressFunc(this.config.onProgress); - initPromise = doInit(loadPromise); - return initPromise; - }, - - /** - * Load a file so it is available in the instance's file system once it runs. Must be called **before** starting the - * instance. - * - * If not provided, the ``path`` is derived from the URL of the loaded file. - * - * @param {string|ArrayBuffer} file The file to preload. - * - * If a ``string`` the file will be loaded from that path. - * - * If an ``ArrayBuffer`` or a view on one, the buffer will used as the content of the file. - * - * @param {string=} path Path by which the file will be accessible. Required, if ``file`` is not a string. - * - * @returns {Promise} A Promise that resolves once the file is loaded. - */ - preloadFile: function (file, path) { - return preloader.preload(file, path, this.config.fileSizes[file]); - }, - - /** - * Start the engine instance using the given override configuration (if any). - * :js:meth:`startGame <Engine.prototype.startGame>` can be used in typical cases instead. - * - * This will initialize the instance if it is not initialized. For manual initialization, see :js:meth:`init <Engine.prototype.init>`. - * The engine must be loaded beforehand. - * - * Fails if a canvas cannot be found on the page, or not specified in the configuration. - * - * @param {EngineConfig} override An optional configuration override. - * @return {Promise} Promise that resolves once the engine started. - */ - start: function (override) { - this.config.update(override); - const me = this; - return me.init().then(function () { - if (!me.rtenv) { - return Promise.reject(new Error('The engine must be initialized before it can be started')); - } - - let config = {}; - try { - config = me.config.getGodotConfig(function () { - me.rtenv = null; - }); - } catch (e) { - return Promise.reject(e); - } - // Godot configuration. - me.rtenv['initConfig'](config); - - // Preload GDNative libraries. - const libs = []; - me.config.gdnativeLibs.forEach(function (lib) { - libs.push(me.rtenv['loadDynamicLibrary'](lib, { 'loadAsync': true })); - }); - return Promise.all(libs).then(function () { - return new Promise(function (resolve, reject) { - preloader.preloadedFiles.forEach(function (file) { - me.rtenv['copyToFS'](file.path, file.buffer); - }); - 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(); - }); - }); - }); - }, - - /** - * Start the game instance using the given configuration override (if any). - * - * This will initialize the instance if it is not initialized. For manual initialization, see :js:meth:`init <Engine.prototype.init>`. - * - * This will load the engine if it is not loaded, and preload the main pck. - * - * This method expects the initial config (or the override) to have both the :js:attr:`executable` and :js:attr:`mainPack` - * properties set (normally done by the editor during export). - * - * @param {EngineConfig} override An optional configuration override. - * @return {Promise} Promise that resolves once the game started. - */ - startGame: function (override) { - this.config.update(override); - // Add main-pack argument. - const exe = this.config.executable; - const pack = this.config.mainPack || `${exe}.pck`; - this.config.args = ['--main-pack', pack].concat(this.config.args); - // Start and init with execName as loadPath if not inited. - const me = this; - return Promise.all([ - this.init(exe), - this.preloadFile(pack, pack), - ]).then(function () { - return me.start.apply(me); - }); - }, - - /** - * Create a file at the specified ``path`` with the passed as ``buffer`` in the instance's file system. - * - * @param {string} path The location where the file will be created. - * @param {ArrayBuffer} buffer The content of the file. - */ - copyToFS: function (path, buffer) { - if (this.rtenv == null) { - throw new Error('Engine must be inited before copying files'); - } - this.rtenv['copyToFS'](path, buffer); - }, - - /** - * Request that the current instance quit. - * - * This is akin the user pressing the close button in the window manager, and will - * have no effect if the engine has crashed, or is stuck in a loop. - * - */ - requestQuit: function () { - if (this.rtenv) { - this.rtenv['request_quit'](); - } - }, - }; - - Engine.prototype = proto; - // Closure compiler exported instance methods. - Engine.prototype['init'] = Engine.prototype.init; - Engine.prototype['preloadFile'] = Engine.prototype.preloadFile; - Engine.prototype['start'] = Engine.prototype.start; - Engine.prototype['startGame'] = Engine.prototype.startGame; - Engine.prototype['copyToFS'] = Engine.prototype.copyToFS; - Engine.prototype['requestQuit'] = Engine.prototype.requestQuit; - // Also expose static methods as instance methods - Engine.prototype['load'] = Engine.load; - Engine.prototype['unload'] = Engine.unload; - Engine.prototype['isWebGLAvailable'] = Engine.isWebGLAvailable; - return new Engine(initConfig); - } - - // Closure compiler exported static methods. - SafeEngine['load'] = Engine.load; - SafeEngine['unload'] = Engine.unload; - SafeEngine['isWebGLAvailable'] = Engine.isWebGLAvailable; - - return SafeEngine; -}()); -if (typeof window !== 'undefined') { - window['Engine'] = Engine; -} diff --git a/platform/javascript/js/engine/preloader.js b/platform/javascript/js/engine/preloader.js deleted file mode 100644 index 564c68d264..0000000000 --- a/platform/javascript/js/engine/preloader.js +++ /dev/null @@ -1,133 +0,0 @@ -const Preloader = /** @constructor */ function () { // eslint-disable-line no-unused-vars - function getTrackedResponse(response, load_status) { - function onloadprogress(reader, controller) { - return reader.read().then(function (result) { - if (load_status.done) { - return Promise.resolve(); - } - if (result.value) { - controller.enqueue(result.value); - load_status.loaded += result.value.length; - } - if (!result.done) { - return onloadprogress(reader, controller); - } - load_status.done = true; - return Promise.resolve(); - }); - } - const reader = response.body.getReader(); - return new Response(new ReadableStream({ - start: function (controller) { - onloadprogress(reader, controller).then(function () { - controller.close(); - }); - }, - }), { headers: response.headers }); - } - - function loadFetch(file, tracker, fileSize, raw) { - tracker[file] = { - total: fileSize || 0, - loaded: 0, - done: false, - }; - return fetch(file).then(function (response) { - if (!response.ok) { - return Promise.reject(new Error(`Failed loading file '${file}'`)); - } - const tr = getTrackedResponse(response, tracker[file]); - if (raw) { - return Promise.resolve(tr); - } - return tr.arrayBuffer(); - }); - } - - function retry(func, attempts = 1) { - function onerror(err) { - if (attempts <= 1) { - return Promise.reject(err); - } - return new Promise(function (resolve, reject) { - setTimeout(function () { - retry(func, attempts - 1).then(resolve).catch(reject); - }, 1000); - }); - } - return func().catch(onerror); - } - - const DOWNLOAD_ATTEMPTS_MAX = 4; - const loadingFiles = {}; - const lastProgress = { loaded: 0, total: 0 }; - let progressFunc = null; - - const animateProgress = function () { - let loaded = 0; - let total = 0; - let totalIsValid = true; - let progressIsFinal = true; - - Object.keys(loadingFiles).forEach(function (file) { - const stat = loadingFiles[file]; - if (!stat.done) { - progressIsFinal = false; - } - if (!totalIsValid || stat.total === 0) { - totalIsValid = false; - total = 0; - } else { - total += stat.total; - } - loaded += stat.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.animateProgress = animateProgress; - - this.setProgressFunc = function (callback) { - progressFunc = callback; - }; - - this.loadPromise = function (file, fileSize, raw = false) { - return retry(loadFetch.bind(null, file, loadingFiles, fileSize, raw), DOWNLOAD_ATTEMPTS_MAX); - }; - - this.preloadedFiles = []; - this.preload = function (pathOrBuffer, destPath, fileSize) { - let buffer = null; - if (typeof pathOrBuffer === 'string') { - const me = this; - return this.loadPromise(pathOrBuffer, fileSize).then(function (buf) { - me.preloadedFiles.push({ - path: destPath || pathOrBuffer, - buffer: buf, - }); - return Promise.resolve(); - }); - } else if (pathOrBuffer instanceof ArrayBuffer) { - buffer = new Uint8Array(pathOrBuffer); - } else if (ArrayBuffer.isView(pathOrBuffer)) { - buffer = new Uint8Array(pathOrBuffer.buffer); - } - if (buffer) { - this.preloadedFiles.push({ - path: destPath, - buffer: pathOrBuffer, - }); - return Promise.resolve(); - } - return Promise.reject(new Error('Invalid object for preloading')); - }; -}; diff --git a/platform/javascript/js/jsdoc2rst/publish.js b/platform/javascript/js/jsdoc2rst/publish.js deleted file mode 100644 index ad9c0fbaaa..0000000000 --- a/platform/javascript/js/jsdoc2rst/publish.js +++ /dev/null @@ -1,354 +0,0 @@ -/* eslint-disable strict */ - -'use strict'; - -const fs = require('fs'); - -class JSDoclet { - constructor(doc) { - this.doc = doc; - this.description = doc['description'] || ''; - this.name = doc['name'] || 'unknown'; - this.longname = doc['longname'] || ''; - this.types = []; - if (doc['type'] && doc['type']['names']) { - this.types = doc['type']['names'].slice(); - } - this.type = this.types.length > 0 ? this.types.join('\\|') : '*'; - this.variable = doc['variable'] || false; - this.kind = doc['kind'] || ''; - this.memberof = doc['memberof'] || null; - this.scope = doc['scope'] || ''; - this.members = []; - this.optional = doc['optional'] || false; - this.defaultvalue = doc['defaultvalue']; - this.summary = doc['summary'] || null; - this.classdesc = doc['classdesc'] || null; - - // Parameters (functions) - this.params = []; - this.returns = doc['returns'] ? doc['returns'][0]['type']['names'][0] : 'void'; - this.returns_desc = doc['returns'] ? doc['returns'][0]['description'] : null; - - this.params = (doc['params'] || []).slice().map((p) => new JSDoclet(p)); - - // Custom tags - this.tags = doc['tags'] || []; - this.header = this.tags.filter((t) => t['title'] === 'header').map((t) => t['text']).pop() || null; - } - - add_member(obj) { - this.members.push(obj); - } - - is_static() { - return this.scope === 'static'; - } - - is_instance() { - return this.scope === 'instance'; - } - - is_object() { - return this.kind === 'Object' || (this.kind === 'typedef' && this.type === 'Object'); - } - - is_class() { - return this.kind === 'class'; - } - - is_function() { - return this.kind === 'function' || (this.kind === 'typedef' && this.type === 'function'); - } - - is_module() { - return this.kind === 'module'; - } -} - -function format_table(f, data, depth = 0) { - if (!data.length) { - return; - } - - const column_sizes = new Array(data[0].length).fill(0); - - data.forEach((row) => { - row.forEach((e, idx) => { - column_sizes[idx] = Math.max(e.length, column_sizes[idx]); - }); - }); - - const indent = ' '.repeat(depth); - let sep = indent; - column_sizes.forEach((size) => { - sep += '+'; - sep += '-'.repeat(size + 2); - }); - sep += '+\n'; - f.write(sep); - - data.forEach((row) => { - let row_text = `${indent}|`; - row.forEach((entry, idx) => { - row_text += ` ${entry.padEnd(column_sizes[idx])} |`; - }); - row_text += '\n'; - f.write(row_text); - f.write(sep); - }); - - f.write('\n'); -} - -function make_header(header, sep) { - return `${header}\n${sep.repeat(header.length)}\n\n`; -} - -function indent_multiline(text, depth) { - const indent = ' '.repeat(depth); - return text.split('\n').map((l) => (l === '' ? l : indent + l)).join('\n'); -} - -function make_rst_signature(obj, types = false, style = false) { - let out = ''; - const fmt = style ? '*' : ''; - obj.params.forEach((arg, idx) => { - if (idx > 0) { - if (arg.optional) { - out += ` ${fmt}[`; - } - out += ', '; - } else { - out += ' '; - if (arg.optional) { - out += `${fmt}[ `; - } - } - if (types) { - out += `${arg.type} `; - } - const variable = arg.variable ? '...' : ''; - const defval = arg.defaultvalue !== undefined ? `=${arg.defaultvalue}` : ''; - out += `${variable}${arg.name}${defval}`; - if (arg.optional) { - out += ` ]${fmt}`; - } - }); - out += ' '; - return out; -} - -function make_rst_param(f, obj, depth = 0) { - const indent = ' '.repeat(depth * 3); - f.write(indent); - f.write(`:param ${obj.type} ${obj.name}:\n`); - f.write(indent_multiline(obj.description, (depth + 1) * 3)); - f.write('\n\n'); -} - -function make_rst_attribute(f, obj, depth = 0, brief = false) { - const indent = ' '.repeat(depth * 3); - f.write(indent); - f.write(`.. js:attribute:: ${obj.name}\n\n`); - - if (brief) { - if (obj.summary) { - f.write(indent_multiline(obj.summary, (depth + 1) * 3)); - } - f.write('\n\n'); - return; - } - - f.write(indent_multiline(obj.description, (depth + 1) * 3)); - f.write('\n\n'); - - f.write(indent); - f.write(` :type: ${obj.type}\n\n`); - - if (obj.defaultvalue !== undefined) { - let defval = obj.defaultvalue; - if (defval === '') { - defval = '""'; - } - f.write(indent); - f.write(` :value: \`\`${defval}\`\`\n\n`); - } -} - -function make_rst_function(f, obj, depth = 0) { - let prefix = ''; - if (obj.is_instance()) { - prefix = 'prototype.'; - } - - const indent = ' '.repeat(depth * 3); - const sig = make_rst_signature(obj); - f.write(indent); - f.write(`.. js:function:: ${prefix}${obj.name}(${sig})\n`); - f.write('\n'); - - f.write(indent_multiline(obj.description, (depth + 1) * 3)); - f.write('\n\n'); - - obj.params.forEach((param) => { - make_rst_param(f, param, depth + 1); - }); - - if (obj.returns !== 'void') { - f.write(indent); - f.write(' :return:\n'); - f.write(indent_multiline(obj.returns_desc, (depth + 2) * 3)); - f.write('\n\n'); - f.write(indent); - f.write(` :rtype: ${obj.returns}\n\n`); - } -} - -function make_rst_object(f, obj) { - let brief = false; - // Our custom header flag. - if (obj.header !== null) { - f.write(make_header(obj.header, '-')); - f.write(`${obj.description}\n\n`); - brief = true; - } - - // Format members table and descriptions - const data = [['type', 'name']].concat(obj.members.map((m) => [m.type, `:js:attr:\`${m.name}\``])); - - f.write(make_header('Properties', '^')); - format_table(f, data, 0); - - make_rst_attribute(f, obj, 0, brief); - - if (!obj.members.length) { - return; - } - - f.write(' **Property Descriptions**\n\n'); - - // Properties first - obj.members.filter((m) => !m.is_function()).forEach((m) => { - make_rst_attribute(f, m, 1); - }); - - // Callbacks last - obj.members.filter((m) => m.is_function()).forEach((m) => { - make_rst_function(f, m, 1); - }); -} - -function make_rst_class(f, obj) { - const header = obj.header ? obj.header : obj.name; - f.write(make_header(header, '-')); - - if (obj.classdesc) { - f.write(`${obj.classdesc}\n\n`); - } - - const funcs = obj.members.filter((m) => m.is_function()); - function make_data(m) { - const base = m.is_static() ? obj.name : `${obj.name}.prototype`; - const params = make_rst_signature(m, true, true); - const sig = `:js:attr:\`${m.name} <${base}.${m.name}>\` **(**${params}**)**`; - return [m.returns, sig]; - } - const sfuncs = funcs.filter((m) => m.is_static()); - const ifuncs = funcs.filter((m) => !m.is_static()); - - f.write(make_header('Static Methods', '^')); - format_table(f, sfuncs.map((m) => make_data(m))); - - f.write(make_header('Instance Methods', '^')); - format_table(f, ifuncs.map((m) => make_data(m))); - - const sig = make_rst_signature(obj); - f.write(`.. js:class:: ${obj.name}(${sig})\n\n`); - f.write(indent_multiline(obj.description, 3)); - f.write('\n\n'); - - obj.params.forEach((p) => { - make_rst_param(f, p, 1); - }); - - f.write(' **Static Methods**\n\n'); - sfuncs.forEach((m) => { - make_rst_function(f, m, 1); - }); - - f.write(' **Instance Methods**\n\n'); - ifuncs.forEach((m) => { - make_rst_function(f, m, 1); - }); -} - -function make_rst_module(f, obj) { - const header = obj.header !== null ? obj.header : obj.name; - f.write(make_header(header, '=')); - f.write(obj.description); - f.write('\n\n'); -} - -function write_base_object(f, obj) { - if (obj.is_object()) { - make_rst_object(f, obj); - } else if (obj.is_function()) { - make_rst_function(f, obj); - } else if (obj.is_class()) { - make_rst_class(f, obj); - } else if (obj.is_module()) { - make_rst_module(f, obj); - } -} - -function generate(f, docs) { - const globs = []; - const SYMBOLS = {}; - docs.filter((d) => !d.ignore && d.kind !== 'package').forEach((d) => { - SYMBOLS[d.name] = d; - if (d.memberof) { - const up = SYMBOLS[d.memberof]; - if (up === undefined) { - console.log(d); // eslint-disable-line no-console - console.log(`Undefined symbol! ${d.memberof}`); // eslint-disable-line no-console - throw new Error('Undefined symbol!'); - } - SYMBOLS[d.memberof].add_member(d); - } else { - globs.push(d); - } - }); - - f.write('.. _doc_html5_shell_classref:\n\n'); - globs.forEach((obj) => write_base_object(f, obj)); -} - -/** - * Generate documentation output. - * - * @param {TAFFY} data - A TaffyDB collection representing - * all the symbols documented in your code. - * @param {object} opts - An object with options information. - */ -exports.publish = function (data, opts) { - const docs = data().get().filter((doc) => !doc.undocumented && !doc.ignore).map((doc) => new JSDoclet(doc)); - const dest = opts.destination; - if (dest === 'dry-run') { - process.stdout.write('Dry run... '); - generate({ - write: function () { /* noop */ }, - }, docs); - process.stdout.write('Okay!\n'); - return; - } - if (dest !== '' && !dest.endsWith('.rst')) { - throw new Error('Destination file must be either a ".rst" file, or an empty string (for printing to stdout)'); - } - if (dest !== '') { - const f = fs.createWriteStream(dest); - generate(f, docs); - } else { - generate(process.stdout, docs); - } -}; diff --git a/platform/javascript/js/libs/audio.worklet.js b/platform/javascript/js/libs/audio.worklet.js deleted file mode 100644 index ea4d8cb221..0000000000 --- a/platform/javascript/js/libs/audio.worklet.js +++ /dev/null @@ -1,211 +0,0 @@ -/*************************************************************************/ -/* audio.worklet.js */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -class RingBuffer { - constructor(p_buffer, p_state, p_threads) { - this.buffer = p_buffer; - this.avail = p_state; - this.threads = p_threads; - this.rpos = 0; - this.wpos = 0; - } - - data_left() { - return this.threads ? Atomics.load(this.avail, 0) : this.avail; - } - - space_left() { - return this.buffer.length - this.data_left(); - } - - read(output) { - const size = this.buffer.length; - let from = 0; - let to_write = output.length; - if (this.rpos + to_write > size) { - const high = size - this.rpos; - output.set(this.buffer.subarray(this.rpos, size)); - from = high; - to_write -= high; - this.rpos = 0; - } - if (to_write) { - output.set(this.buffer.subarray(this.rpos, this.rpos + to_write), from); - } - this.rpos += to_write; - if (this.threads) { - Atomics.add(this.avail, 0, -output.length); - Atomics.notify(this.avail, 0); - } else { - this.avail -= output.length; - } - } - - write(p_buffer) { - const to_write = p_buffer.length; - const mw = this.buffer.length - this.wpos; - if (mw >= to_write) { - this.buffer.set(p_buffer, this.wpos); - this.wpos += to_write; - if (mw === to_write) { - this.wpos = 0; - } - } else { - const high = p_buffer.subarray(0, mw); - const low = p_buffer.subarray(mw); - this.buffer.set(high, this.wpos); - this.buffer.set(low); - this.wpos = low.length; - } - if (this.threads) { - Atomics.add(this.avail, 0, to_write); - Atomics.notify(this.avail, 0); - } else { - this.avail += to_write; - } - } -} - -class GodotProcessor extends AudioWorkletProcessor { - constructor() { - super(); - this.threads = false; - this.running = true; - this.lock = null; - this.notifier = null; - this.output = null; - this.output_buffer = new Float32Array(); - this.input = null; - this.input_buffer = new Float32Array(); - this.port.onmessage = (event) => { - const cmd = event.data['cmd']; - const data = event.data['data']; - this.parse_message(cmd, data); - }; - } - - process_notify() { - if (this.notifier) { - Atomics.add(this.notifier, 0, 1); - Atomics.notify(this.notifier, 0); - } - } - - parse_message(p_cmd, p_data) { - if (p_cmd === 'start' && p_data) { - const state = p_data[0]; - let idx = 0; - this.threads = true; - this.lock = state.subarray(idx, ++idx); - this.notifier = state.subarray(idx, ++idx); - const avail_in = state.subarray(idx, ++idx); - const avail_out = state.subarray(idx, ++idx); - this.input = new RingBuffer(p_data[1], avail_in, true); - this.output = new RingBuffer(p_data[2], avail_out, true); - } else if (p_cmd === 'stop') { - this.running = false; - this.output = null; - this.input = null; - } else if (p_cmd === 'start_nothreads') { - this.output = new RingBuffer(p_data[0], p_data[0].length, false); - } else if (p_cmd === 'chunk') { - this.output.write(p_data); - } - } - - static array_has_data(arr) { - return arr.length && arr[0].length && arr[0][0].length; - } - - process(inputs, outputs, parameters) { - if (!this.running) { - return false; // Stop processing. - } - if (this.output === null) { - return true; // Not ready yet, keep processing. - } - const process_input = GodotProcessor.array_has_data(inputs); - if (process_input) { - const input = inputs[0]; - const chunk = input[0].length * input.length; - if (this.input_buffer.length !== chunk) { - this.input_buffer = new Float32Array(chunk); - } - if (!this.threads) { - GodotProcessor.write_input(this.input_buffer, input); - this.port.postMessage({ 'cmd': 'input', 'data': this.input_buffer }); - } else if (this.input.space_left() >= chunk) { - GodotProcessor.write_input(this.input_buffer, input); - this.input.write(this.input_buffer); - } else { - this.port.postMessage('Input buffer is full! Skipping input frame.'); - } - } - const process_output = GodotProcessor.array_has_data(outputs); - if (process_output) { - const output = outputs[0]; - const chunk = output[0].length * output.length; - if (this.output_buffer.length !== chunk) { - this.output_buffer = new Float32Array(chunk); - } - if (this.output.data_left() >= chunk) { - this.output.read(this.output_buffer); - GodotProcessor.write_output(output, this.output_buffer); - if (!this.threads) { - this.port.postMessage({ 'cmd': 'read', 'data': chunk }); - } - } else { - this.port.postMessage('Output buffer has not enough frames! Skipping output frame.'); - } - } - this.process_notify(); - return true; - } - - static write_output(dest, source) { - const channels = dest.length; - for (let ch = 0; ch < channels; ch++) { - for (let sample = 0; sample < dest[ch].length; sample++) { - dest[ch][sample] = source[sample * channels + ch]; - } - } - } - - static write_input(dest, source) { - const channels = source.length; - for (let ch = 0; ch < channels; ch++) { - for (let sample = 0; sample < source[ch].length; sample++) { - dest[sample * channels + ch] = source[ch][sample]; - } - } - } -} - -registerProcessor('godot-processor', GodotProcessor); diff --git a/platform/javascript/js/libs/library_godot_audio.js b/platform/javascript/js/libs/library_godot_audio.js deleted file mode 100644 index 756c1ac595..0000000000 --- a/platform/javascript/js/libs/library_godot_audio.js +++ /dev/null @@ -1,484 +0,0 @@ -/*************************************************************************/ -/* library_godot_audio.js */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -const GodotAudio = { - $GodotAudio__deps: ['$GodotRuntime', '$GodotOS'], - $GodotAudio: { - ctx: null, - input: null, - driver: null, - interval: 0, - - init: function (mix_rate, latency, onstatechange, onlatencyupdate) { - const opts = {}; - // If mix_rate is 0, let the browser choose. - if (mix_rate) { - opts['sampleRate'] = mix_rate; - } - // Do not specify, leave 'interactive' for good performance. - // opts['latencyHint'] = latency / 1000; - const ctx = new (window.AudioContext || window.webkitAudioContext)(opts); - GodotAudio.ctx = ctx; - ctx.onstatechange = function () { - let state = 0; - switch (ctx.state) { - case 'suspended': - state = 0; - break; - case 'running': - state = 1; - break; - case 'closed': - state = 2; - break; - - // no default - } - onstatechange(state); - }; - ctx.onstatechange(); // Immediately notify state. - // Update computed latency - GodotAudio.interval = setInterval(function () { - let computed_latency = 0; - if (ctx.baseLatency) { - computed_latency += GodotAudio.ctx.baseLatency; - } - if (ctx.outputLatency) { - computed_latency += GodotAudio.ctx.outputLatency; - } - onlatencyupdate(computed_latency); - }, 1000); - GodotOS.atexit(GodotAudio.close_async); - return ctx.destination.channelCount; - }, - - create_input: function (callback) { - if (GodotAudio.input) { - return 0; // Already started. - } - function gotMediaInput(stream) { - try { - GodotAudio.input = GodotAudio.ctx.createMediaStreamSource(stream); - callback(GodotAudio.input); - } catch (e) { - GodotRuntime.error('Failed creaating input.', e); - } - } - if (navigator.mediaDevices && navigator.mediaDevices.getUserMedia) { - navigator.mediaDevices.getUserMedia({ - 'audio': true, - }).then(gotMediaInput, function (e) { - GodotRuntime.error('Error getting user media.', e); - }); - } else { - if (!navigator.getUserMedia) { - navigator.getUserMedia = navigator.webkitGetUserMedia || navigator.mozGetUserMedia; - } - if (!navigator.getUserMedia) { - GodotRuntime.error('getUserMedia not available.'); - return 1; - } - navigator.getUserMedia({ - 'audio': true, - }, gotMediaInput, function (e) { - GodotRuntime.print(e); - }); - } - return 0; - }, - - close_async: function (resolve, reject) { - const ctx = GodotAudio.ctx; - GodotAudio.ctx = null; - // Audio was not initialized. - if (!ctx) { - resolve(); - return; - } - // Remove latency callback - if (GodotAudio.interval) { - clearInterval(GodotAudio.interval); - GodotAudio.interval = 0; - } - // Disconnect input, if it was started. - if (GodotAudio.input) { - GodotAudio.input.disconnect(); - GodotAudio.input = null; - } - // Disconnect output - let closed = Promise.resolve(); - if (GodotAudio.driver) { - closed = GodotAudio.driver.close(); - } - closed.then(function () { - return ctx.close(); - }).then(function () { - ctx.onstatechange = null; - resolve(); - }).catch(function (e) { - ctx.onstatechange = null; - GodotRuntime.error('Error closing AudioContext', e); - resolve(); - }); - }, - }, - - godot_audio_is_available__sig: 'i', - godot_audio_is_available__proxy: 'sync', - godot_audio_is_available: function () { - if (!(window.AudioContext || window.webkitAudioContext)) { - return 0; - } - return 1; - }, - - godot_audio_has_worklet__sig: 'i', - godot_audio_has_worklet: function () { - return (GodotAudio.ctx && GodotAudio.ctx.audioWorklet) ? 1 : 0; - }, - - godot_audio_has_script_processor__sig: 'i', - godot_audio_has_script_processor: function () { - return (GodotAudio.ctx && GodotAudio.ctx.createScriptProcessor) ? 1 : 0; - }, - - godot_audio_init__sig: 'iiiii', - godot_audio_init: function (p_mix_rate, p_latency, p_state_change, p_latency_update) { - const statechange = GodotRuntime.get_func(p_state_change); - const latencyupdate = GodotRuntime.get_func(p_latency_update); - const mix_rate = GodotRuntime.getHeapValue(p_mix_rate, 'i32'); - const channels = GodotAudio.init(mix_rate, p_latency, statechange, latencyupdate); - GodotRuntime.setHeapValue(p_mix_rate, GodotAudio.ctx.sampleRate, 'i32'); - return channels; - }, - - godot_audio_resume__sig: 'v', - godot_audio_resume: function () { - if (GodotAudio.ctx && GodotAudio.ctx.state !== 'running') { - GodotAudio.ctx.resume(); - } - }, - - godot_audio_capture_start__proxy: 'sync', - godot_audio_capture_start__sig: 'i', - godot_audio_capture_start: function () { - return GodotAudio.create_input(function (input) { - input.connect(GodotAudio.driver.get_node()); - }); - }, - - godot_audio_capture_stop__proxy: 'sync', - godot_audio_capture_stop__sig: 'v', - godot_audio_capture_stop: function () { - if (GodotAudio.input) { - const tracks = GodotAudio.input['mediaStream']['getTracks'](); - for (let i = 0; i < tracks.length; i++) { - tracks[i]['stop'](); - } - GodotAudio.input.disconnect(); - GodotAudio.input = null; - } - }, -}; - -autoAddDeps(GodotAudio, '$GodotAudio'); -mergeInto(LibraryManager.library, GodotAudio); - -/** - * The AudioWorklet API driver, used when threads are available. - */ -const GodotAudioWorklet = { - $GodotAudioWorklet__deps: ['$GodotAudio', '$GodotConfig'], - $GodotAudioWorklet: { - promise: null, - worklet: null, - ring_buffer: null, - - create: function (channels) { - const path = GodotConfig.locate_file('godot.audio.worklet.js'); - GodotAudioWorklet.promise = GodotAudio.ctx.audioWorklet.addModule(path).then(function () { - GodotAudioWorklet.worklet = new AudioWorkletNode( - GodotAudio.ctx, - 'godot-processor', - { - 'outputChannelCount': [channels], - } - ); - return Promise.resolve(); - }); - GodotAudio.driver = GodotAudioWorklet; - }, - - start: function (in_buf, out_buf, state) { - GodotAudioWorklet.promise.then(function () { - const node = GodotAudioWorklet.worklet; - node.connect(GodotAudio.ctx.destination); - node.port.postMessage({ - 'cmd': 'start', - 'data': [state, in_buf, out_buf], - }); - node.port.onmessage = function (event) { - GodotRuntime.error(event.data); - }; - }); - }, - - start_no_threads: function (p_out_buf, p_out_size, out_callback, p_in_buf, p_in_size, in_callback) { - function RingBuffer() { - let wpos = 0; - let rpos = 0; - let pending_samples = 0; - const wbuf = new Float32Array(p_out_size); - - function send(port) { - if (pending_samples === 0) { - return; - } - const buffer = GodotRuntime.heapSub(HEAPF32, p_out_buf, p_out_size); - const size = buffer.length; - const tot_sent = pending_samples; - out_callback(wpos, pending_samples); - if (wpos + pending_samples >= size) { - const high = size - wpos; - wbuf.set(buffer.subarray(wpos, size)); - pending_samples -= high; - wpos = 0; - } - if (pending_samples > 0) { - wbuf.set(buffer.subarray(wpos, wpos + pending_samples), tot_sent - pending_samples); - } - port.postMessage({ 'cmd': 'chunk', 'data': wbuf.subarray(0, tot_sent) }); - wpos += pending_samples; - pending_samples = 0; - } - this.receive = function (recv_buf) { - const buffer = GodotRuntime.heapSub(HEAPF32, p_in_buf, p_in_size); - const from = rpos; - let to_write = recv_buf.length; - let high = 0; - if (rpos + to_write >= p_in_size) { - high = p_in_size - rpos; - buffer.set(recv_buf.subarray(0, high), rpos); - to_write -= high; - rpos = 0; - } - if (to_write) { - buffer.set(recv_buf.subarray(high, to_write), rpos); - } - in_callback(from, recv_buf.length); - rpos += to_write; - }; - this.consumed = function (size, port) { - pending_samples += size; - send(port); - }; - } - GodotAudioWorklet.ring_buffer = new RingBuffer(); - GodotAudioWorklet.promise.then(function () { - const node = GodotAudioWorklet.worklet; - const buffer = GodotRuntime.heapSlice(HEAPF32, p_out_buf, p_out_size); - node.connect(GodotAudio.ctx.destination); - node.port.postMessage({ - 'cmd': 'start_nothreads', - 'data': [buffer, p_in_size], - }); - node.port.onmessage = function (event) { - if (!GodotAudioWorklet.worklet) { - return; - } - if (event.data['cmd'] === 'read') { - const read = event.data['data']; - GodotAudioWorklet.ring_buffer.consumed(read, GodotAudioWorklet.worklet.port); - } else if (event.data['cmd'] === 'input') { - const buf = event.data['data']; - if (buf.length > p_in_size) { - GodotRuntime.error('Input chunk is too big'); - return; - } - GodotAudioWorklet.ring_buffer.receive(buf); - } else { - GodotRuntime.error(event.data); - } - }; - }); - }, - - get_node: function () { - return GodotAudioWorklet.worklet; - }, - - close: function () { - return new Promise(function (resolve, reject) { - if (GodotAudioWorklet.promise === null) { - return; - } - GodotAudioWorklet.promise.then(function () { - GodotAudioWorklet.worklet.port.postMessage({ - 'cmd': 'stop', - 'data': null, - }); - GodotAudioWorklet.worklet.disconnect(); - GodotAudioWorklet.worklet = null; - GodotAudioWorklet.promise = null; - resolve(); - }).catch(function (err) { /* aborted? */ }); - }); - }, - }, - - godot_audio_worklet_create__sig: 'ii', - godot_audio_worklet_create: function (channels) { - try { - GodotAudioWorklet.create(channels); - } catch (e) { - GodotRuntime.error('Error starting AudioDriverWorklet', e); - return 1; - } - return 0; - }, - - godot_audio_worklet_start__sig: 'viiiii', - godot_audio_worklet_start: function (p_in_buf, p_in_size, p_out_buf, p_out_size, p_state) { - const out_buffer = GodotRuntime.heapSub(HEAPF32, p_out_buf, p_out_size); - const in_buffer = GodotRuntime.heapSub(HEAPF32, p_in_buf, p_in_size); - const state = GodotRuntime.heapSub(HEAP32, p_state, 4); - GodotAudioWorklet.start(in_buffer, out_buffer, state); - }, - - godot_audio_worklet_start_no_threads__sig: 'viiiiii', - godot_audio_worklet_start_no_threads: function (p_out_buf, p_out_size, p_out_callback, p_in_buf, p_in_size, p_in_callback) { - const out_callback = GodotRuntime.get_func(p_out_callback); - const in_callback = GodotRuntime.get_func(p_in_callback); - GodotAudioWorklet.start_no_threads(p_out_buf, p_out_size, out_callback, p_in_buf, p_in_size, in_callback); - }, - - godot_audio_worklet_state_wait__sig: 'iiii', - godot_audio_worklet_state_wait: function (p_state, p_idx, p_expected, p_timeout) { - Atomics.wait(HEAP32, (p_state >> 2) + p_idx, p_expected, p_timeout); - return Atomics.load(HEAP32, (p_state >> 2) + p_idx); - }, - - godot_audio_worklet_state_add__sig: 'iiii', - godot_audio_worklet_state_add: function (p_state, p_idx, p_value) { - return Atomics.add(HEAP32, (p_state >> 2) + p_idx, p_value); - }, - - godot_audio_worklet_state_get__sig: 'iii', - godot_audio_worklet_state_get: function (p_state, p_idx) { - return Atomics.load(HEAP32, (p_state >> 2) + p_idx); - }, -}; - -autoAddDeps(GodotAudioWorklet, '$GodotAudioWorklet'); -mergeInto(LibraryManager.library, GodotAudioWorklet); - -/* - * The deprecated ScriptProcessorNode API, used when threads are disabled. - */ -const GodotAudioScript = { - $GodotAudioScript__deps: ['$GodotAudio'], - $GodotAudioScript: { - script: null, - - create: function (buffer_length, channel_count) { - GodotAudioScript.script = GodotAudio.ctx.createScriptProcessor(buffer_length, 2, channel_count); - GodotAudio.driver = GodotAudioScript; - return GodotAudioScript.script.bufferSize; - }, - - start: function (p_in_buf, p_in_size, p_out_buf, p_out_size, onprocess) { - GodotAudioScript.script.onaudioprocess = function (event) { - // Read input - const inb = GodotRuntime.heapSub(HEAPF32, p_in_buf, p_in_size); - const input = event.inputBuffer; - if (GodotAudio.input) { - const inlen = input.getChannelData(0).length; - for (let ch = 0; ch < 2; ch++) { - const data = input.getChannelData(ch); - for (let s = 0; s < inlen; s++) { - inb[s * 2 + ch] = data[s]; - } - } - } - - // Let Godot process the input/output. - onprocess(); - - // Write the output. - const outb = GodotRuntime.heapSub(HEAPF32, p_out_buf, p_out_size); - const output = event.outputBuffer; - const channels = output.numberOfChannels; - for (let ch = 0; ch < channels; ch++) { - const data = output.getChannelData(ch); - // Loop through samples and assign computed values. - for (let sample = 0; sample < data.length; sample++) { - data[sample] = outb[sample * channels + ch]; - } - } - }; - GodotAudioScript.script.connect(GodotAudio.ctx.destination); - }, - - get_node: function () { - return GodotAudioScript.script; - }, - - close: function () { - return new Promise(function (resolve, reject) { - GodotAudioScript.script.disconnect(); - GodotAudioScript.script.onaudioprocess = null; - GodotAudioScript.script = null; - resolve(); - }); - }, - }, - - godot_audio_script_create__sig: 'iii', - godot_audio_script_create: function (buffer_length, channel_count) { - const buf_len = GodotRuntime.getHeapValue(buffer_length, 'i32'); - try { - const out_len = GodotAudioScript.create(buf_len, channel_count); - GodotRuntime.setHeapValue(buffer_length, out_len, 'i32'); - } catch (e) { - GodotRuntime.error('Error starting AudioDriverScriptProcessor', e); - return 1; - } - return 0; - }, - - godot_audio_script_start__sig: 'viiiii', - godot_audio_script_start: function (p_in_buf, p_in_size, p_out_buf, p_out_size, p_cb) { - const onprocess = GodotRuntime.get_func(p_cb); - GodotAudioScript.start(p_in_buf, p_in_size, p_out_buf, p_out_size, onprocess); - }, -}; - -autoAddDeps(GodotAudioScript, '$GodotAudioScript'); -mergeInto(LibraryManager.library, GodotAudioScript); diff --git a/platform/javascript/js/libs/library_godot_display.js b/platform/javascript/js/libs/library_godot_display.js deleted file mode 100644 index 768eaf9e1d..0000000000 --- a/platform/javascript/js/libs/library_godot_display.js +++ /dev/null @@ -1,754 +0,0 @@ -/*************************************************************************/ -/* library_godot_display.js */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -const GodotDisplayVK = { - - $GodotDisplayVK__deps: ['$GodotRuntime', '$GodotConfig', '$GodotEventListeners'], - $GodotDisplayVK__postset: 'GodotOS.atexit(function(resolve, reject) { GodotDisplayVK.clear(); resolve(); });', - $GodotDisplayVK: { - textinput: null, - textarea: null, - - available: function () { - return GodotConfig.virtual_keyboard && 'ontouchstart' in window; - }, - - init: function (input_cb) { - function create(what) { - const elem = document.createElement(what); - elem.style.display = 'none'; - elem.style.position = 'absolute'; - elem.style.zIndex = '-1'; - elem.style.background = 'transparent'; - elem.style.padding = '0px'; - elem.style.margin = '0px'; - elem.style.overflow = 'hidden'; - elem.style.width = '0px'; - elem.style.height = '0px'; - elem.style.border = '0px'; - elem.style.outline = 'none'; - elem.readonly = true; - elem.disabled = true; - GodotEventListeners.add(elem, 'input', function (evt) { - const c_str = GodotRuntime.allocString(elem.value); - input_cb(c_str, elem.selectionEnd); - GodotRuntime.free(c_str); - }, false); - GodotEventListeners.add(elem, 'blur', function (evt) { - elem.style.display = 'none'; - elem.readonly = true; - elem.disabled = true; - }, false); - GodotConfig.canvas.insertAdjacentElement('beforebegin', elem); - return elem; - } - GodotDisplayVK.textinput = create('input'); - GodotDisplayVK.textarea = create('textarea'); - GodotDisplayVK.updateSize(); - }, - show: function (text, type, start, end) { - if (!GodotDisplayVK.textinput || !GodotDisplayVK.textarea) { - return; - } - if (GodotDisplayVK.textinput.style.display !== '' || GodotDisplayVK.textarea.style.display !== '') { - GodotDisplayVK.hide(); - } - GodotDisplayVK.updateSize(); - - let elem = GodotDisplayVK.textinput; - switch (type) { - case 0: // KEYBOARD_TYPE_DEFAULT - elem.type = 'text'; - elem.inputmode = ''; - break; - case 1: // KEYBOARD_TYPE_MULTILINE - elem = GodotDisplayVK.textarea; - break; - case 2: // KEYBOARD_TYPE_NUMBER - elem.type = 'text'; - elem.inputmode = 'numeric'; - break; - case 3: // KEYBOARD_TYPE_NUMBER_DECIMAL - elem.type = 'text'; - elem.inputmode = 'decimal'; - break; - case 4: // KEYBOARD_TYPE_PHONE - elem.type = 'tel'; - elem.inputmode = ''; - break; - case 5: // KEYBOARD_TYPE_EMAIL_ADDRESS - elem.type = 'email'; - elem.inputmode = ''; - break; - case 6: // KEYBOARD_TYPE_PASSWORD - elem.type = 'password'; - elem.inputmode = ''; - break; - case 7: // KEYBOARD_TYPE_URL - elem.type = 'url'; - elem.inputmode = ''; - break; - default: - elem.type = 'text'; - elem.inputmode = ''; - break; - } - - elem.readonly = false; - elem.disabled = false; - elem.value = text; - elem.style.display = 'block'; - elem.focus(); - elem.setSelectionRange(start, end); - }, - hide: function () { - if (!GodotDisplayVK.textinput || !GodotDisplayVK.textarea) { - return; - } - [GodotDisplayVK.textinput, GodotDisplayVK.textarea].forEach(function (elem) { - elem.blur(); - elem.style.display = 'none'; - elem.value = ''; - }); - }, - updateSize: function () { - if (!GodotDisplayVK.textinput || !GodotDisplayVK.textarea) { - return; - } - const rect = GodotConfig.canvas.getBoundingClientRect(); - function update(elem) { - elem.style.left = `${rect.left}px`; - elem.style.top = `${rect.top}px`; - elem.style.width = `${rect.width}px`; - elem.style.height = `${rect.height}px`; - } - update(GodotDisplayVK.textinput); - update(GodotDisplayVK.textarea); - }, - clear: function () { - if (GodotDisplayVK.textinput) { - GodotDisplayVK.textinput.remove(); - GodotDisplayVK.textinput = null; - } - if (GodotDisplayVK.textarea) { - GodotDisplayVK.textarea.remove(); - GodotDisplayVK.textarea = null; - } - }, - }, -}; -mergeInto(LibraryManager.library, GodotDisplayVK); - -/* - * Display server cursor helper. - * Keeps track of cursor status and custom shapes. - */ -const GodotDisplayCursor = { - $GodotDisplayCursor__deps: ['$GodotOS', '$GodotConfig'], - $GodotDisplayCursor__postset: 'GodotOS.atexit(function(resolve, reject) { GodotDisplayCursor.clear(); resolve(); });', - $GodotDisplayCursor: { - shape: 'auto', - visible: true, - cursors: {}, - set_style: function (style) { - GodotConfig.canvas.style.cursor = style; - }, - set_shape: function (shape) { - GodotDisplayCursor.shape = shape; - let css = shape; - if (shape in GodotDisplayCursor.cursors) { - const c = GodotDisplayCursor.cursors[shape]; - css = `url("${c.url}") ${c.x} ${c.y}, auto`; - } - if (GodotDisplayCursor.visible) { - GodotDisplayCursor.set_style(css); - } - }, - clear: function () { - GodotDisplayCursor.set_style(''); - GodotDisplayCursor.shape = 'auto'; - GodotDisplayCursor.visible = true; - Object.keys(GodotDisplayCursor.cursors).forEach(function (key) { - URL.revokeObjectURL(GodotDisplayCursor.cursors[key]); - delete GodotDisplayCursor.cursors[key]; - }); - }, - lockPointer: function () { - const canvas = GodotConfig.canvas; - if (canvas.requestPointerLock) { - canvas.requestPointerLock(); - } - }, - releasePointer: function () { - if (document.exitPointerLock) { - document.exitPointerLock(); - } - }, - isPointerLocked: function () { - return document.pointerLockElement === GodotConfig.canvas; - }, - }, -}; -mergeInto(LibraryManager.library, GodotDisplayCursor); - -const GodotDisplayScreen = { - $GodotDisplayScreen__deps: ['$GodotConfig', '$GodotOS', '$GL', 'emscripten_webgl_get_current_context'], - $GodotDisplayScreen: { - desired_size: [0, 0], - hidpi: true, - getPixelRatio: function () { - return GodotDisplayScreen.hidpi ? window.devicePixelRatio || 1 : 1; - }, - isFullscreen: function () { - const elem = document.fullscreenElement || document.mozFullscreenElement - || document.webkitFullscreenElement || document.msFullscreenElement; - if (elem) { - return elem === GodotConfig.canvas; - } - // But maybe knowing the element is not supported. - return document.fullscreen || document.mozFullScreen - || document.webkitIsFullscreen; - }, - hasFullscreen: function () { - return document.fullscreenEnabled || document.mozFullScreenEnabled - || document.webkitFullscreenEnabled; - }, - requestFullscreen: function () { - if (!GodotDisplayScreen.hasFullscreen()) { - return 1; - } - const canvas = GodotConfig.canvas; - try { - const promise = (canvas.requestFullscreen || canvas.msRequestFullscreen - || canvas.mozRequestFullScreen || canvas.mozRequestFullscreen - || canvas.webkitRequestFullscreen - ).call(canvas); - // Some browsers (Safari) return undefined. - // For the standard ones, we need to catch it. - if (promise) { - promise.catch(function () { - // nothing to do. - }); - } - } catch (e) { - return 1; - } - return 0; - }, - exitFullscreen: function () { - if (!GodotDisplayScreen.isFullscreen()) { - return 0; - } - try { - const promise = document.exitFullscreen(); - if (promise) { - promise.catch(function () { - // nothing to do. - }); - } - } catch (e) { - return 1; - } - return 0; - }, - _updateGL: function () { - const gl_context_handle = _emscripten_webgl_get_current_context(); // eslint-disable-line no-undef - const gl = GL.getContext(gl_context_handle); - if (gl) { - GL.resizeOffscreenFramebuffer(gl); - } - }, - updateSize: function () { - const isFullscreen = GodotDisplayScreen.isFullscreen(); - const wantsFullWindow = GodotConfig.canvas_resize_policy === 2; - const noResize = GodotConfig.canvas_resize_policy === 0; - const wwidth = GodotDisplayScreen.desired_size[0]; - const wheight = GodotDisplayScreen.desired_size[1]; - const canvas = GodotConfig.canvas; - let width = wwidth; - let height = wheight; - if (noResize) { - // Don't resize canvas, just update GL if needed. - if (canvas.width !== width || canvas.height !== height) { - GodotDisplayScreen.desired_size = [canvas.width, canvas.height]; - GodotDisplayScreen._updateGL(); - return 1; - } - return 0; - } - const scale = GodotDisplayScreen.getPixelRatio(); - if (isFullscreen || wantsFullWindow) { - // We need to match screen size. - width = window.innerWidth * scale; - height = window.innerHeight * scale; - } - const csw = `${width / scale}px`; - const csh = `${height / scale}px`; - if (canvas.style.width !== csw || canvas.style.height !== csh || canvas.width !== width || canvas.height !== height) { - // Size doesn't match. - // Resize canvas, set correct CSS pixel size, update GL. - canvas.width = width; - canvas.height = height; - canvas.style.width = csw; - canvas.style.height = csh; - GodotDisplayScreen._updateGL(); - return 1; - } - return 0; - }, - }, -}; -mergeInto(LibraryManager.library, GodotDisplayScreen); - -/** - * Display server interface. - * - * Exposes all the functions needed by DisplayServer implementation. - */ -const GodotDisplay = { - $GodotDisplay__deps: ['$GodotConfig', '$GodotRuntime', '$GodotDisplayCursor', '$GodotEventListeners', '$GodotDisplayScreen', '$GodotDisplayVK'], - $GodotDisplay: { - window_icon: '', - getDPI: function () { - // devicePixelRatio is given in dppx - // https://drafts.csswg.org/css-values/#resolution - // > due to the 1:96 fixed ratio of CSS *in* to CSS *px*, 1dppx is equivalent to 96dpi. - const dpi = Math.round(window.devicePixelRatio * 96); - return dpi >= 96 ? dpi : 96; - }, - }, - - godot_js_display_is_swap_ok_cancel__sig: 'i', - godot_js_display_is_swap_ok_cancel: function () { - const win = (['Windows', 'Win64', 'Win32', 'WinCE']); - const plat = navigator.platform || ''; - if (win.indexOf(plat) !== -1) { - return 1; - } - 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 - }, - - godot_js_display_screen_dpi_get__sig: 'i', - godot_js_display_screen_dpi_get: function () { - return GodotDisplay.getDPI(); - }, - - godot_js_display_pixel_ratio_get__sig: 'f', - godot_js_display_pixel_ratio_get: function () { - return GodotDisplayScreen.getPixelRatio(); - }, - - godot_js_display_fullscreen_request__sig: 'i', - godot_js_display_fullscreen_request: function () { - return GodotDisplayScreen.requestFullscreen(); - }, - - godot_js_display_fullscreen_exit__sig: 'i', - godot_js_display_fullscreen_exit: function () { - return GodotDisplayScreen.exitFullscreen(); - }, - - godot_js_display_desired_size_set__sig: 'vii', - godot_js_display_desired_size_set: function (width, height) { - GodotDisplayScreen.desired_size = [width, height]; - GodotDisplayScreen.updateSize(); - }, - - godot_js_display_size_update__sig: 'i', - godot_js_display_size_update: function () { - const updated = GodotDisplayScreen.updateSize(); - if (updated) { - GodotDisplayVK.updateSize(); - } - return updated; - }, - - godot_js_display_screen_size_get__sig: 'vii', - godot_js_display_screen_size_get: function (width, height) { - const scale = GodotDisplayScreen.getPixelRatio(); - GodotRuntime.setHeapValue(width, window.screen.width * scale, 'i32'); - 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'); - }, - - godot_js_display_has_webgl__sig: 'ii', - godot_js_display_has_webgl: function (p_version) { - if (p_version !== 1 && p_version !== 2) { - return false; - } - try { - return !!document.createElement('canvas').getContext(p_version === 2 ? 'webgl2' : 'webgl'); - } catch (e) { /* Not available */ } - return false; - }, - - /* - * Canvas - */ - godot_js_display_canvas_focus__sig: 'v', - godot_js_display_canvas_focus: function () { - GodotConfig.canvas.focus(); - }, - - godot_js_display_canvas_is_focused__sig: 'i', - godot_js_display_canvas_is_focused: function () { - return document.activeElement === GodotConfig.canvas; - }, - - /* - * Touchscreen - */ - godot_js_display_touchscreen_is_available__sig: 'i', - godot_js_display_touchscreen_is_available: function () { - return 'ontouchstart' in window; - }, - - /* - * Clipboard - */ - godot_js_display_clipboard_set__sig: 'ii', - godot_js_display_clipboard_set: function (p_text) { - const text = GodotRuntime.parseString(p_text); - if (!navigator.clipboard || !navigator.clipboard.writeText) { - return 1; - } - navigator.clipboard.writeText(text).catch(function (e) { - // Setting OS clipboard is only possible from an input callback. - GodotRuntime.error('Setting OS clipboard is only possible from an input callback for the HTML5 plafrom. Exception:', e); - }); - return 0; - }, - - godot_js_display_clipboard_get__sig: 'ii', - godot_js_display_clipboard_get: function (callback) { - const func = GodotRuntime.get_func(callback); - try { - navigator.clipboard.readText().then(function (result) { - const ptr = GodotRuntime.allocString(result); - func(ptr); - GodotRuntime.free(ptr); - }).catch(function (e) { - // Fail graciously. - }); - } catch (e) { - // Fail graciously. - } - }, - - /* - * Window - */ - godot_js_display_window_title_set__sig: 'vi', - godot_js_display_window_title_set: function (p_data) { - document.title = GodotRuntime.parseString(p_data); - }, - - godot_js_display_window_icon_set__sig: 'vii', - godot_js_display_window_icon_set: function (p_ptr, p_len) { - let 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); - } - const old_icon = GodotDisplay.window_icon; - const png = new Blob([GodotRuntime.heapSlice(HEAPU8, p_ptr, p_len)], { type: 'image/png' }); - GodotDisplay.window_icon = URL.createObjectURL(png); - link.href = GodotDisplay.window_icon; - if (old_icon) { - URL.revokeObjectURL(old_icon); - } - }, - - /* - * Cursor - */ - godot_js_display_cursor_set_visible__sig: 'vi', - godot_js_display_cursor_set_visible: function (p_visible) { - const visible = p_visible !== 0; - if (visible === GodotDisplayCursor.visible) { - return; - } - GodotDisplayCursor.visible = visible; - if (visible) { - GodotDisplayCursor.set_shape(GodotDisplayCursor.shape); - } else { - GodotDisplayCursor.set_style('none'); - } - }, - - godot_js_display_cursor_is_hidden__sig: 'i', - godot_js_display_cursor_is_hidden: function () { - return !GodotDisplayCursor.visible; - }, - - godot_js_display_cursor_set_shape__sig: 'vi', - godot_js_display_cursor_set_shape: function (p_string) { - GodotDisplayCursor.set_shape(GodotRuntime.parseString(p_string)); - }, - - godot_js_display_cursor_set_custom_shape__sig: 'viiiii', - godot_js_display_cursor_set_custom_shape: function (p_shape, p_ptr, p_len, p_hotspot_x, p_hotspot_y) { - const shape = GodotRuntime.parseString(p_shape); - const old_shape = GodotDisplayCursor.cursors[shape]; - if (p_len > 0) { - const png = new Blob([GodotRuntime.heapSlice(HEAPU8, p_ptr, p_len)], { type: 'image/png' }); - const url = URL.createObjectURL(png); - GodotDisplayCursor.cursors[shape] = { - url: url, - x: p_hotspot_x, - y: p_hotspot_y, - }; - } else { - delete GodotDisplayCursor.cursors[shape]; - } - if (shape === GodotDisplayCursor.shape) { - GodotDisplayCursor.set_shape(GodotDisplayCursor.shape); - } - if (old_shape) { - URL.revokeObjectURL(old_shape.url); - } - }, - - godot_js_display_cursor_lock_set__sig: 'vi', - godot_js_display_cursor_lock_set: function (p_lock) { - if (p_lock) { - GodotDisplayCursor.lockPointer(); - } else { - GodotDisplayCursor.releasePointer(); - } - }, - - godot_js_display_cursor_is_locked__sig: 'i', - godot_js_display_cursor_is_locked: function () { - return GodotDisplayCursor.isPointerLocked() ? 1 : 0; - }, - - /* - * Listeners - */ - godot_js_display_fullscreen_cb__sig: 'vi', - godot_js_display_fullscreen_cb: function (callback) { - const canvas = GodotConfig.canvas; - const func = GodotRuntime.get_func(callback); - function change_cb(evt) { - if (evt.target === canvas) { - func(GodotDisplayScreen.isFullscreen()); - } - } - GodotEventListeners.add(document, 'fullscreenchange', change_cb, false); - GodotEventListeners.add(document, 'mozfullscreenchange', change_cb, false); - GodotEventListeners.add(document, 'webkitfullscreenchange', change_cb, false); - }, - - godot_js_display_window_blur_cb__sig: 'vi', - godot_js_display_window_blur_cb: function (callback) { - const func = GodotRuntime.get_func(callback); - GodotEventListeners.add(window, 'blur', function () { - func(); - }, false); - }, - - godot_js_display_notification_cb__sig: 'viiiii', - godot_js_display_notification_cb: function (callback, p_enter, p_exit, p_in, p_out) { - const canvas = GodotConfig.canvas; - const func = GodotRuntime.get_func(callback); - const notif = [p_enter, p_exit, p_in, p_out]; - ['mouseover', 'mouseleave', 'focus', 'blur'].forEach(function (evt_name, idx) { - GodotEventListeners.add(canvas, evt_name, function () { - func(notif[idx]); - }, true); - }); - }, - - godot_js_display_setup_canvas__sig: 'viiii', - godot_js_display_setup_canvas: function (p_width, p_height, p_fullscreen, p_hidpi) { - const canvas = GodotConfig.canvas; - GodotEventListeners.add(canvas, 'contextmenu', function (ev) { - ev.preventDefault(); - }, false); - GodotEventListeners.add(canvas, 'webglcontextlost', function (ev) { - alert('WebGL context lost, please reload the page'); // eslint-disable-line no-alert - ev.preventDefault(); - }, false); - GodotDisplayScreen.hidpi = !!p_hidpi; - switch (GodotConfig.canvas_resize_policy) { - case 0: // None - GodotDisplayScreen.desired_size = [canvas.width, canvas.height]; - break; - case 1: // Project - GodotDisplayScreen.desired_size = [p_width, p_height]; - break; - default: // Full window - // Ensure we display in the right place, the size will be handled by updateSize - canvas.style.position = 'absolute'; - canvas.style.top = 0; - canvas.style.left = 0; - break; - } - GodotDisplayScreen.updateSize(); - if (p_fullscreen) { - GodotDisplayScreen.requestFullscreen(); - } - }, - - /* - * Virtual Keyboard - */ - godot_js_display_vk_show__sig: 'viiii', - godot_js_display_vk_show: function (p_text, p_type, p_start, p_end) { - const text = GodotRuntime.parseString(p_text); - const start = p_start > 0 ? p_start : 0; - const end = p_end > 0 ? p_end : start; - GodotDisplayVK.show(text, p_type, start, end); - }, - - godot_js_display_vk_hide__sig: 'v', - godot_js_display_vk_hide: function () { - GodotDisplayVK.hide(); - }, - - godot_js_display_vk_available__sig: 'i', - godot_js_display_vk_available: function () { - 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); - if (GodotDisplayVK.available()) { - GodotDisplayVK.init(input_cb); - } - }, -}; - -autoAddDeps(GodotDisplay, '$GodotDisplay'); -mergeInto(LibraryManager.library, GodotDisplay); diff --git a/platform/javascript/js/libs/library_godot_fetch.js b/platform/javascript/js/libs/library_godot_fetch.js deleted file mode 100644 index 285e50a035..0000000000 --- a/platform/javascript/js/libs/library_godot_fetch.js +++ /dev/null @@ -1,247 +0,0 @@ -/*************************************************************************/ -/* library_godot_fetch.js */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -const GodotFetch = { - $GodotFetch__deps: ['$IDHandler', '$GodotRuntime'], - $GodotFetch: { - - onread: function (id, result) { - const obj = IDHandler.get(id); - if (!obj) { - return; - } - if (result.value) { - obj.chunks.push(result.value); - } - obj.reading = false; - obj.done = result.done; - }, - - onresponse: function (id, response) { - const obj = IDHandler.get(id); - if (!obj) { - return; - } - let chunked = false; - response.headers.forEach(function (value, header) { - const v = value.toLowerCase().trim(); - const h = header.toLowerCase().trim(); - if (h === 'transfer-encoding' && v === 'chunked') { - chunked = true; - } - }); - obj.status = response.status; - obj.response = response; - obj.reader = response.body.getReader(); - obj.chunked = chunked; - }, - - onerror: function (id, err) { - GodotRuntime.error(err); - const obj = IDHandler.get(id); - if (!obj) { - return; - } - obj.error = err; - }, - - create: function (method, url, headers, body) { - const obj = { - request: null, - response: null, - reader: null, - error: null, - done: false, - reading: false, - status: 0, - chunks: [], - bodySize: -1, - }; - const id = IDHandler.add(obj); - const init = { - method: method, - headers: headers, - body: body, - }; - obj.request = fetch(url, init); - obj.request.then(GodotFetch.onresponse.bind(null, id)).catch(GodotFetch.onerror.bind(null, id)); - return id; - }, - - free: function (id) { - const obj = IDHandler.get(id); - if (!obj) { - return; - } - IDHandler.remove(id); - if (!obj.request) { - return; - } - // Try to abort - obj.request.then(function (response) { - response.abort(); - }).catch(function (e) { /* nothing to do */ }); - }, - - read: function (id) { - const obj = IDHandler.get(id); - if (!obj) { - return; - } - if (obj.reader && !obj.reading) { - if (obj.done) { - obj.reader = null; - return; - } - obj.reading = true; - obj.reader.read().then(GodotFetch.onread.bind(null, id)).catch(GodotFetch.onerror.bind(null, id)); - } - }, - }, - - godot_js_fetch_create__sig: 'iiiiiii', - godot_js_fetch_create: function (p_method, p_url, p_headers, p_headers_size, p_body, p_body_size) { - const method = GodotRuntime.parseString(p_method); - const url = GodotRuntime.parseString(p_url); - const headers = GodotRuntime.parseStringArray(p_headers, p_headers_size); - const body = p_body_size ? GodotRuntime.heapSlice(HEAP8, p_body, p_body_size) : null; - return GodotFetch.create(method, url, headers.map(function (hv) { - const idx = hv.indexOf(':'); - if (idx <= 0) { - return []; - } - return [ - hv.slice(0, idx).trim(), - hv.slice(idx + 1).trim(), - ]; - }).filter(function (v) { - return v.length === 2; - }), body); - }, - - godot_js_fetch_state_get__sig: 'ii', - godot_js_fetch_state_get: function (p_id) { - const obj = IDHandler.get(p_id); - if (!obj) { - return -1; - } - if (obj.error) { - return -1; - } - if (!obj.response) { - return 0; - } - if (obj.reader) { - return 1; - } - if (obj.done) { - return 2; - } - return -1; - }, - - godot_js_fetch_http_status_get__sig: 'ii', - godot_js_fetch_http_status_get: function (p_id) { - const obj = IDHandler.get(p_id); - if (!obj || !obj.response) { - return 0; - } - return obj.status; - }, - - godot_js_fetch_read_headers__sig: 'iiii', - godot_js_fetch_read_headers: function (p_id, p_parse_cb, p_ref) { - const obj = IDHandler.get(p_id); - if (!obj || !obj.response) { - return 1; - } - const cb = GodotRuntime.get_func(p_parse_cb); - const arr = []; - obj.response.headers.forEach(function (v, h) { - arr.push(`${h}:${v}`); - }); - const c_ptr = GodotRuntime.allocStringArray(arr); - cb(arr.length, c_ptr, p_ref); - GodotRuntime.freeStringArray(c_ptr, arr.length); - return 0; - }, - - godot_js_fetch_read_chunk__sig: 'iiii', - godot_js_fetch_read_chunk: function (p_id, p_buf, p_buf_size) { - const obj = IDHandler.get(p_id); - if (!obj || !obj.response) { - return 0; - } - let to_read = p_buf_size; - const chunks = obj.chunks; - while (to_read && chunks.length) { - const chunk = obj.chunks[0]; - if (chunk.length > to_read) { - GodotRuntime.heapCopy(HEAP8, chunk.slice(0, to_read), p_buf); - chunks[0] = chunk.slice(to_read); - to_read = 0; - } else { - GodotRuntime.heapCopy(HEAP8, chunk, p_buf); - to_read -= chunk.length; - chunks.pop(); - } - } - if (!chunks.length) { - GodotFetch.read(p_id); - } - return p_buf_size - to_read; - }, - - godot_js_fetch_body_length_get__sig: 'ii', - godot_js_fetch_body_length_get: function (p_id) { - const obj = IDHandler.get(p_id); - if (!obj || !obj.response) { - return -1; - } - return obj.bodySize; - }, - - godot_js_fetch_is_chunked__sig: 'ii', - godot_js_fetch_is_chunked: function (p_id) { - const obj = IDHandler.get(p_id); - if (!obj || !obj.response) { - return -1; - } - return obj.chunked ? 1 : 0; - }, - - godot_js_fetch_free__sig: 'vi', - godot_js_fetch_free: function (id) { - GodotFetch.free(id); - }, -}; - -autoAddDeps(GodotFetch, '$GodotFetch'); -mergeInto(LibraryManager.library, GodotFetch); diff --git a/platform/javascript/js/libs/library_godot_input.js b/platform/javascript/js/libs/library_godot_input.js deleted file mode 100644 index 51571d64a2..0000000000 --- a/platform/javascript/js/libs/library_godot_input.js +++ /dev/null @@ -1,549 +0,0 @@ -/*************************************************************************/ -/* library_godot_input.js */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -/* - * Gamepad API helper. - */ -const GodotInputGamepads = { - $GodotInputGamepads__deps: ['$GodotRuntime', '$GodotEventListeners'], - $GodotInputGamepads: { - samples: [], - - get_pads: function () { - try { - // Will throw in iframe when permission is denied. - // Will throw/warn in the future for insecure contexts. - // See https://github.com/w3c/gamepad/pull/120 - const pads = navigator.getGamepads(); - if (pads) { - return pads; - } - return []; - } catch (e) { - return []; - } - }, - - get_samples: function () { - return GodotInputGamepads.samples; - }, - - get_sample: function (index) { - const samples = GodotInputGamepads.samples; - return index < samples.length ? samples[index] : null; - }, - - sample: function () { - const pads = GodotInputGamepads.get_pads(); - const samples = []; - for (let i = 0; i < pads.length; i++) { - const pad = pads[i]; - if (!pad) { - samples.push(null); - continue; - } - const s = { - standard: pad.mapping === 'standard', - buttons: [], - axes: [], - connected: pad.connected, - }; - for (let b = 0; b < pad.buttons.length; b++) { - s.buttons.push(pad.buttons[b].value); - } - for (let a = 0; a < pad.axes.length; a++) { - s.axes.push(pad.axes[a]); - } - samples.push(s); - } - GodotInputGamepads.samples = samples; - }, - - init: function (onchange) { - GodotInputGamepads.samples = []; - function add(pad) { - const guid = GodotInputGamepads.get_guid(pad); - const c_id = GodotRuntime.allocString(pad.id); - const c_guid = GodotRuntime.allocString(guid); - onchange(pad.index, 1, c_id, c_guid); - GodotRuntime.free(c_id); - GodotRuntime.free(c_guid); - } - const pads = GodotInputGamepads.get_pads(); - for (let i = 0; i < pads.length; i++) { - // Might be reserved space. - if (pads[i]) { - add(pads[i]); - } - } - GodotEventListeners.add(window, 'gamepadconnected', function (evt) { - if (evt.gamepad) { - add(evt.gamepad); - } - }, false); - GodotEventListeners.add(window, 'gamepaddisconnected', function (evt) { - if (evt.gamepad) { - onchange(evt.gamepad.index, 0); - } - }, false); - }, - - get_guid: function (pad) { - if (pad.mapping) { - return pad.mapping; - } - const ua = navigator.userAgent; - let os = 'Unknown'; - if (ua.indexOf('Android') >= 0) { - os = 'Android'; - } else if (ua.indexOf('Linux') >= 0) { - os = 'Linux'; - } else if (ua.indexOf('iPhone') >= 0) { - os = 'iOS'; - } else if (ua.indexOf('Macintosh') >= 0) { - // Updated iPads will fall into this category. - os = 'MacOSX'; - } else if (ua.indexOf('Windows') >= 0) { - os = 'Windows'; - } - - const id = pad.id; - // Chrom* style: NAME (Vendor: xxxx Product: xxxx) - const exp1 = /vendor: ([0-9a-f]{4}) product: ([0-9a-f]{4})/i; - // Firefox/Safari style (safari may remove leading zeores) - const exp2 = /^([0-9a-f]+)-([0-9a-f]+)-/i; - let vendor = ''; - let product = ''; - if (exp1.test(id)) { - const match = exp1.exec(id); - vendor = match[1].padStart(4, '0'); - product = match[2].padStart(4, '0'); - } else if (exp2.test(id)) { - const match = exp2.exec(id); - vendor = match[1].padStart(4, '0'); - product = match[2].padStart(4, '0'); - } - if (!vendor || !product) { - return `${os}Unknown`; - } - return os + vendor + product; - }, - }, -}; -mergeInto(LibraryManager.library, GodotInputGamepads); - -/* - * Drag and drop helper. - * This is pretty big, but basically detect dropped files on GodotConfig.canvas, - * process them one by one (recursively for directories), and copies them to - * the temporary FS path '/tmp/drop-[random]/' so it can be emitted as a godot - * event (that requires a string array of paths). - * - * NOTE: The temporary files are removed after the callback. This means that - * deferred callbacks won't be able to access the files. - */ -const GodotInputDragDrop = { - $GodotInputDragDrop__deps: ['$FS', '$GodotFS'], - $GodotInputDragDrop: { - promises: [], - pending_files: [], - - add_entry: function (entry) { - if (entry.isDirectory) { - GodotInputDragDrop.add_dir(entry); - } else if (entry.isFile) { - GodotInputDragDrop.add_file(entry); - } else { - GodotRuntime.error('Unrecognized entry...', entry); - } - }, - - add_dir: function (entry) { - GodotInputDragDrop.promises.push(new Promise(function (resolve, reject) { - const reader = entry.createReader(); - reader.readEntries(function (entries) { - for (let i = 0; i < entries.length; i++) { - GodotInputDragDrop.add_entry(entries[i]); - } - resolve(); - }); - })); - }, - - add_file: function (entry) { - GodotInputDragDrop.promises.push(new Promise(function (resolve, reject) { - entry.file(function (file) { - const reader = new FileReader(); - reader.onload = function () { - const f = { - 'path': file.relativePath || file.webkitRelativePath, - 'name': file.name, - 'type': file.type, - 'size': file.size, - 'data': reader.result, - }; - if (!f['path']) { - f['path'] = f['name']; - } - GodotInputDragDrop.pending_files.push(f); - resolve(); - }; - reader.onerror = function () { - GodotRuntime.print('Error reading file'); - reject(); - }; - reader.readAsArrayBuffer(file); - }, function (err) { - GodotRuntime.print('Error!'); - reject(); - }); - })); - }, - - process: function (resolve, reject) { - if (GodotInputDragDrop.promises.length === 0) { - resolve(); - return; - } - GodotInputDragDrop.promises.pop().then(function () { - setTimeout(function () { - GodotInputDragDrop.process(resolve, reject); - }, 0); - }); - }, - - _process_event: function (ev, callback) { - ev.preventDefault(); - if (ev.dataTransfer.items) { - // Use DataTransferItemList interface to access the file(s) - for (let i = 0; i < ev.dataTransfer.items.length; i++) { - const item = ev.dataTransfer.items[i]; - let entry = null; - if ('getAsEntry' in item) { - entry = item.getAsEntry(); - } else if ('webkitGetAsEntry' in item) { - entry = item.webkitGetAsEntry(); - } - if (entry) { - GodotInputDragDrop.add_entry(entry); - } - } - } else { - GodotRuntime.error('File upload not supported'); - } - new Promise(GodotInputDragDrop.process).then(function () { - const DROP = `/tmp/drop-${parseInt(Math.random() * (1 << 30), 10)}/`; - const drops = []; - const files = []; - FS.mkdir(DROP.slice(0, -1)); // Without trailing slash - GodotInputDragDrop.pending_files.forEach((elem) => { - const path = elem['path']; - GodotFS.copy_to_fs(DROP + path, elem['data']); - let idx = path.indexOf('/'); - if (idx === -1) { - // Root file - drops.push(DROP + path); - } else { - // Subdir - const sub = path.substr(0, idx); - idx = sub.indexOf('/'); - if (idx < 0 && drops.indexOf(DROP + sub) === -1) { - drops.push(DROP + sub); - } - } - files.push(DROP + path); - }); - GodotInputDragDrop.promises = []; - GodotInputDragDrop.pending_files = []; - callback(drops); - if (GodotConfig.persistent_drops) { - // Delay removal at exit. - GodotOS.atexit(function (resolve, reject) { - GodotInputDragDrop.remove_drop(files, DROP); - resolve(); - }); - } else { - GodotInputDragDrop.remove_drop(files, DROP); - } - }); - }, - - remove_drop: function (files, drop_path) { - const dirs = [drop_path.substr(0, drop_path.length - 1)]; - // Remove temporary files - files.forEach(function (file) { - FS.unlink(file); - let dir = file.replace(drop_path, ''); - let idx = dir.lastIndexOf('/'); - while (idx > 0) { - dir = dir.substr(0, idx); - if (dirs.indexOf(drop_path + dir) === -1) { - dirs.push(drop_path + dir); - } - idx = dir.lastIndexOf('/'); - } - }); - // Remove dirs. - dirs.sort(function (a, b) { - const al = (a.match(/\//g) || []).length; - const bl = (b.match(/\//g) || []).length; - if (al > bl) { - return -1; - } else if (al < bl) { - return 1; - } - return 0; - }).forEach(function (dir) { - FS.rmdir(dir); - }); - }, - - handler: function (callback) { - return function (ev) { - GodotInputDragDrop._process_event(ev, callback); - }; - }, - }, -}; -mergeInto(LibraryManager.library, GodotInputDragDrop); - -/* - * Godot exposed input functions. - */ -const GodotInput = { - $GodotInput__deps: ['$GodotRuntime', '$GodotConfig', '$GodotEventListeners', '$GodotInputGamepads', '$GodotInputDragDrop'], - $GodotInput: { - getModifiers: function (evt) { - return (evt.shiftKey + 0) + ((evt.altKey + 0) << 1) + ((evt.ctrlKey + 0) << 2) + ((evt.metaKey + 0) << 3); - }, - computePosition: function (evt, rect) { - const canvas = GodotConfig.canvas; - const rw = canvas.width / rect.width; - const rh = canvas.height / rect.height; - const x = (evt.clientX - rect.x) * rw; - const y = (evt.clientY - rect.y) * rh; - return [x, y]; - }, - }, - - /* - * Mouse API - */ - godot_js_input_mouse_move_cb__sig: 'vi', - godot_js_input_mouse_move_cb: function (callback) { - const func = GodotRuntime.get_func(callback); - const canvas = GodotConfig.canvas; - function move_cb(evt) { - const rect = canvas.getBoundingClientRect(); - const pos = GodotInput.computePosition(evt, rect); - // Scale movement - const rw = canvas.width / rect.width; - const rh = canvas.height / rect.height; - const rel_pos_x = evt.movementX * rw; - const rel_pos_y = evt.movementY * rh; - const modifiers = GodotInput.getModifiers(evt); - func(pos[0], pos[1], rel_pos_x, rel_pos_y, modifiers); - } - GodotEventListeners.add(window, 'mousemove', move_cb, false); - }, - - godot_js_input_mouse_wheel_cb__sig: 'vi', - godot_js_input_mouse_wheel_cb: function (callback) { - const func = GodotRuntime.get_func(callback); - function wheel_cb(evt) { - if (func(evt['deltaX'] || 0, evt['deltaY'] || 0)) { - evt.preventDefault(); - } - } - GodotEventListeners.add(GodotConfig.canvas, 'wheel', wheel_cb, false); - }, - - godot_js_input_mouse_button_cb__sig: 'vi', - godot_js_input_mouse_button_cb: function (callback) { - const func = GodotRuntime.get_func(callback); - const canvas = GodotConfig.canvas; - function button_cb(p_pressed, evt) { - const rect = canvas.getBoundingClientRect(); - const pos = GodotInput.computePosition(evt, rect); - const modifiers = GodotInput.getModifiers(evt); - // Since the event is consumed, focus manually. - // NOTE: The iframe container may not have focus yet, so focus even when already active. - if (p_pressed) { - GodotConfig.canvas.focus(); - } - if (func(p_pressed, evt.button, pos[0], pos[1], modifiers)) { - evt.preventDefault(); - } - } - GodotEventListeners.add(canvas, 'mousedown', button_cb.bind(null, 1), false); - GodotEventListeners.add(window, 'mouseup', button_cb.bind(null, 0), false); - }, - - /* - * Touch API - */ - godot_js_input_touch_cb__sig: 'viii', - godot_js_input_touch_cb: function (callback, ids, coords) { - const func = GodotRuntime.get_func(callback); - const canvas = GodotConfig.canvas; - function touch_cb(type, evt) { - // Since the event is consumed, focus manually. - // NOTE: The iframe container may not have focus yet, so focus even when already active. - if (type === 0) { - GodotConfig.canvas.focus(); - } - const rect = canvas.getBoundingClientRect(); - const touches = evt.changedTouches; - for (let i = 0; i < touches.length; i++) { - const touch = touches[i]; - const pos = GodotInput.computePosition(touch, rect); - GodotRuntime.setHeapValue(coords + (i * 2) * 8, pos[0], 'double'); - GodotRuntime.setHeapValue(coords + (i * 2 + 1) * 8, pos[1], 'double'); - GodotRuntime.setHeapValue(ids + i * 4, touch.identifier, 'i32'); - } - func(type, touches.length); - if (evt.cancelable) { - evt.preventDefault(); - } - } - GodotEventListeners.add(canvas, 'touchstart', touch_cb.bind(null, 0), false); - GodotEventListeners.add(canvas, 'touchend', touch_cb.bind(null, 1), false); - GodotEventListeners.add(canvas, 'touchcancel', touch_cb.bind(null, 1), false); - GodotEventListeners.add(canvas, 'touchmove', touch_cb.bind(null, 2), false); - }, - - /* - * Key API - */ - godot_js_input_key_cb__sig: 'viii', - godot_js_input_key_cb: function (callback, code, key) { - const func = GodotRuntime.get_func(callback); - function key_cb(pressed, evt) { - const modifiers = GodotInput.getModifiers(evt); - GodotRuntime.stringToHeap(evt.code, code, 32); - GodotRuntime.stringToHeap(evt.key, key, 32); - func(pressed, evt.repeat, modifiers); - evt.preventDefault(); - } - GodotEventListeners.add(GodotConfig.canvas, 'keydown', key_cb.bind(null, 1), false); - GodotEventListeners.add(GodotConfig.canvas, 'keyup', key_cb.bind(null, 0), false); - }, - - /* - * Gamepad API - */ - godot_js_input_gamepad_cb__sig: 'vi', - godot_js_input_gamepad_cb: function (change_cb) { - const onchange = GodotRuntime.get_func(change_cb); - GodotInputGamepads.init(onchange); - }, - - godot_js_input_gamepad_sample_count__sig: 'i', - godot_js_input_gamepad_sample_count: function () { - return GodotInputGamepads.get_samples().length; - }, - - godot_js_input_gamepad_sample__sig: 'i', - godot_js_input_gamepad_sample: function () { - GodotInputGamepads.sample(); - return 0; - }, - - godot_js_input_gamepad_sample_get__sig: 'iiiiiii', - godot_js_input_gamepad_sample_get: function (p_index, r_btns, r_btns_num, r_axes, r_axes_num, r_standard) { - const sample = GodotInputGamepads.get_sample(p_index); - if (!sample || !sample.connected) { - return 1; - } - const btns = sample.buttons; - const btns_len = btns.length < 16 ? btns.length : 16; - for (let i = 0; i < btns_len; i++) { - GodotRuntime.setHeapValue(r_btns + (i << 2), btns[i], 'float'); - } - GodotRuntime.setHeapValue(r_btns_num, btns_len, 'i32'); - const axes = sample.axes; - const axes_len = axes.length < 10 ? axes.length : 10; - for (let i = 0; i < axes_len; i++) { - GodotRuntime.setHeapValue(r_axes + (i << 2), axes[i], 'float'); - } - GodotRuntime.setHeapValue(r_axes_num, axes_len, 'i32'); - const is_standard = sample.standard ? 1 : 0; - GodotRuntime.setHeapValue(r_standard, is_standard, 'i32'); - return 0; - }, - - /* - * Drag/Drop API - */ - godot_js_input_drop_files_cb__sig: 'vi', - godot_js_input_drop_files_cb: function (callback) { - const func = GodotRuntime.get_func(callback); - const dropFiles = function (files) { - const args = files || []; - if (!args.length) { - return; - } - const argc = args.length; - const argv = GodotRuntime.allocStringArray(args); - func(argv, argc); - GodotRuntime.freeStringArray(argv, argc); - }; - const canvas = GodotConfig.canvas; - GodotEventListeners.add(canvas, 'dragover', function (ev) { - // Prevent default behavior (which would try to open the file(s)) - ev.preventDefault(); - }, false); - GodotEventListeners.add(canvas, 'drop', GodotInputDragDrop.handler(dropFiles)); - }, - - /* Paste API */ - godot_js_input_paste_cb__sig: 'vi', - godot_js_input_paste_cb: function (callback) { - const func = GodotRuntime.get_func(callback); - GodotEventListeners.add(window, 'paste', function (evt) { - const text = evt.clipboardData.getData('text'); - const ptr = GodotRuntime.allocString(text); - func(ptr); - GodotRuntime.free(ptr); - }, false); - }, - - godot_js_input_vibrate_handheld__sig: 'vi', - godot_js_input_vibrate_handheld: function (p_duration_ms) { - if (typeof navigator.vibrate !== 'function') { - GodotRuntime.print('This browser does not support vibration.'); - } else { - navigator.vibrate(p_duration_ms); - } - }, -}; - -autoAddDeps(GodotInput, '$GodotInput'); -mergeInto(LibraryManager.library, GodotInput); diff --git a/platform/javascript/js/libs/library_godot_javascript_singleton.js b/platform/javascript/js/libs/library_godot_javascript_singleton.js deleted file mode 100644 index 692f27676a..0000000000 --- a/platform/javascript/js/libs/library_godot_javascript_singleton.js +++ /dev/null @@ -1,346 +0,0 @@ -/*************************************************************************/ -/* library_godot_eval.js */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -const GodotJSWrapper = { - - $GodotJSWrapper__deps: ['$GodotRuntime', '$IDHandler'], - $GodotJSWrapper__postset: 'GodotJSWrapper.proxies = new Map();', - $GodotJSWrapper: { - proxies: null, - cb_ret: null, - - MyProxy: function (val) { - const id = IDHandler.add(this); - GodotJSWrapper.proxies.set(val, id); - let refs = 1; - this.ref = function () { - refs++; - }; - this.unref = function () { - refs--; - if (refs === 0) { - IDHandler.remove(id); - GodotJSWrapper.proxies.delete(val); - } - }; - this.get_val = function () { - return val; - }; - this.get_id = function () { - return id; - }; - }, - - get_proxied: function (val) { - const id = GodotJSWrapper.proxies.get(val); - if (id === undefined) { - const proxy = new GodotJSWrapper.MyProxy(val); - return proxy.get_id(); - } - IDHandler.get(id).ref(); - return id; - }, - - get_proxied_value: function (id) { - const proxy = IDHandler.get(id); - if (proxy === undefined) { - return undefined; - } - return proxy.get_val(); - }, - - variant2js: function (type, val) { - switch (type) { - case 0: - return null; - case 1: - return !!GodotRuntime.getHeapValue(val, 'i64'); - case 2: - return GodotRuntime.getHeapValue(val, 'i64'); - case 3: - return GodotRuntime.getHeapValue(val, 'double'); - case 4: - return GodotRuntime.parseString(GodotRuntime.getHeapValue(val, '*')); - case 21: // OBJECT - return GodotJSWrapper.get_proxied_value(GodotRuntime.getHeapValue(val, 'i64')); - default: - return undefined; - } - }, - - js2variant: function (p_val, p_exchange) { - if (p_val === undefined || p_val === null) { - return 0; // NIL - } - const type = typeof (p_val); - if (type === 'boolean') { - GodotRuntime.setHeapValue(p_exchange, p_val, 'i64'); - return 1; // BOOL - } else if (type === 'number') { - if (Number.isInteger(p_val)) { - GodotRuntime.setHeapValue(p_exchange, p_val, 'i64'); - return 2; // INT - } - GodotRuntime.setHeapValue(p_exchange, p_val, 'double'); - return 3; // REAL - } else if (type === 'string') { - const c_str = GodotRuntime.allocString(p_val); - GodotRuntime.setHeapValue(p_exchange, c_str, '*'); - return 4; // STRING - } - const id = GodotJSWrapper.get_proxied(p_val); - GodotRuntime.setHeapValue(p_exchange, id, 'i64'); - return 21; - }, - }, - - godot_js_wrapper_interface_get__sig: 'ii', - godot_js_wrapper_interface_get: function (p_name) { - const name = GodotRuntime.parseString(p_name); - if (typeof (window[name]) !== 'undefined') { - return GodotJSWrapper.get_proxied(window[name]); - } - return 0; - }, - - godot_js_wrapper_object_get__sig: 'iiii', - godot_js_wrapper_object_get: function (p_id, p_exchange, p_prop) { - const obj = GodotJSWrapper.get_proxied_value(p_id); - if (obj === undefined) { - return 0; - } - if (p_prop) { - const prop = GodotRuntime.parseString(p_prop); - try { - return GodotJSWrapper.js2variant(obj[prop], p_exchange); - } catch (e) { - GodotRuntime.error(`Error getting variable ${prop} on object`, obj); - return 0; // NIL - } - } - return GodotJSWrapper.js2variant(obj, p_exchange); - }, - - godot_js_wrapper_object_set__sig: 'viiii', - godot_js_wrapper_object_set: function (p_id, p_name, p_type, p_exchange) { - const obj = GodotJSWrapper.get_proxied_value(p_id); - if (obj === undefined) { - return; - } - const name = GodotRuntime.parseString(p_name); - try { - obj[name] = GodotJSWrapper.variant2js(p_type, p_exchange); - } catch (e) { - GodotRuntime.error(`Error setting variable ${name} on object`, obj); - } - }, - - godot_js_wrapper_object_call__sig: 'iiiiiiiii', - godot_js_wrapper_object_call: function (p_id, p_method, p_args, p_argc, p_convert_callback, p_exchange, p_lock, p_free_lock_callback) { - const obj = GodotJSWrapper.get_proxied_value(p_id); - if (obj === undefined) { - return -1; - } - const method = GodotRuntime.parseString(p_method); - const convert = GodotRuntime.get_func(p_convert_callback); - const freeLock = GodotRuntime.get_func(p_free_lock_callback); - const args = new Array(p_argc); - for (let i = 0; i < p_argc; i++) { - const type = convert(p_args, i, p_exchange, p_lock); - const lock = GodotRuntime.getHeapValue(p_lock, '*'); - args[i] = GodotJSWrapper.variant2js(type, p_exchange); - if (lock) { - freeLock(p_lock, type); - } - } - try { - const res = obj[method](...args); - return GodotJSWrapper.js2variant(res, p_exchange); - } catch (e) { - GodotRuntime.error(`Error calling method ${method} on:`, obj, 'error:', e); - return -1; - } - }, - - godot_js_wrapper_object_unref__sig: 'vi', - godot_js_wrapper_object_unref: function (p_id) { - const proxy = IDHandler.get(p_id); - if (proxy !== undefined) { - proxy.unref(); - } - }, - - godot_js_wrapper_create_cb__sig: 'iii', - godot_js_wrapper_create_cb: function (p_ref, p_func) { - const func = GodotRuntime.get_func(p_func); - let id = 0; - const cb = function () { - if (!GodotJSWrapper.get_proxied_value(id)) { - return undefined; - } - // The callback will store the returned value in this variable via - // "godot_js_wrapper_object_set_cb_ret" upon calling the user function. - // This is safe! JavaScript is single threaded (and using it in threads is not a good idea anyway). - GodotJSWrapper.cb_ret = null; - const args = Array.from(arguments); - func(p_ref, GodotJSWrapper.get_proxied(args), args.length); - const ret = GodotJSWrapper.cb_ret; - GodotJSWrapper.cb_ret = null; - return ret; - }; - id = GodotJSWrapper.get_proxied(cb); - return id; - }, - - godot_js_wrapper_object_set_cb_ret__sig: 'vii', - godot_js_wrapper_object_set_cb_ret: function (p_val_type, p_val_ex) { - GodotJSWrapper.cb_ret = GodotJSWrapper.variant2js(p_val_type, p_val_ex); - }, - - godot_js_wrapper_object_getvar__sig: 'iiii', - godot_js_wrapper_object_getvar: function (p_id, p_type, p_exchange) { - const obj = GodotJSWrapper.get_proxied_value(p_id); - if (obj === undefined) { - return -1; - } - const prop = GodotJSWrapper.variant2js(p_type, p_exchange); - if (prop === undefined || prop === null) { - return -1; - } - try { - return GodotJSWrapper.js2variant(obj[prop], p_exchange); - } catch (e) { - GodotRuntime.error(`Error getting variable ${prop} on object`, obj, e); - return -1; - } - }, - - godot_js_wrapper_object_setvar__sig: 'iiiiii', - godot_js_wrapper_object_setvar: function (p_id, p_key_type, p_key_ex, p_val_type, p_val_ex) { - const obj = GodotJSWrapper.get_proxied_value(p_id); - if (obj === undefined) { - return -1; - } - const key = GodotJSWrapper.variant2js(p_key_type, p_key_ex); - try { - obj[key] = GodotJSWrapper.variant2js(p_val_type, p_val_ex); - return 0; - } catch (e) { - GodotRuntime.error(`Error setting variable ${key} on object`, obj); - return -1; - } - }, - - godot_js_wrapper_create_object__sig: 'iiiiiiii', - godot_js_wrapper_create_object: function (p_object, p_args, p_argc, p_convert_callback, p_exchange, p_lock, p_free_lock_callback) { - const name = GodotRuntime.parseString(p_object); - if (typeof (window[name]) === 'undefined') { - return -1; - } - const convert = GodotRuntime.get_func(p_convert_callback); - const freeLock = GodotRuntime.get_func(p_free_lock_callback); - const args = new Array(p_argc); - for (let i = 0; i < p_argc; i++) { - const type = convert(p_args, i, p_exchange, p_lock); - const lock = GodotRuntime.getHeapValue(p_lock, '*'); - args[i] = GodotJSWrapper.variant2js(type, p_exchange); - if (lock) { - freeLock(p_lock, type); - } - } - try { - const res = new window[name](...args); - return GodotJSWrapper.js2variant(res, p_exchange); - } catch (e) { - GodotRuntime.error(`Error calling constructor ${name} with args:`, args, 'error:', e); - return -1; - } - }, -}; - -autoAddDeps(GodotJSWrapper, '$GodotJSWrapper'); -mergeInto(LibraryManager.library, GodotJSWrapper); - -const GodotEval = { - godot_js_eval__deps: ['$GodotRuntime'], - godot_js_eval__sig: 'iiiiiii', - godot_js_eval: function (p_js, p_use_global_ctx, p_union_ptr, p_byte_arr, p_byte_arr_write, p_callback) { - const js_code = GodotRuntime.parseString(p_js); - let eval_ret = null; - try { - if (p_use_global_ctx) { - // indirect eval call grants global execution context - const global_eval = eval; // eslint-disable-line no-eval - eval_ret = global_eval(js_code); - } else { - eval_ret = eval(js_code); // eslint-disable-line no-eval - } - } catch (e) { - GodotRuntime.error(e); - } - - switch (typeof eval_ret) { - case 'boolean': - GodotRuntime.setHeapValue(p_union_ptr, eval_ret, 'i32'); - return 1; // BOOL - - case 'number': - GodotRuntime.setHeapValue(p_union_ptr, eval_ret, 'double'); - return 3; // REAL - - case 'string': - GodotRuntime.setHeapValue(p_union_ptr, GodotRuntime.allocString(eval_ret), '*'); - return 4; // STRING - - case 'object': - if (eval_ret === null) { - break; - } - - 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) { - const func = GodotRuntime.get_func(p_callback); - const bytes_ptr = func(p_byte_arr, p_byte_arr_write, eval_ret.length); - HEAPU8.set(eval_ret, bytes_ptr); - return 20; // POOL_BYTE_ARRAY - } - break; - - // no default - } - return 0; // NIL - }, -}; - -mergeInto(LibraryManager.library, GodotEval); diff --git a/platform/javascript/js/libs/library_godot_os.js b/platform/javascript/js/libs/library_godot_os.js deleted file mode 100644 index 377eec3234..0000000000 --- a/platform/javascript/js/libs/library_godot_os.js +++ /dev/null @@ -1,427 +0,0 @@ -/*************************************************************************/ -/* library_godot_os.js */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -const IDHandler = { - $IDHandler: { - _last_id: 0, - _references: {}, - - get: function (p_id) { - return IDHandler._references[p_id]; - }, - - add: function (p_data) { - const id = ++IDHandler._last_id; - IDHandler._references[id] = p_data; - return id; - }, - - remove: function (p_id) { - delete IDHandler._references[p_id]; - }, - }, -}; - -autoAddDeps(IDHandler, '$IDHandler'); -mergeInto(LibraryManager.library, IDHandler); - -const GodotConfig = { - $GodotConfig__postset: 'Module["initConfig"] = GodotConfig.init_config;', - $GodotConfig__deps: ['$GodotRuntime'], - $GodotConfig: { - canvas: null, - locale: 'en', - canvas_resize_policy: 2, // Adaptive - virtual_keyboard: false, - persistent_drops: false, - on_execute: null, - on_exit: null, - - init_config: function (p_opts) { - GodotConfig.canvas_resize_policy = p_opts['canvasResizePolicy']; - GodotConfig.canvas = p_opts['canvas']; - GodotConfig.locale = p_opts['locale'] || GodotConfig.locale; - GodotConfig.virtual_keyboard = p_opts['virtualKeyboard']; - GodotConfig.persistent_drops = !!p_opts['persistentDrops']; - GodotConfig.on_execute = p_opts['onExecute']; - GodotConfig.on_exit = p_opts['onExit']; - if (p_opts['focusCanvas']) { - GodotConfig.canvas.focus(); - } - }, - - locate_file: function (file) { - return Module['locateFile'](file); // eslint-disable-line no-undef - }, - clear: function () { - GodotConfig.canvas = null; - GodotConfig.locale = 'en'; - GodotConfig.canvas_resize_policy = 2; - GodotConfig.virtual_keyboard = false; - GodotConfig.persistent_drops = false; - GodotConfig.on_execute = null; - GodotConfig.on_exit = null; - }, - }, - - godot_js_config_canvas_id_get__sig: 'vii', - godot_js_config_canvas_id_get: function (p_ptr, p_ptr_max) { - GodotRuntime.stringToHeap(`#${GodotConfig.canvas.id}`, p_ptr, p_ptr_max); - }, - - godot_js_config_locale_get__sig: 'vii', - godot_js_config_locale_get: function (p_ptr, p_ptr_max) { - GodotRuntime.stringToHeap(GodotConfig.locale, p_ptr, p_ptr_max); - }, -}; - -autoAddDeps(GodotConfig, '$GodotConfig'); -mergeInto(LibraryManager.library, GodotConfig); - -const GodotFS = { - $GodotFS__deps: ['$ERRNO_CODES', '$FS', '$IDBFS', '$GodotRuntime'], - $GodotFS__postset: [ - 'Module["initFS"] = GodotFS.init;', - 'Module["copyToFS"] = GodotFS.copy_to_fs;', - ].join(''), - $GodotFS: { - _idbfs: false, - _syncing: false, - _mount_points: [], - - is_persistent: function () { - return GodotFS._idbfs ? 1 : 0; - }, - - // Initialize godot file system, setting up persistent paths. - // Returns a promise that resolves when the FS is ready. - // We keep track of mount_points, so that we can properly close the IDBFS - // since emscripten is not doing it by itself. (emscripten GH#12516). - init: function (persistentPaths) { - GodotFS._idbfs = false; - if (!Array.isArray(persistentPaths)) { - return Promise.reject(new Error('Persistent paths must be an array')); - } - if (!persistentPaths.length) { - return Promise.resolve(); - } - GodotFS._mount_points = persistentPaths.slice(); - - function createRecursive(dir) { - try { - FS.stat(dir); - } catch (e) { - if (e.errno !== ERRNO_CODES.ENOENT) { - throw e; - } - FS.mkdirTree(dir); - } - } - - GodotFS._mount_points.forEach(function (path) { - createRecursive(path); - FS.mount(IDBFS, {}, path); - }); - return new Promise(function (resolve, reject) { - FS.syncfs(true, function (err) { - if (err) { - GodotFS._mount_points = []; - GodotFS._idbfs = false; - GodotRuntime.print(`IndexedDB not available: ${err.message}`); - } else { - GodotFS._idbfs = true; - } - resolve(err); - }); - }); - }, - - // Deinit godot file system, making sure to unmount file systems, and close IDBFS(s). - deinit: function () { - GodotFS._mount_points.forEach(function (path) { - try { - FS.unmount(path); - } catch (e) { - GodotRuntime.print('Already unmounted', e); - } - if (GodotFS._idbfs && IDBFS.dbs[path]) { - IDBFS.dbs[path].close(); - delete IDBFS.dbs[path]; - } - }); - GodotFS._mount_points = []; - GodotFS._idbfs = false; - GodotFS._syncing = false; - }, - - sync: function () { - if (GodotFS._syncing) { - GodotRuntime.error('Already syncing!'); - return Promise.resolve(); - } - GodotFS._syncing = true; - return new Promise(function (resolve, reject) { - FS.syncfs(false, function (error) { - if (error) { - GodotRuntime.error(`Failed to save IDB file system: ${error.message}`); - } - GodotFS._syncing = false; - resolve(error); - }); - }); - }, - - // Copies a buffer to the internal file system. Creating directories recursively. - copy_to_fs: function (path, buffer) { - const idx = path.lastIndexOf('/'); - let dir = '/'; - if (idx > 0) { - dir = path.slice(0, idx); - } - try { - FS.stat(dir); - } catch (e) { - if (e.errno !== ERRNO_CODES.ENOENT) { - throw e; - } - FS.mkdirTree(dir); - } - FS.writeFile(path, new Uint8Array(buffer)); - }, - }, -}; -mergeInto(LibraryManager.library, GodotFS); - -const GodotOS = { - $GodotOS__deps: ['$GodotRuntime', '$GodotConfig', '$GodotFS'], - $GodotOS__postset: [ - 'Module["request_quit"] = function() { GodotOS.request_quit() };', - 'Module["onExit"] = GodotOS.cleanup;', - 'GodotOS._fs_sync_promise = Promise.resolve();', - ].join(''), - $GodotOS: { - request_quit: function () {}, - _async_cbs: [], - _fs_sync_promise: null, - - atexit: function (p_promise_cb) { - GodotOS._async_cbs.push(p_promise_cb); - }, - - cleanup: function (exit_code) { - const cb = GodotConfig.on_exit; - GodotFS.deinit(); - GodotConfig.clear(); - if (cb) { - cb(exit_code); - } - }, - - finish_async: function (callback) { - GodotOS._fs_sync_promise.then(function (err) { - const promises = []; - GodotOS._async_cbs.forEach(function (cb) { - promises.push(new Promise(cb)); - }); - return Promise.all(promises); - }).then(function () { - return GodotFS.sync(); // Final FS sync. - }).then(function (err) { - // Always deferred. - setTimeout(function () { - callback(); - }, 0); - }); - }, - }, - - godot_js_os_finish_async__sig: 'vi', - godot_js_os_finish_async: function (p_callback) { - const func = GodotRuntime.get_func(p_callback); - GodotOS.finish_async(func); - }, - - godot_js_os_request_quit_cb__sig: 'vi', - godot_js_os_request_quit_cb: function (p_callback) { - GodotOS.request_quit = GodotRuntime.get_func(p_callback); - }, - - godot_js_os_fs_is_persistent__sig: 'i', - godot_js_os_fs_is_persistent: function () { - return GodotFS.is_persistent(); - }, - - godot_js_os_fs_sync__sig: 'vi', - godot_js_os_fs_sync: function (callback) { - const func = GodotRuntime.get_func(callback); - GodotOS._fs_sync_promise = GodotFS.sync(); - GodotOS._fs_sync_promise.then(function (err) { - func(); - }); - }, - - godot_js_os_execute__sig: 'ii', - godot_js_os_execute: function (p_json) { - const json_args = GodotRuntime.parseString(p_json); - const args = JSON.parse(json_args); - if (GodotConfig.on_execute) { - GodotConfig.on_execute(args); - return 0; - } - return 1; - }, - - godot_js_os_shell_open__sig: 'vi', - godot_js_os_shell_open: function (p_uri) { - window.open(GodotRuntime.parseString(p_uri), '_blank'); - }, - - godot_js_os_hw_concurrency_get__sig: 'i', - godot_js_os_hw_concurrency_get: function () { - // 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', - godot_js_os_download_buffer: function (p_ptr, p_size, p_name, p_mime) { - const buf = GodotRuntime.heapSlice(HEAP8, p_ptr, p_size); - const name = GodotRuntime.parseString(p_name); - const mime = GodotRuntime.parseString(p_mime); - const blob = new Blob([buf], { type: mime }); - const url = window.URL.createObjectURL(blob); - const a = document.createElement('a'); - a.href = url; - a.download = name; - a.style.display = 'none'; - document.body.appendChild(a); - a.click(); - a.remove(); - window.URL.revokeObjectURL(url); - }, -}; - -autoAddDeps(GodotOS, '$GodotOS'); -mergeInto(LibraryManager.library, GodotOS); - -/* - * Godot event listeners. - * Keeps track of registered event listeners so it can remove them on shutdown. - */ -const GodotEventListeners = { - $GodotEventListeners__deps: ['$GodotOS'], - $GodotEventListeners__postset: 'GodotOS.atexit(function(resolve, reject) { GodotEventListeners.clear(); resolve(); });', - $GodotEventListeners: { - handlers: [], - - has: function (target, event, method, capture) { - return GodotEventListeners.handlers.findIndex(function (e) { - return e.target === target && e.event === event && e.method === method && e.capture === capture; - }) !== -1; - }, - - add: function (target, event, method, capture) { - if (GodotEventListeners.has(target, event, method, capture)) { - return; - } - function Handler(p_target, p_event, p_method, p_capture) { - this.target = p_target; - this.event = p_event; - this.method = p_method; - this.capture = p_capture; - } - GodotEventListeners.handlers.push(new Handler(target, event, method, capture)); - target.addEventListener(event, method, capture); - }, - - clear: function () { - GodotEventListeners.handlers.forEach(function (h) { - h.target.removeEventListener(h.event, h.method, h.capture); - }); - GodotEventListeners.handlers.length = 0; - }, - }, -}; -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); diff --git a/platform/javascript/js/libs/library_godot_runtime.js b/platform/javascript/js/libs/library_godot_runtime.js deleted file mode 100644 index e2f7c8dca6..0000000000 --- a/platform/javascript/js/libs/library_godot_runtime.js +++ /dev/null @@ -1,134 +0,0 @@ -/*************************************************************************/ -/* library_godot_runtime.js */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -const GodotRuntime = { - $GodotRuntime: { - /* - * Functions - */ - get_func: function (ptr) { - return wasmTable.get(ptr); // eslint-disable-line no-undef - }, - - /* - * Prints - */ - error: function () { - err.apply(null, Array.from(arguments)); // eslint-disable-line no-undef - }, - - print: function () { - out.apply(null, Array.from(arguments)); // eslint-disable-line no-undef - }, - - /* - * Memory - */ - malloc: function (p_size) { - return _malloc(p_size); // eslint-disable-line no-undef - }, - - free: function (p_ptr) { - _free(p_ptr); // eslint-disable-line no-undef - }, - - getHeapValue: function (p_ptr, p_type) { - return getValue(p_ptr, p_type); // eslint-disable-line no-undef - }, - - setHeapValue: function (p_ptr, p_value, p_type) { - setValue(p_ptr, p_value, p_type); // eslint-disable-line no-undef - }, - - heapSub: function (p_heap, p_ptr, p_len) { - const bytes = p_heap.BYTES_PER_ELEMENT; - return p_heap.subarray(p_ptr / bytes, p_ptr / bytes + p_len); - }, - - heapSlice: function (p_heap, p_ptr, p_len) { - const bytes = p_heap.BYTES_PER_ELEMENT; - return p_heap.slice(p_ptr / bytes, p_ptr / bytes + p_len); - }, - - heapCopy: function (p_dst, p_src, p_ptr) { - const bytes = p_src.BYTES_PER_ELEMENT; - return p_dst.set(p_src, p_ptr / bytes); - }, - - /* - * Strings - */ - parseString: function (p_ptr) { - return UTF8ToString(p_ptr); // eslint-disable-line no-undef - }, - - parseStringArray: function (p_ptr, p_size) { - const strings = []; - const ptrs = GodotRuntime.heapSub(HEAP32, p_ptr, p_size); // TODO wasm64 - ptrs.forEach(function (ptr) { - strings.push(GodotRuntime.parseString(ptr)); - }); - return strings; - }, - - strlen: function (p_str) { - return lengthBytesUTF8(p_str); // eslint-disable-line no-undef - }, - - allocString: function (p_str) { - const length = GodotRuntime.strlen(p_str) + 1; - const c_str = GodotRuntime.malloc(length); - stringToUTF8(p_str, c_str, length); // eslint-disable-line no-undef - return c_str; - }, - - allocStringArray: function (p_strings) { - const size = p_strings.length; - const c_ptr = GodotRuntime.malloc(size * 4); - for (let i = 0; i < size; i++) { - HEAP32[(c_ptr >> 2) + i] = GodotRuntime.allocString(p_strings[i]); - } - return c_ptr; - }, - - freeStringArray: function (p_ptr, p_len) { - for (let i = 0; i < p_len; i++) { - GodotRuntime.free(HEAP32[(p_ptr >> 2) + i]); - } - GodotRuntime.free(p_ptr); - }, - - stringToHeap: function (p_str, p_ptr, p_len) { - return stringToUTF8Array(p_str, HEAP8, p_ptr, p_len); // eslint-disable-line no-undef - }, - }, -}; -autoAddDeps(GodotRuntime, '$GodotRuntime'); -mergeInto(LibraryManager.library, GodotRuntime); |