diff --git a/docs/udesigner.data b/docs/udesigner.data index e597bcf..2aaa5d9 100644 Binary files a/docs/udesigner.data and b/docs/udesigner.data differ diff --git a/docs/udesigner.js b/docs/udesigner.js index c199eb9..77afec9 100644 --- a/docs/udesigner.js +++ b/docs/udesigner.js @@ -19,16 +19,12 @@ var Module = typeof Module != 'undefined' ? Module : {}; // Attempt to auto-detect the environment var ENVIRONMENT_IS_WEB = typeof window == 'object'; -var ENVIRONMENT_IS_WORKER = typeof importScripts == 'function'; +var ENVIRONMENT_IS_WORKER = typeof WorkerGlobalScope != 'undefined'; // N.b. Electron.js environment is simultaneously a NODE-environment, but // also a web environment. -var ENVIRONMENT_IS_NODE = typeof process == 'object' && typeof process.versions == 'object' && typeof process.versions.node == 'string'; +var ENVIRONMENT_IS_NODE = typeof process == 'object' && typeof process.versions == 'object' && typeof process.versions.node == 'string' && process.type != 'renderer'; var ENVIRONMENT_IS_SHELL = !ENVIRONMENT_IS_WEB && !ENVIRONMENT_IS_NODE && !ENVIRONMENT_IS_WORKER; -if (Module['ENVIRONMENT']) { - throw new Error('Module.ENVIRONMENT has been deprecated. To force the environment, use the ENVIRONMENT compile-time option (for example, -sENVIRONMENT=web or -sENVIRONMENT=node)'); -} - if (ENVIRONMENT_IS_NODE) { // `require()` is no-op in an ESM module, use `createRequire()` to construct // the require()` function. This is only necessary for multi-environment @@ -39,39 +35,33 @@ if (ENVIRONMENT_IS_NODE) { // --pre-jses are emitted after the Module integration code, so that they can // refer to Module (if they choose; they can also define Module) -// include: /tmp/tmp_90qdbgt.js +// include: /tmp/tmp7icpanro.js - if (!Module.expectedDataFileDownloads) { - Module.expectedDataFileDownloads = 0; - } - - Module.expectedDataFileDownloads++; + Module['expectedDataFileDownloads'] ??= 0; + Module['expectedDataFileDownloads']++; (() => { // Do not attempt to redownload the virtual filesystem data when in a pthread or a Wasm Worker context. var isPthread = typeof ENVIRONMENT_IS_PTHREAD != 'undefined' && ENVIRONMENT_IS_PTHREAD; var isWasmWorker = typeof ENVIRONMENT_IS_WASM_WORKER != 'undefined' && ENVIRONMENT_IS_WASM_WORKER; if (isPthread || isWasmWorker) return; + var isNode = typeof process === 'object' && typeof process.versions === 'object' && typeof process.versions.node === 'string'; function loadPackage(metadata) { var PACKAGE_PATH = ''; if (typeof window === 'object') { - PACKAGE_PATH = window['encodeURIComponent'](window.location.pathname.toString().substring(0, window.location.pathname.toString().lastIndexOf('/')) + '/'); + PACKAGE_PATH = window['encodeURIComponent'](window.location.pathname.substring(0, window.location.pathname.lastIndexOf('/')) + '/'); } else if (typeof process === 'undefined' && typeof location !== 'undefined') { // web worker - PACKAGE_PATH = encodeURIComponent(location.pathname.toString().substring(0, location.pathname.toString().lastIndexOf('/')) + '/'); + PACKAGE_PATH = encodeURIComponent(location.pathname.substring(0, location.pathname.lastIndexOf('/')) + '/'); } var PACKAGE_NAME = '../../docs/udesigner.data'; var REMOTE_PACKAGE_BASE = 'udesigner.data'; - if (typeof Module['locateFilePackage'] === 'function' && !Module['locateFile']) { - Module['locateFile'] = Module['locateFilePackage']; - err('warning: you defined Module.locateFilePackage, that has been renamed to Module.locateFile (using your locateFilePackage for now)'); - } var REMOTE_PACKAGE_NAME = Module['locateFile'] ? Module['locateFile'](REMOTE_PACKAGE_BASE, '') : REMOTE_PACKAGE_BASE; var REMOTE_PACKAGE_SIZE = metadata['remote_package_size']; function fetchRemotePackage(packageName, packageSize, callback, errback) { - if (typeof process === 'object' && typeof process.versions === 'object' && typeof process.versions.node === 'string') { - require('fs').readFile(packageName, function(err, contents) { + if (isNode) { + require('fs').readFile(packageName, (err, contents) => { if (err) { errback(err); } else { @@ -80,51 +70,58 @@ var REMOTE_PACKAGE_SIZE = metadata['remote_package_size']; }); return; } - var xhr = new XMLHttpRequest(); - xhr.open('GET', packageName, true); - xhr.responseType = 'arraybuffer'; - xhr.onprogress = function(event) { - var url = packageName; - var size = packageSize; - if (event.total) size = event.total; - if (event.loaded) { - if (!xhr.addedTotal) { - xhr.addedTotal = true; - if (!Module.dataFileDownloads) Module.dataFileDownloads = {}; - Module.dataFileDownloads[url] = { - loaded: event.loaded, - total: size - }; - } else { - Module.dataFileDownloads[url].loaded = event.loaded; + Module['dataFileDownloads'] ??= {}; + fetch(packageName) + .catch((cause) => Promise.reject(new Error(`Network Error: ${packageName}`, {cause}))) // If fetch fails, rewrite the error to include the failing URL & the cause. + .then((response) => { + if (!response.ok) { + return Promise.reject(new Error(`${response.status}: ${response.url}`)); } - var total = 0; - var loaded = 0; - var num = 0; - for (var download in Module.dataFileDownloads) { - var data = Module.dataFileDownloads[download]; - total += data.total; - loaded += data.loaded; - num++; + + if (!response.body && response.arrayBuffer) { // If we're using the polyfill, readers won't be available... + return response.arrayBuffer().then(callback); } - total = Math.ceil(total * Module.expectedDataFileDownloads/num); - if (Module['setStatus']) Module['setStatus'](`Downloading data... (${loaded}/${total})`); - } else if (!Module.dataFileDownloads) { - if (Module['setStatus']) Module['setStatus']('Downloading data...'); - } - }; - xhr.onerror = function(event) { - throw new Error("NetworkError for: " + packageName); - } - xhr.onload = function(event) { - if (xhr.status == 200 || xhr.status == 304 || xhr.status == 206 || (xhr.status == 0 && xhr.response)) { // file URLs can return 0 - var packageData = xhr.response; - callback(packageData); - } else { - throw new Error(xhr.statusText + " : " + xhr.responseURL); - } - }; - xhr.send(null); + + const reader = response.body.getReader(); + const iterate = () => reader.read().then(handleChunk).catch((cause) => { + return Promise.reject(new Error(`Unexpected error while handling : ${response.url} ${cause}`, {cause})); + }); + + const chunks = []; + const headers = response.headers; + const total = Number(headers.get('Content-Length') ?? packageSize); + let loaded = 0; + + const handleChunk = ({done, value}) => { + if (!done) { + chunks.push(value); + loaded += value.length; + Module['dataFileDownloads'][packageName] = {loaded, total}; + + let totalLoaded = 0; + let totalSize = 0; + + for (const download of Object.values(Module['dataFileDownloads'])) { + totalLoaded += download.loaded; + totalSize += download.total; + } + + Module['setStatus']?.(`Downloading data... (${totalLoaded}/${totalSize})`); + return iterate(); + } else { + const packageData = new Uint8Array(chunks.map((c) => c.length).reduce((a, b) => a + b, 0)); + let offset = 0; + for (const chunk of chunks) { + packageData.set(chunk, offset); + offset += chunk.length; + } + callback(packageData.buffer); + } + }; + + Module['setStatus']?.('Downloading data...'); + return iterate(); + }); }; function handleError(error) { @@ -134,7 +131,7 @@ var REMOTE_PACKAGE_SIZE = metadata['remote_package_size']; var fetchedCallback = null; var fetched = Module['getPreloadedPackage'] ? Module['getPreloadedPackage'](REMOTE_PACKAGE_NAME, REMOTE_PACKAGE_SIZE) : null; - if (!fetched) fetchRemotePackage(REMOTE_PACKAGE_NAME, REMOTE_PACKAGE_SIZE, function(data) { + if (!fetched) fetchRemotePackage(REMOTE_PACKAGE_NAME, REMOTE_PACKAGE_SIZE, (data) => { if (fetchedCallback) { fetchedCallback(data); fetchedCallback = null; @@ -143,7 +140,7 @@ var REMOTE_PACKAGE_SIZE = metadata['remote_package_size']; } }, handleError); - function runWithFS() { + function runWithFS(Module) { function assert(check, msg) { if (!check) throw msg + new Error().stack; @@ -197,9 +194,9 @@ Module['FS_createPath']("/", "assets", true, true); }; Module['addRunDependency']('datafile_../../docs/udesigner.data'); - if (!Module.preloadResults) Module.preloadResults = {}; + Module['preloadResults'] ??= {}; - Module.preloadResults[PACKAGE_NAME] = {fromCache: false}; + Module['preloadResults'][PACKAGE_NAME] = {fromCache: false}; if (fetched) { processPackageData(fetched); fetched = null; @@ -209,10 +206,9 @@ Module['FS_createPath']("/", "assets", true, true); } if (Module['calledRun']) { - runWithFS(); + runWithFS(Module); } else { - if (!Module['preRun']) Module['preRun'] = []; - Module["preRun"].push(runWithFS); // FS is not initialized yet, wait for it + (Module['preRun'] ??= []).push(runWithFS); // FS is not initialized yet, wait for it } } @@ -220,21 +216,21 @@ Module['FS_createPath']("/", "assets", true, true); })(); -// end include: /tmp/tmp_90qdbgt.js -// include: /tmp/tmpg0ofgdo3.js +// end include: /tmp/tmp7icpanro.js +// include: /tmp/tmp436h17j8.js // All the pre-js content up to here must remain later on, we need to run // it. if (Module['$ww'] || (typeof ENVIRONMENT_IS_PTHREAD != 'undefined' && ENVIRONMENT_IS_PTHREAD)) Module['preRun'] = []; var necessaryPreJSTasks = Module['preRun'].slice(); - // end include: /tmp/tmpg0ofgdo3.js -// include: /tmp/tmpiu8r_zac.js + // end include: /tmp/tmp436h17j8.js +// include: /tmp/tmp8lesez8q.js if (!Module['preRun']) throw 'Module.preRun should exist because file support used it; did a pre-js delete it?'; necessaryPreJSTasks.forEach((task) => { if (Module['preRun'].indexOf(task) < 0) throw 'All preRun tasks that exist before user pre-js code should remain after; did you replace Module or modify Module.preRun?'; }); - // end include: /tmp/tmpiu8r_zac.js + // end include: /tmp/tmp8lesez8q.js // Sometimes an existing Module object exists with properties @@ -260,9 +256,7 @@ function locateFile(path) { } // Hooks that are implemented differently in different runtime environments. -var read_, - readAsync, - readBinary; +var readAsync, readBinary; if (ENVIRONMENT_IS_NODE) { if (typeof process == 'undefined' || !process.release || process.release.name !== 'node') throw new Error('not compiled for this environment (did you build to HTML and try to run it not on the web, or set ENVIRONMENT to something - like node - and run it someplace else - like on the web?)'); @@ -283,28 +277,23 @@ if (ENVIRONMENT_IS_NODE) { scriptDirectory = __dirname + '/'; // include: node_shell_read.js -read_ = (filename, binary) => { +readBinary = (filename) => { // We need to re-wrap `file://` strings to URLs. Normalizing isn't // necessary in that case, the path should already be absolute. filename = isFileURI(filename) ? new URL(filename) : nodePath.normalize(filename); - return fs.readFileSync(filename, binary ? undefined : 'utf8'); -}; - -readBinary = (filename) => { - var ret = read_(filename, true); - if (!ret.buffer) { - ret = new Uint8Array(ret); - } + var ret = fs.readFileSync(filename); assert(ret.buffer); return ret; }; -readAsync = (filename, onload, onerror, binary = true) => { - // See the comment in the `read_` function. +readAsync = (filename, binary = true) => { + // See the comment in the `readBinary` function. filename = isFileURI(filename) ? new URL(filename) : nodePath.normalize(filename); - fs.readFile(filename, binary ? undefined : 'utf8', (err, data) => { - if (err) onerror(err); - else onload(binary ? data.buffer : data); + return new Promise((resolve, reject) => { + fs.readFile(filename, binary ? undefined : 'utf8', (err, data) => { + if (err) reject(err); + else resolve(binary ? data.buffer : data); + }); }); }; // end include: node_shell_read.js @@ -318,13 +307,6 @@ readAsync = (filename, onload, onerror, binary = true) => { module['exports'] = Module; } - process.on('uncaughtException', (ex) => { - // suppress ExitStatus exceptions from showing an error - if (ex !== 'unwind' && !(ex instanceof ExitStatus) && !(ex.context instanceof ExitStatus)) { - throw ex; - } - }); - quit_ = (status, toThrow) => { process.exitCode = status; throw toThrow; @@ -333,7 +315,7 @@ readAsync = (filename, onload, onerror, binary = true) => { } else if (ENVIRONMENT_IS_SHELL) { - if ((typeof process == 'object' && typeof require === 'function') || typeof window == 'object' || typeof importScripts == 'function') throw new Error('not compiled for this environment (did you build to HTML and try to run it not on the web, or set ENVIRONMENT to something - like node - and run it someplace else - like on the web?)'); + if ((typeof process == 'object' && typeof require === 'function') || typeof window == 'object' || typeof WorkerGlobalScope != 'undefined') throw new Error('not compiled for this environment (did you build to HTML and try to run it not on the web, or set ENVIRONMENT to something - like node - and run it someplace else - like on the web?)'); } else @@ -358,18 +340,11 @@ if (ENVIRONMENT_IS_WEB || ENVIRONMENT_IS_WORKER) { scriptDirectory = scriptDirectory.substr(0, scriptDirectory.replace(/[?#].*/, '').lastIndexOf('/')+1); } - if (!(typeof window == 'object' || typeof importScripts == 'function')) throw new Error('not compiled for this environment (did you build to HTML and try to run it not on the web, or set ENVIRONMENT to something - like node - and run it someplace else - like on the web?)'); + if (!(typeof window == 'object' || typeof WorkerGlobalScope != 'undefined')) throw new Error('not compiled for this environment (did you build to HTML and try to run it not on the web, or set ENVIRONMENT to something - like node - and run it someplace else - like on the web?)'); { // include: web_or_worker_shell_read.js -read_ = (url) => { - var xhr = new XMLHttpRequest(); - xhr.open('GET', url, false); - xhr.send(null); - return xhr.responseText; - } - - if (ENVIRONMENT_IS_WORKER) { +if (ENVIRONMENT_IS_WORKER) { readBinary = (url) => { var xhr = new XMLHttpRequest(); xhr.open('GET', url, false); @@ -379,34 +354,34 @@ read_ = (url) => { }; } - readAsync = (url, onload, onerror) => { + readAsync = (url) => { // Fetch has some additional restrictions over XHR, like it can't be used on a file:// url. // See https://github.com/github/fetch/pull/92#issuecomment-140665932 // Cordova or Electron apps are typically loaded from a file:// url. // So use XHR on webview if URL is a file URL. if (isFileURI(url)) { - var xhr = new XMLHttpRequest(); - xhr.open('GET', url, true); - xhr.responseType = 'arraybuffer'; - xhr.onload = () => { - if (xhr.status == 200 || (xhr.status == 0 && xhr.response)) { // file URLs can return 0 - onload(xhr.response); - return; - } - onerror(); - }; - xhr.onerror = onerror; - xhr.send(null); - return; + return new Promise((resolve, reject) => { + var xhr = new XMLHttpRequest(); + xhr.open('GET', url, true); + xhr.responseType = 'arraybuffer'; + xhr.onload = () => { + if (xhr.status == 200 || (xhr.status == 0 && xhr.response)) { // file URLs can return 0 + resolve(xhr.response); + return; + } + reject(xhr.status); + }; + xhr.onerror = reject; + xhr.send(null); + }); } - fetch(url, { credentials: 'same-origin' }) - .then((response) => { - if (response.ok) { - return response.arrayBuffer(); - } - return Promise.reject(new Error(response.status + ' : ' + response.url)); - }) - .then(onload, onerror) + return fetch(url, { credentials: 'same-origin' }) + .then((response) => { + if (response.ok) { + return response.arrayBuffer(); + } + return Promise.reject(new Error(response.status + ' : ' + response.url)); + }) }; // end include: web_or_worker_shell_read.js } @@ -434,21 +409,18 @@ if (Module['arguments']) arguments_ = Module['arguments'];legacyModuleProp('argu if (Module['thisProgram']) thisProgram = Module['thisProgram'];legacyModuleProp('thisProgram', 'thisProgram'); -if (Module['quit']) quit_ = Module['quit'];legacyModuleProp('quit', 'quit_'); - // perform assertions in shell.js after we set up out() and err(), as otherwise if an assertion fails it cannot print the message // Assertions on removed incoming Module JS APIs. assert(typeof Module['memoryInitializerPrefixURL'] == 'undefined', 'Module.memoryInitializerPrefixURL option was removed, use Module.locateFile instead'); assert(typeof Module['pthreadMainPrefixURL'] == 'undefined', 'Module.pthreadMainPrefixURL option was removed, use Module.locateFile instead'); assert(typeof Module['cdInitializerPrefixURL'] == 'undefined', 'Module.cdInitializerPrefixURL option was removed, use Module.locateFile instead'); assert(typeof Module['filePackagePrefixURL'] == 'undefined', 'Module.filePackagePrefixURL option was removed, use Module.locateFile instead'); -assert(typeof Module['read'] == 'undefined', 'Module.read option was removed (modify read_ in JS)'); +assert(typeof Module['read'] == 'undefined', 'Module.read option was removed'); assert(typeof Module['readAsync'] == 'undefined', 'Module.readAsync option was removed (modify readAsync in JS)'); assert(typeof Module['readBinary'] == 'undefined', 'Module.readBinary option was removed (modify readBinary in JS)'); assert(typeof Module['setWindowTitle'] == 'undefined', 'Module.setWindowTitle option was removed (modify emscripten_set_window_title in JS)'); assert(typeof Module['TOTAL_MEMORY'] == 'undefined', 'Module.TOTAL_MEMORY has been renamed Module.INITIAL_MEMORY'); legacyModuleProp('asm', 'wasmExports'); -legacyModuleProp('read', 'read_'); legacyModuleProp('readAsync', 'readAsync'); legacyModuleProp('readBinary', 'readBinary'); legacyModuleProp('setWindowTitle', 'setWindowTitle'); @@ -477,8 +449,7 @@ assert(!ENVIRONMENT_IS_SHELL, 'shell environment detected but not enabled at bui // An online HTML version (which may be of a different version of Emscripten) // is up at http://kripken.github.io/emscripten-site/docs/api_reference/preamble.js.html -var wasmBinary; -if (Module['wasmBinary']) wasmBinary = Module['wasmBinary'];legacyModuleProp('wasmBinary', 'wasmBinary'); +var wasmBinary = Module['wasmBinary'];legacyModuleProp('wasmBinary', 'wasmBinary'); if (typeof WebAssembly != 'object') { err('no native wasm support detected'); @@ -573,6 +544,7 @@ function updateMemoryViews() { Module['HEAPF32'] = HEAPF32 = new Float32Array(b); Module['HEAPF64'] = HEAPF64 = new Float64Array(b); } + // end include: runtime_shared.js assert(!Module['STACK_SIZE'], 'STACK_SIZE can no longer be set at runtime. Use -sSTACK_SIZE at link time') @@ -621,16 +593,6 @@ function checkStackCookie() { } } // end include: runtime_stack_check.js -// include: runtime_assertions.js -// Endianness check -(function() { - var h16 = new Int16Array(1); - var h8 = new Int8Array(h16.buffer); - h16[0] = 0x6373; - if (h8[0] !== 0x73 || h8[1] !== 0x63) throw 'Runtime error: expected the system to be little-endian! (Run with -sSUPPORT_BIG_ENDIAN to bypass)'; -})(); - -// end include: runtime_assertions.js var __ATPRERUN__ = []; // functions called before the runtime is initialized var __ATINIT__ = []; // functions called during startup var __ATMAIN__ = []; // functions called when main() is to be run @@ -656,7 +618,7 @@ function initRuntime() { checkStackCookie(); -if (!Module['noFSInit'] && !FS.init.initialized) +if (!Module['noFSInit'] && !FS.initialized) FS.init(); FS.ignorePermissions = false; @@ -804,7 +766,6 @@ function abort(what) { err(what); ABORT = true; - EXITSTATUS = 1; // Use a wasm runtime error, because a JS error might be seen as a foreign // exception, which means we'd run destructors on it. We need the error to @@ -897,15 +858,12 @@ function getBinaryPromise(binaryFile) { // If we don't have the binary yet, load it asynchronously using readAsync. if (!wasmBinary ) { - // Fetch the binary use readAsync - return new Promise((resolve, reject) => { - readAsync(binaryFile, - (response) => resolve(new Uint8Array(/** @type{!ArrayBuffer} */(response))), - (error) => { - try { resolve(getBinarySync(binaryFile)); } - catch (e) { reject(e); } - }); - }); + // Fetch the binary using readAsync + return readAsync(binaryFile).then( + (response) => new Uint8Array(/** @type{!ArrayBuffer} */(response)), + // Fall back to getBinarySync if readAsync fails + () => getBinarySync(binaryFile) + ); } // Otherwise, getBinarySync should be able to get it synchronously @@ -1030,7 +988,7 @@ function createWasm() { } } - if (!wasmBinaryFile) wasmBinaryFile = findWasmBinary(); + wasmBinaryFile ??= findWasmBinary(); instantiateAsync(wasmBinary, wasmBinaryFile, info, receiveInstantiationResult); return {}; // no exports yet; we'll fill them in later @@ -1041,6 +999,18 @@ var tempDouble; var tempI64; // include: runtime_debug.js +// Endianness check +(() => { + var h16 = new Int16Array(1); + var h8 = new Int8Array(h16.buffer); + h16[0] = 0x6373; + if (h8[0] !== 0x73 || h8[1] !== 0x63) throw 'Runtime error: expected the system to be little-endian! (Run with -sSUPPORT_BIG_ENDIAN to bypass)'; +})(); + +if (Module['ENVIRONMENT']) { + throw new Error('Module.ENVIRONMENT has been deprecated. To force the environment, use the ENVIRONMENT compile-time option (for example, -sENVIRONMENT=web or -sENVIRONMENT=node)'); +} + function legacyModuleProp(prop, newName, incoming=true) { if (!Object.getOwnPropertyDescriptor(Module, prop)) { Object.defineProperty(Module, prop, { @@ -1073,45 +1043,51 @@ function isExportedByForceFilesystem(name) { name === 'removeRunDependency'; } -function missingGlobal(sym, msg) { - if (typeof globalThis != 'undefined') { +/** + * Intercept access to a global symbol. This enables us to give informative + * warnings/errors when folks attempt to use symbols they did not include in + * their build, or no symbols that no longer exist. + */ +function hookGlobalSymbolAccess(sym, func) { + if (typeof globalThis != 'undefined' && !Object.getOwnPropertyDescriptor(globalThis, sym)) { Object.defineProperty(globalThis, sym, { configurable: true, get() { - warnOnce(`\`${sym}\` is not longer defined by emscripten. ${msg}`); + func(); return undefined; } }); } } +function missingGlobal(sym, msg) { + hookGlobalSymbolAccess(sym, () => { + warnOnce(`\`${sym}\` is not longer defined by emscripten. ${msg}`); + }); +} + missingGlobal('buffer', 'Please use HEAP8.buffer or wasmMemory.buffer'); missingGlobal('asm', 'Please use wasmExports instead'); function missingLibrarySymbol(sym) { - if (typeof globalThis != 'undefined' && !Object.getOwnPropertyDescriptor(globalThis, sym)) { - Object.defineProperty(globalThis, sym, { - configurable: true, - get() { - // Can't `abort()` here because it would break code that does runtime - // checks. e.g. `if (typeof SDL === 'undefined')`. - var msg = `\`${sym}\` is a library symbol and not included by default; add it to your library.js __deps or to DEFAULT_LIBRARY_FUNCS_TO_INCLUDE on the command line`; - // DEFAULT_LIBRARY_FUNCS_TO_INCLUDE requires the name as it appears in - // library.js, which means $name for a JS name with no prefix, or name - // for a JS name like _name. - var librarySymbol = sym; - if (!librarySymbol.startsWith('_')) { - librarySymbol = '$' + sym; - } - msg += ` (e.g. -sDEFAULT_LIBRARY_FUNCS_TO_INCLUDE='${librarySymbol}')`; - if (isExportedByForceFilesystem(sym)) { - msg += '. Alternatively, forcing filesystem support (-sFORCE_FILESYSTEM) can export this for you'; - } - warnOnce(msg); - return undefined; - } - }); - } + hookGlobalSymbolAccess(sym, () => { + // Can't `abort()` here because it would break code that does runtime + // checks. e.g. `if (typeof SDL === 'undefined')`. + var msg = `\`${sym}\` is a library symbol and not included by default; add it to your library.js __deps or to DEFAULT_LIBRARY_FUNCS_TO_INCLUDE on the command line`; + // DEFAULT_LIBRARY_FUNCS_TO_INCLUDE requires the name as it appears in + // library.js, which means $name for a JS name with no prefix, or name + // for a JS name like _name. + var librarySymbol = sym; + if (!librarySymbol.startsWith('_')) { + librarySymbol = '$' + sym; + } + msg += ` (e.g. -sDEFAULT_LIBRARY_FUNCS_TO_INCLUDE='${librarySymbol}')`; + if (isExportedByForceFilesystem(sym)) { + msg += '. Alternatively, forcing filesystem support (-sFORCE_FILESYSTEM) can export this for you'; + } + warnOnce(msg); + }); + // Any symbol that is not included from the JS library is also (by definition) // not exported on the Module object. unexportedRuntimeSymbol(sym); @@ -1139,7 +1115,7 @@ function dbg(...args) { console.warn(...args); } // end include: runtime_debug.js -var compilerSettings = {"ASSERTIONS":1,"STACK_OVERFLOW_CHECK":1,"CHECK_NULL_WRITES":true,"VERBOSE":false,"INVOKE_RUN":true,"EXIT_RUNTIME":false,"STACK_SIZE":65536,"MALLOC":"dlmalloc","ABORTING_MALLOC":0,"INITIAL_HEAP":16777216,"INITIAL_MEMORY":-1,"MAXIMUM_MEMORY":2147483648,"ALLOW_MEMORY_GROWTH":1,"MEMORY_GROWTH_GEOMETRIC_STEP":0.2,"MEMORY_GROWTH_GEOMETRIC_CAP":100663296,"MEMORY_GROWTH_LINEAR_STEP":-1,"MEMORY64":0,"INITIAL_TABLE":-1,"ALLOW_TABLE_GROWTH":false,"GLOBAL_BASE":1024,"TABLE_BASE":1,"USE_CLOSURE_COMPILER":false,"CLOSURE_WARNINGS":"quiet","IGNORE_CLOSURE_COMPILER_ERRORS":false,"DECLARE_ASM_MODULE_EXPORTS":true,"INLINING_LIMIT":false,"SUPPORT_BIG_ENDIAN":false,"SAFE_HEAP":0,"SAFE_HEAP_LOG":false,"EMULATE_FUNCTION_POINTER_CASTS":false,"EXCEPTION_DEBUG":false,"DEMANGLE_SUPPORT":false,"LIBRARY_DEBUG":false,"SYSCALL_DEBUG":false,"SOCKET_DEBUG":false,"DYLINK_DEBUG":0,"FS_DEBUG":false,"SOCKET_WEBRTC":false,"WEBSOCKET_URL":"ws:#","PROXY_POSIX_SOCKETS":false,"WEBSOCKET_SUBPROTOCOL":"binary","OPENAL_DEBUG":false,"WEBSOCKET_DEBUG":false,"GL_ASSERTIONS":false,"TRACE_WEBGL_CALLS":false,"GL_DEBUG":false,"GL_TESTING":false,"GL_MAX_TEMP_BUFFER_SIZE":2097152,"GL_UNSAFE_OPTS":true,"FULL_ES2":1,"GL_EMULATE_GLES_VERSION_STRING_FORMAT":true,"GL_EXTENSIONS_IN_PREFIXED_FORMAT":true,"GL_SUPPORT_AUTOMATIC_ENABLE_EXTENSIONS":true,"GL_SUPPORT_SIMPLE_ENABLE_EXTENSIONS":true,"GL_TRACK_ERRORS":true,"GL_SUPPORT_EXPLICIT_SWAP_CONTROL":false,"GL_POOL_TEMP_BUFFERS":true,"GL_EXPLICIT_UNIFORM_LOCATION":false,"GL_EXPLICIT_UNIFORM_BINDING":false,"USE_WEBGL2":1,"MIN_WEBGL_VERSION":1,"MAX_WEBGL_VERSION":2,"WEBGL2_BACKWARDS_COMPATIBILITY_EMULATION":false,"FULL_ES3":1,"LEGACY_GL_EMULATION":false,"GL_FFP_ONLY":false,"GL_PREINITIALIZED_CONTEXT":false,"USE_WEBGPU":false,"STB_IMAGE":false,"GL_DISABLE_HALF_FLOAT_EXTENSION_IF_BROKEN":false,"GL_WORKAROUND_SAFARI_GETCONTEXT_BUG":true,"GL_ENABLE_GET_PROC_ADDRESS":true,"JS_MATH":false,"POLYFILL_OLD_MATH_FUNCTIONS":0,"LEGACY_VM_SUPPORT":false,"ENVIRONMENT":"web,webview,worker,node","LZ4":false,"DISABLE_EXCEPTION_CATCHING":0,"DISABLE_EXCEPTION_THROWING":0,"EXPORT_EXCEPTION_HANDLING_HELPERS":true,"EXCEPTION_STACK_TRACES":1,"WASM_EXNREF":false,"NODEJS_CATCH_EXIT":true,"NODEJS_CATCH_REJECTION":0,"ASYNCIFY":0,"ASYNCIFY_IGNORE_INDIRECT":false,"ASYNCIFY_STACK_SIZE":4096,"ASYNCIFY_PROPAGATE_ADD":true,"ASYNCIFY_ADVISE":false,"ASYNCIFY_LAZY_LOAD_CODE":false,"ASYNCIFY_DEBUG":0,"JSPI":0,"CASE_INSENSITIVE_FS":false,"FILESYSTEM":true,"FORCE_FILESYSTEM":1,"NODERAWFS":false,"NODE_CODE_CACHING":false,"EXPORT_ALL":false,"EXPORT_KEEPALIVE":true,"RETAIN_COMPILER_SETTINGS":1,"INCLUDE_FULL_LIBRARY":false,"RELOCATABLE":false,"MAIN_MODULE":0,"SIDE_MODULE":0,"BUILD_AS_WORKER":false,"PROXY_TO_WORKER":false,"PROXY_TO_WORKER_FILENAME":"","PROXY_TO_PTHREAD":false,"LINKABLE":false,"STRICT":false,"IGNORE_MISSING_MAIN":true,"STRICT_JS":false,"WARN_ON_UNDEFINED_SYMBOLS":true,"ERROR_ON_UNDEFINED_SYMBOLS":true,"SMALL_XHR_CHUNKS":false,"HEADLESS":false,"DETERMINISTIC":false,"MODULARIZE":false,"EXPORT_ES6":false,"USE_ES6_IMPORT_META":true,"EXPORT_NAME":"Module","DYNAMIC_EXECUTION":1,"BOOTSTRAPPING_STRUCT_INFO":false,"EMSCRIPTEN_TRACING":false,"USE_GLFW":3,"WASM":1,"STANDALONE_WASM":false,"BINARYEN_IGNORE_IMPLICIT_TRAPS":false,"BINARYEN_EXTRA_PASSES":"","WASM_ASYNC_COMPILATION":true,"DYNCALLS":false,"WASM_BIGINT":false,"EMIT_PRODUCERS_SECTION":false,"EMIT_EMSCRIPTEN_LICENSE":false,"LEGALIZE_JS_FFI":true,"USE_SDL":0,"USE_SDL_GFX":0,"USE_SDL_IMAGE":1,"USE_SDL_TTF":1,"USE_SDL_NET":1,"USE_ICU":false,"USE_ZLIB":false,"USE_BZIP2":false,"USE_GIFLIB":false,"USE_LIBJPEG":false,"USE_LIBPNG":false,"USE_REGAL":false,"USE_BOOST_HEADERS":false,"USE_BULLET":false,"USE_VORBIS":false,"USE_OGG":false,"USE_MPG123":false,"USE_FREETYPE":false,"USE_SDL_MIXER":1,"USE_HARFBUZZ":false,"USE_COCOS2D":0,"USE_MODPLUG":false,"USE_SQLITE3":false,"SHARED_MEMORY":false,"WASM_WORKERS":0,"AUDIO_WORKLET":0,"WEBAUDIO_DEBUG":0,"PTHREAD_POOL_SIZE":0,"PTHREAD_POOL_SIZE_STRICT":1,"PTHREAD_POOL_DELAY_LOAD":false,"DEFAULT_PTHREAD_STACK_SIZE":0,"PTHREADS_PROFILING":false,"ALLOW_BLOCKING_ON_MAIN_THREAD":true,"PTHREADS_DEBUG":false,"EVAL_CTORS":0,"TEXTDECODER":1,"EMBIND_STD_STRING_IS_UTF8":true,"EMBIND_AOT":false,"OFFSCREENCANVAS_SUPPORT":false,"OFFSCREENCANVASES_TO_PTHREAD":"#canvas","OFFSCREEN_FRAMEBUFFER":false,"FETCH_SUPPORT_INDEXEDDB":true,"FETCH_DEBUG":false,"FETCH":false,"WASMFS":false,"SINGLE_FILE":false,"AUTO_JS_LIBRARIES":true,"AUTO_NATIVE_LIBRARIES":true,"MIN_FIREFOX_VERSION":79,"MIN_SAFARI_VERSION":140100,"MIN_CHROME_VERSION":85,"MIN_NODE_VERSION":160000,"SUPPORT_ERRNO":true,"MINIMAL_RUNTIME":0,"MINIMAL_RUNTIME_STREAMING_WASM_COMPILATION":false,"MINIMAL_RUNTIME_STREAMING_WASM_INSTANTIATION":false,"SUPPORT_LONGJMP":"emscripten","DISABLE_DEPRECATED_FIND_EVENT_TARGET_BEHAVIOR":true,"HTML5_SUPPORT_DEFERRING_USER_SENSITIVE_REQUESTS":true,"MINIFY_HTML":true,"MAYBE_WASM2JS":false,"ASAN_SHADOW_SIZE":-1,"USE_OFFSET_CONVERTER":false,"LOAD_SOURCE_MAP":false,"DEFAULT_TO_CXX":true,"PRINTF_LONG_DOUBLE":false,"SEPARATE_DWARF_URL":"","ERROR_ON_WASM_CHANGES_AFTER_LINK":false,"ABORT_ON_WASM_EXCEPTIONS":false,"PURE_WASI":false,"IMPORTED_MEMORY":false,"SPLIT_MODULE":false,"AUTOLOAD_DYLIBS":true,"ALLOW_UNIMPLEMENTED_SYSCALLS":true,"TRUSTED_TYPES":false,"POLYFILL":true,"RUNTIME_DEBUG":false,"LEGACY_RUNTIME":false,"OFFSCREEN_FRAMEBUFFER_FORBID_VAO_PATH":false,"TEST_MEMORY_GROWTH_FAILS":false,"TARGET_BASENAME":"udesigner","TARGET_JS_NAME":"udesigner.js","SYSCALLS_REQUIRE_FILESYSTEM":true,"AUTODEBUG":false,"WASM2JS":false,"UBSAN_RUNTIME":0,"USE_LSAN":false,"USE_ASAN":false,"EMBIND":false,"EMIT_TSD":false,"MAIN_READS_PARAMS":true,"WASI_MODULE_NAME":"wasi_snapshot_preview1","EMSCRIPTEN_VERSION":"3.1.61-git","USE_RTTI":true,"OPT_LEVEL":1,"DEBUG_LEVEL":0,"SHRINK_LEVEL":0,"EMIT_NAME_SECTION":false,"EMIT_SYMBOL_MAP":false,"WASM_BINARY_FILE":"udesigner.wasm","WASM_WORKER_FILE":"","AUDIO_WORKLET_FILE":"","SOURCE_MAP_BASE":"","SUPPORT_BASE64_EMBEDDING":false,"ENVIRONMENT_MAY_BE_WEB":true,"ENVIRONMENT_MAY_BE_WORKER":true,"ENVIRONMENT_MAY_BE_NODE":true,"ENVIRONMENT_MAY_BE_SHELL":false,"ENVIRONMENT_MAY_BE_WEBVIEW":true,"MINIFY_WASM_IMPORTS_AND_EXPORTS":false,"MINIFY_WASM_IMPORTED_MODULES":false,"MINIFY_WASM_EXPORT_NAMES":true,"SUPPORTS_GLOBALTHIS":true,"SUPPORTS_PROMISE_ANY":true,"LTO":0,"CAN_ADDRESS_2GB":false,"SEPARATE_DWARF":false,"WASM_EXCEPTIONS":false,"EXPECT_MAIN":true,"USE_READY_PROMISE":true,"MEMORYPROFILER":false,"GENERATE_SOURCE_MAP":false,"GENERATE_DWARF":false,"STACK_HIGH":0,"STACK_LOW":0,"HEAP_BASE":0,"HAS_MAIN":true,"LINK_AS_CXX":true,"MAYBE_CLOSURE_COMPILER":1,"TRANSPILE":false,"STACK_FIRST":false,"HAVE_EM_ASM":false,"PTHREADS":false,"BULK_MEMORY":false,"MINIFY_WHITESPACE":false,"WARN_DEPRECATED":true,"WEBGL_USE_GARBAGE_FREE_APIS":1,"AGGRESSIVE_VARIABLE_ELIMINATION":0,"ALIASING_FUNCTION_POINTERS":0,"ASM_JS":1,"AUTO_ARCHIVE_INDEXES":0,"BINARYEN":1,"BINARYEN_ASYNC_COMPILATION":true,"BINARYEN_MEM_MAX":2147483648,"BINARYEN_METHOD":"native-wasm","BINARYEN_PASSES":"","BINARYEN_SCRIPTS":"","BINARYEN_TRAP_MODE":-1,"BUILD_AS_SHARED_LIB":0,"DOUBLE_MODE":0,"ELIMINATE_DUPLICATE_FUNCTIONS":0,"ELIMINATE_DUPLICATE_FUNCTIONS_DUMP_EQUIVALENT_FUNCTIONS":0,"ELIMINATE_DUPLICATE_FUNCTIONS_PASSES":5,"EMITTING_JS":1,"EMIT_EMSCRIPTEN_METADATA":0,"ERROR_ON_MISSING_LIBRARIES":1,"EXPORT_BINDINGS":0,"EXPORT_FUNCTION_TABLES":0,"FAST_UNROLLED_MEMCPY_AND_MEMSET":0,"FINALIZE_ASM_JS":0,"FORCE_ALIGNED_MEMORY":0,"FUNCTION_POINTER_ALIGNMENT":2,"LLD_REPORT_UNDEFINED":1,"MEMFS_APPEND_TO_TYPED_ARRAYS":1,"MEMORY_GROWTH_STEP":-1,"MEM_INIT_METHOD":0,"MIN_EDGE_VERSION":2147483647,"MIN_IE_VERSION":2147483647,"PGO":0,"PRECISE_F32":0,"PRECISE_I64_MATH":1,"QUANTUM_SIZE":4,"RESERVED_FUNCTION_POINTERS":false,"REVERSE_DEPS":"auto","RUNNING_JS_OPTS":0,"RUNTIME_LOGGING":false,"SAFE_SPLIT_MEMORY":0,"SAFE_STACK":0,"SEPARATE_ASM":0,"SEPARATE_ASM_MODULE_NAME":"","SHELL_FILE":"","SIMPLIFY_IFS":1,"SKIP_STACK_IN_SMALL":0,"SPLIT_MEMORY":0,"SWAPPABLE_ASM_MODULE":0,"TOTAL_MEMORY":-1,"TOTAL_STACK":65536,"UNALIGNED_MEMORY":0,"USES_DYNAMIC_ALLOC":1,"USE_PTHREADS":0,"WARN_UNALIGNED":0,"WASM_BACKEND":-1,"WASM_MEM_MAX":2147483648,"WASM_OBJECT_FILES":0,"WORKAROUND_IOS_9_RIGHT_SHIFT_BUG":0,"WORKAROUND_OLD_WEBGL_UNIFORM_UPLOAD_IGNORED_OFFSET_BUG":0} ; +var compilerSettings = {"ASSERTIONS":1,"STACK_OVERFLOW_CHECK":1,"CHECK_NULL_WRITES":true,"VERBOSE":false,"INVOKE_RUN":true,"EXIT_RUNTIME":false,"STACK_SIZE":65536,"MALLOC":"dlmalloc","ABORTING_MALLOC":0,"INITIAL_HEAP":16777216,"INITIAL_MEMORY":-1,"MAXIMUM_MEMORY":2147483648,"ALLOW_MEMORY_GROWTH":1,"MEMORY_GROWTH_GEOMETRIC_STEP":0.2,"MEMORY_GROWTH_GEOMETRIC_CAP":100663296,"MEMORY_GROWTH_LINEAR_STEP":-1,"MEMORY64":0,"INITIAL_TABLE":-1,"ALLOW_TABLE_GROWTH":false,"GLOBAL_BASE":1024,"TABLE_BASE":1,"USE_CLOSURE_COMPILER":false,"CLOSURE_WARNINGS":"quiet","IGNORE_CLOSURE_COMPILER_ERRORS":false,"DECLARE_ASM_MODULE_EXPORTS":true,"INLINING_LIMIT":false,"SUPPORT_BIG_ENDIAN":false,"SAFE_HEAP":0,"SAFE_HEAP_LOG":false,"EMULATE_FUNCTION_POINTER_CASTS":false,"EXCEPTION_DEBUG":false,"DEMANGLE_SUPPORT":false,"LIBRARY_DEBUG":false,"SYSCALL_DEBUG":false,"SOCKET_DEBUG":false,"DYLINK_DEBUG":0,"FS_DEBUG":false,"SOCKET_WEBRTC":false,"WEBSOCKET_URL":"ws:#","PROXY_POSIX_SOCKETS":false,"WEBSOCKET_SUBPROTOCOL":"binary","OPENAL_DEBUG":false,"WEBSOCKET_DEBUG":false,"GL_ASSERTIONS":false,"TRACE_WEBGL_CALLS":false,"GL_DEBUG":false,"GL_TESTING":false,"GL_MAX_TEMP_BUFFER_SIZE":2097152,"GL_UNSAFE_OPTS":true,"FULL_ES2":1,"GL_EMULATE_GLES_VERSION_STRING_FORMAT":true,"GL_EXTENSIONS_IN_PREFIXED_FORMAT":true,"GL_SUPPORT_AUTOMATIC_ENABLE_EXTENSIONS":true,"GL_SUPPORT_SIMPLE_ENABLE_EXTENSIONS":true,"GL_TRACK_ERRORS":true,"GL_SUPPORT_EXPLICIT_SWAP_CONTROL":false,"GL_POOL_TEMP_BUFFERS":true,"GL_EXPLICIT_UNIFORM_LOCATION":false,"GL_EXPLICIT_UNIFORM_BINDING":false,"USE_WEBGL2":1,"MIN_WEBGL_VERSION":1,"MAX_WEBGL_VERSION":2,"WEBGL2_BACKWARDS_COMPATIBILITY_EMULATION":false,"FULL_ES3":1,"LEGACY_GL_EMULATION":false,"GL_FFP_ONLY":false,"GL_PREINITIALIZED_CONTEXT":false,"USE_WEBGPU":false,"STB_IMAGE":false,"GL_DISABLE_HALF_FLOAT_EXTENSION_IF_BROKEN":false,"GL_WORKAROUND_SAFARI_GETCONTEXT_BUG":true,"GL_ENABLE_GET_PROC_ADDRESS":true,"JS_MATH":false,"POLYFILL_OLD_MATH_FUNCTIONS":0,"LEGACY_VM_SUPPORT":false,"ENVIRONMENT":"web,webview,worker,node","LZ4":false,"DISABLE_EXCEPTION_CATCHING":0,"DISABLE_EXCEPTION_THROWING":0,"EXPORT_EXCEPTION_HANDLING_HELPERS":true,"EXCEPTION_STACK_TRACES":1,"WASM_EXNREF":false,"NODEJS_CATCH_EXIT":false,"NODEJS_CATCH_REJECTION":0,"ASYNCIFY":0,"ASYNCIFY_IGNORE_INDIRECT":false,"ASYNCIFY_STACK_SIZE":4096,"ASYNCIFY_PROPAGATE_ADD":true,"ASYNCIFY_ADVISE":false,"ASYNCIFY_LAZY_LOAD_CODE":false,"ASYNCIFY_DEBUG":0,"JSPI":0,"CASE_INSENSITIVE_FS":false,"FILESYSTEM":true,"FORCE_FILESYSTEM":1,"NODERAWFS":false,"NODE_CODE_CACHING":false,"EXPORT_ALL":false,"EXPORT_KEEPALIVE":true,"RETAIN_COMPILER_SETTINGS":1,"INCLUDE_FULL_LIBRARY":false,"RELOCATABLE":false,"MAIN_MODULE":0,"SIDE_MODULE":0,"BUILD_AS_WORKER":false,"PROXY_TO_WORKER":false,"PROXY_TO_WORKER_FILENAME":"","PROXY_TO_PTHREAD":false,"LINKABLE":false,"STRICT":false,"IGNORE_MISSING_MAIN":true,"STRICT_JS":false,"WARN_ON_UNDEFINED_SYMBOLS":true,"ERROR_ON_UNDEFINED_SYMBOLS":true,"SMALL_XHR_CHUNKS":false,"HEADLESS":false,"DETERMINISTIC":false,"MODULARIZE":false,"EXPORT_ES6":false,"USE_ES6_IMPORT_META":true,"EXPORT_NAME":"Module","DYNAMIC_EXECUTION":1,"BOOTSTRAPPING_STRUCT_INFO":false,"EMSCRIPTEN_TRACING":false,"USE_GLFW":3,"WASM":1,"STANDALONE_WASM":false,"BINARYEN_IGNORE_IMPLICIT_TRAPS":false,"BINARYEN_EXTRA_PASSES":"","WASM_ASYNC_COMPILATION":true,"DYNCALLS":false,"WASM_BIGINT":false,"EMIT_PRODUCERS_SECTION":false,"EMIT_EMSCRIPTEN_LICENSE":false,"LEGALIZE_JS_FFI":true,"USE_SDL":0,"USE_SDL_GFX":0,"USE_SDL_IMAGE":1,"USE_SDL_TTF":1,"USE_SDL_NET":1,"USE_ICU":false,"USE_ZLIB":false,"USE_BZIP2":false,"USE_GIFLIB":false,"USE_LIBJPEG":false,"USE_LIBPNG":false,"USE_REGAL":false,"USE_BOOST_HEADERS":false,"USE_BULLET":false,"USE_VORBIS":false,"USE_OGG":false,"USE_MPG123":false,"USE_FREETYPE":false,"USE_SDL_MIXER":1,"USE_HARFBUZZ":false,"USE_COCOS2D":0,"USE_MODPLUG":false,"USE_SQLITE3":false,"SHARED_MEMORY":false,"WASM_WORKERS":0,"AUDIO_WORKLET":0,"WEBAUDIO_DEBUG":0,"PTHREAD_POOL_SIZE":0,"PTHREAD_POOL_SIZE_STRICT":1,"PTHREAD_POOL_DELAY_LOAD":false,"DEFAULT_PTHREAD_STACK_SIZE":0,"PTHREADS_PROFILING":false,"ALLOW_BLOCKING_ON_MAIN_THREAD":true,"PTHREADS_DEBUG":false,"EVAL_CTORS":0,"TEXTDECODER":1,"EMBIND_STD_STRING_IS_UTF8":true,"EMBIND_AOT":false,"OFFSCREENCANVAS_SUPPORT":false,"OFFSCREENCANVASES_TO_PTHREAD":"#canvas","OFFSCREEN_FRAMEBUFFER":false,"FETCH_SUPPORT_INDEXEDDB":true,"FETCH_DEBUG":false,"FETCH":false,"WASMFS":false,"SINGLE_FILE":false,"AUTO_JS_LIBRARIES":true,"AUTO_NATIVE_LIBRARIES":true,"MIN_FIREFOX_VERSION":79,"MIN_SAFARI_VERSION":140100,"MIN_CHROME_VERSION":85,"MIN_NODE_VERSION":160000,"SUPPORT_ERRNO":true,"MINIMAL_RUNTIME":0,"MINIMAL_RUNTIME_STREAMING_WASM_COMPILATION":false,"MINIMAL_RUNTIME_STREAMING_WASM_INSTANTIATION":false,"SUPPORT_LONGJMP":"emscripten","DISABLE_DEPRECATED_FIND_EVENT_TARGET_BEHAVIOR":true,"HTML5_SUPPORT_DEFERRING_USER_SENSITIVE_REQUESTS":true,"MINIFY_HTML":true,"MAYBE_WASM2JS":false,"ASAN_SHADOW_SIZE":-1,"USE_OFFSET_CONVERTER":false,"LOAD_SOURCE_MAP":false,"DEFAULT_TO_CXX":true,"PRINTF_LONG_DOUBLE":false,"SEPARATE_DWARF_URL":"","ERROR_ON_WASM_CHANGES_AFTER_LINK":false,"ABORT_ON_WASM_EXCEPTIONS":false,"PURE_WASI":false,"IMPORTED_MEMORY":false,"SPLIT_MODULE":false,"AUTOLOAD_DYLIBS":true,"ALLOW_UNIMPLEMENTED_SYSCALLS":true,"TRUSTED_TYPES":false,"POLYFILL":true,"RUNTIME_DEBUG":false,"LEGACY_RUNTIME":false,"OFFSCREEN_FRAMEBUFFER_FORBID_VAO_PATH":false,"TARGET_BASENAME":"udesigner","TARGET_JS_NAME":"udesigner.js","SYSCALLS_REQUIRE_FILESYSTEM":true,"AUTODEBUG":false,"WASM2JS":false,"UBSAN_RUNTIME":0,"USE_LSAN":false,"USE_ASAN":false,"EMBIND":false,"EMIT_TSD":false,"MAIN_READS_PARAMS":true,"WASI_MODULE_NAME":"wasi_snapshot_preview1","EMSCRIPTEN_VERSION":"3.1.71-git","USE_RTTI":true,"OPT_LEVEL":1,"DEBUG_LEVEL":0,"SHRINK_LEVEL":0,"EMIT_NAME_SECTION":false,"EMIT_SYMBOL_MAP":false,"WASM_BINARY_FILE":"udesigner.wasm","WASM_WORKER_FILE":"","AUDIO_WORKLET_FILE":"","SOURCE_MAP_BASE":"","SUPPORT_BASE64_EMBEDDING":false,"ENVIRONMENT_MAY_BE_WEB":true,"ENVIRONMENT_MAY_BE_WORKER":true,"ENVIRONMENT_MAY_BE_NODE":true,"ENVIRONMENT_MAY_BE_SHELL":false,"ENVIRONMENT_MAY_BE_WEBVIEW":true,"MINIFY_WASM_IMPORTS_AND_EXPORTS":false,"MINIFY_WASM_IMPORTED_MODULES":false,"MINIFY_WASM_EXPORT_NAMES":true,"SUPPORTS_GLOBALTHIS":true,"SUPPORTS_PROMISE_ANY":true,"LTO":0,"CAN_ADDRESS_2GB":false,"SEPARATE_DWARF":false,"WASM_EXCEPTIONS":false,"EXPECT_MAIN":true,"USE_READY_PROMISE":true,"MEMORYPROFILER":false,"GENERATE_SOURCE_MAP":false,"GENERATE_DWARF":false,"STACK_HIGH":0,"STACK_LOW":0,"HEAP_BASE":0,"HAS_MAIN":true,"LINK_AS_CXX":true,"MAYBE_CLOSURE_COMPILER":1,"TRANSPILE":false,"STACK_FIRST":false,"HAVE_EM_ASM":false,"PTHREADS":false,"BULK_MEMORY":false,"MINIFY_WHITESPACE":false,"WARN_DEPRECATED":true,"WEBGL_USE_GARBAGE_FREE_APIS":1,"INCLUDE_WEBGL1_FALLBACK":true,"MINIFICATION_MAP":"","AGGRESSIVE_VARIABLE_ELIMINATION":0,"ALIASING_FUNCTION_POINTERS":0,"ASM_JS":1,"AUTO_ARCHIVE_INDEXES":0,"BINARYEN":1,"BINARYEN_ASYNC_COMPILATION":true,"BINARYEN_MEM_MAX":2147483648,"BINARYEN_METHOD":"native-wasm","BINARYEN_PASSES":"","BINARYEN_SCRIPTS":"","BINARYEN_TRAP_MODE":-1,"BUILD_AS_SHARED_LIB":0,"DOUBLE_MODE":0,"ELIMINATE_DUPLICATE_FUNCTIONS":0,"ELIMINATE_DUPLICATE_FUNCTIONS_DUMP_EQUIVALENT_FUNCTIONS":0,"ELIMINATE_DUPLICATE_FUNCTIONS_PASSES":5,"EMITTING_JS":1,"EMIT_EMSCRIPTEN_METADATA":0,"ERROR_ON_MISSING_LIBRARIES":1,"EXPORT_BINDINGS":0,"EXPORT_FUNCTION_TABLES":0,"FAST_UNROLLED_MEMCPY_AND_MEMSET":0,"FINALIZE_ASM_JS":0,"FORCE_ALIGNED_MEMORY":0,"FUNCTION_POINTER_ALIGNMENT":2,"LLD_REPORT_UNDEFINED":1,"MEMFS_APPEND_TO_TYPED_ARRAYS":1,"MEMORY_GROWTH_STEP":-1,"MEM_INIT_METHOD":0,"MIN_EDGE_VERSION":2147483647,"MIN_IE_VERSION":2147483647,"PGO":0,"PRECISE_F32":0,"PRECISE_I64_MATH":1,"QUANTUM_SIZE":4,"RESERVED_FUNCTION_POINTERS":false,"REVERSE_DEPS":"auto","RUNNING_JS_OPTS":0,"RUNTIME_LOGGING":false,"SAFE_SPLIT_MEMORY":0,"SAFE_STACK":0,"SEPARATE_ASM":0,"SEPARATE_ASM_MODULE_NAME":"","SHELL_FILE":"","SIMPLIFY_IFS":1,"SKIP_STACK_IN_SMALL":0,"SPLIT_MEMORY":0,"SWAPPABLE_ASM_MODULE":0,"TOTAL_MEMORY":-1,"TOTAL_STACK":65536,"UNALIGNED_MEMORY":0,"USES_DYNAMIC_ALLOC":1,"USE_PTHREADS":0,"WARN_UNALIGNED":0,"WASM_BACKEND":-1,"WASM_MEM_MAX":2147483648,"WASM_OBJECT_FILES":0,"WORKAROUND_IOS_9_RIGHT_SHIFT_BUG":0,"WORKAROUND_OLD_WEBGL_UNIFORM_UPLOAD_IGNORED_OFFSET_BUG":0} ; function getCompilerSetting(name) { if (!(name in compilerSettings)) return 'invalid compiler setting: ' + name; @@ -1163,11 +1139,12 @@ function ImGui_ImplGlfw_EmscriptenOpenURL(url) { url = url ? UTF8ToString(url) : // end include: preamble.js - /** @constructor */ - function ExitStatus(status) { - this.name = 'ExitStatus'; - this.message = `Program terminated with exit(${status})`; - this.status = status; + class ExitStatus { + name = 'ExitStatus'; + constructor(status) { + this.message = `Program terminated with exit(${status})`; + this.status = status; + } } var callRuntimeCallbacks = (callbacks) => { @@ -1240,25 +1217,25 @@ function ImGui_ImplGlfw_EmscriptenOpenURL(url) { url = url ? UTF8ToString(url) : } }; - var UTF8Decoder = typeof TextDecoder != 'undefined' ? new TextDecoder('utf8') : undefined; + var UTF8Decoder = typeof TextDecoder != 'undefined' ? new TextDecoder() : undefined; /** * Given a pointer 'idx' to a null-terminated UTF8-encoded string in the given * array that contains uint8 values, returns a copy of that string as a * Javascript String object. * heapOrArray is either a regular array, or a JavaScript typed array view. - * @param {number} idx + * @param {number=} idx * @param {number=} maxBytesToRead * @return {string} */ - var UTF8ArrayToString = (heapOrArray, idx, maxBytesToRead) => { + var UTF8ArrayToString = (heapOrArray, idx = 0, maxBytesToRead = NaN) => { var endIdx = idx + maxBytesToRead; var endPtr = idx; // TextDecoder needs to know the byte length in advance, it doesn't stop on // null terminator by itself. Also, use the length info to avoid running tiny // strings through TextDecoder, since .subarray() allocates garbage. // (As a tiny code save trick, compare endPtr against endIdx using a negation, - // so that undefined means Infinity) + // so that undefined/NaN means Infinity) while (heapOrArray[endPtr] && !(endPtr >= endIdx)) ++endPtr; if (endPtr - idx > 16 && heapOrArray.buffer && UTF8Decoder) { @@ -1320,6 +1297,7 @@ function ImGui_ImplGlfw_EmscriptenOpenURL(url) { url = url ? UTF8ToString(url) : var exceptionCaught = []; + var uncaughtExceptionCount = 0; var ___cxa_begin_catch = (ptr) => { var info = new ExceptionInfo(ptr); @@ -1329,8 +1307,8 @@ function ImGui_ImplGlfw_EmscriptenOpenURL(url) { url = url ? UTF8ToString(url) : } info.set_rethrown(false); exceptionCaught.push(info); - ___cxa_increment_exception_refcount(info.excPtr); - return info.get_exception_ptr(); + ___cxa_increment_exception_refcount(ptr); + return ___cxa_get_exception_ptr(ptr); }; @@ -1404,22 +1382,6 @@ function ImGui_ImplGlfw_EmscriptenOpenURL(url) { url = url ? UTF8ToString(url) : get_adjusted_ptr() { return HEAPU32[(((this.ptr)+(16))>>2)]; } - - // Get pointer which is expected to be received by catch clause in C++ code. It may be adjusted - // when the pointer is casted to some of the exception object base classes (e.g. when virtual - // inheritance is used). When a pointer is thrown this method should return the thrown pointer - // itself. - get_exception_ptr() { - // Work around a fastcomp bug, this code is still included for some reason in a build without - // exceptions support. - var isPointer = ___cxa_is_pointer_type(this.get_type()); - if (isPointer) { - return HEAPU32[((this.excPtr)>>2)]; - } - var adjusted = this.get_adjusted_ptr(); - if (adjusted !== 0) return adjusted; - return this.excPtr; - } } var ___resumeException = (ptr) => { @@ -1505,13 +1467,13 @@ function ImGui_ImplGlfw_EmscriptenOpenURL(url) { url = url ? UTF8ToString(url) : /** @suppress {duplicate } */ - function syscallGetVarargI() { + var syscallGetVarargI = () => { assert(SYSCALLS.varargs != undefined); // the `+` prepended here is necessary to convince the JSCompiler that varargs is indeed a number. var ret = HEAP32[((+SYSCALLS.varargs)>>2)]; SYSCALLS.varargs += 4; return ret; - } + }; var syscallGetVarargP = syscallGetVarargI; @@ -1891,7 +1853,7 @@ function ImGui_ImplGlfw_EmscriptenOpenURL(url) { url = url ? UTF8ToString(url) : }, put_char(tty, val) { if (val === null || val === 10) { - out(UTF8ArrayToString(tty.output, 0)); + out(UTF8ArrayToString(tty.output)); tty.output = []; } else { if (val != 0) tty.output.push(val); // val == 0 would cut text output off in the middle. @@ -1899,7 +1861,7 @@ function ImGui_ImplGlfw_EmscriptenOpenURL(url) { url = url ? UTF8ToString(url) : }, fsync(tty) { if (tty.output && tty.output.length > 0) { - out(UTF8ArrayToString(tty.output, 0)); + out(UTF8ArrayToString(tty.output)); tty.output = []; } }, @@ -1928,7 +1890,7 @@ function ImGui_ImplGlfw_EmscriptenOpenURL(url) { url = url ? UTF8ToString(url) : default_tty1_ops:{ put_char(tty, val) { if (val === null || val === 10) { - err(UTF8ArrayToString(tty.output, 0)); + err(UTF8ArrayToString(tty.output)); tty.output = []; } else { if (val != 0) tty.output.push(val); @@ -1936,7 +1898,7 @@ function ImGui_ImplGlfw_EmscriptenOpenURL(url) { url = url ? UTF8ToString(url) : }, fsync(tty) { if (tty.output && tty.output.length > 0) { - err(UTF8ArrayToString(tty.output, 0)); + err(UTF8ArrayToString(tty.output)); tty.output = []; } }, @@ -1946,7 +1908,6 @@ function ImGui_ImplGlfw_EmscriptenOpenURL(url) { url = url ? UTF8ToString(url) : var zeroMemory = (address, size) => { HEAPU8.fill(0, address, address + size); - return address; }; var alignMemory = (size, alignment) => { @@ -2254,26 +2215,28 @@ function ImGui_ImplGlfw_EmscriptenOpenURL(url) { url = url ? UTF8ToString(url) : var allocated; var contents = stream.node.contents; // Only make a new copy when MAP_PRIVATE is specified. - if (!(flags & 2) && contents.buffer === HEAP8.buffer) { + if (!(flags & 2) && contents && contents.buffer === HEAP8.buffer) { // We can't emulate MAP_SHARED when the file is not backed by the // buffer we're mapping to (e.g. the HEAP buffer). allocated = false; ptr = contents.byteOffset; } else { - // Try to avoid unnecessary slices. - if (position > 0 || position + length < contents.length) { - if (contents.subarray) { - contents = contents.subarray(position, position + length); - } else { - contents = Array.prototype.slice.call(contents, position, position + length); - } - } allocated = true; ptr = mmapAlloc(length); if (!ptr) { throw new FS.ErrnoError(48); } - HEAP8.set(contents, ptr); + if (contents) { + // Try to avoid unnecessary slices. + if (position > 0 || position + length < contents.length) { + if (contents.subarray) { + contents = contents.subarray(position, position + length); + } else { + contents = Array.prototype.slice.call(contents, position, position + length); + } + } + HEAP8.set(contents, ptr); + } } return { ptr, allocated }; }, @@ -2288,17 +2251,20 @@ function ImGui_ImplGlfw_EmscriptenOpenURL(url) { url = url ? UTF8ToString(url) : /** @param {boolean=} noRunDep */ var asyncLoad = (url, onload, onerror, noRunDep) => { var dep = !noRunDep ? getUniqueRunDependency(`al ${url}`) : ''; - readAsync(url, (arrayBuffer) => { - assert(arrayBuffer, `Loading data file "${url}" failed (no arrayBuffer).`); - onload(new Uint8Array(arrayBuffer)); - if (dep) removeRunDependency(dep); - }, (event) => { - if (onerror) { - onerror(); - } else { - throw `Loading data file "${url}" failed.`; + readAsync(url).then( + (arrayBuffer) => { + assert(arrayBuffer, `Loading data file "${url}" failed (no arrayBuffer).`); + onload(new Uint8Array(arrayBuffer)); + if (dep) removeRunDependency(dep); + }, + (err) => { + if (onerror) { + onerror(); + } else { + throw `Loading data file "${url}" failed.`; + } } - }); + ); if (dep) addRunDependency(dep); }; @@ -2378,127 +2344,11 @@ function ImGui_ImplGlfw_EmscriptenOpenURL(url) { url = url ? UTF8ToString(url) : - var ERRNO_MESSAGES = { - 0:"Success", - 1:"Arg list too long", - 2:"Permission denied", - 3:"Address already in use", - 4:"Address not available", - 5:"Address family not supported by protocol family", - 6:"No more processes", - 7:"Socket already connected", - 8:"Bad file number", - 9:"Trying to read unreadable message", - 10:"Mount device busy", - 11:"Operation canceled", - 12:"No children", - 13:"Connection aborted", - 14:"Connection refused", - 15:"Connection reset by peer", - 16:"File locking deadlock error", - 17:"Destination address required", - 18:"Math arg out of domain of func", - 19:"Quota exceeded", - 20:"File exists", - 21:"Bad address", - 22:"File too large", - 23:"Host is unreachable", - 24:"Identifier removed", - 25:"Illegal byte sequence", - 26:"Connection already in progress", - 27:"Interrupted system call", - 28:"Invalid argument", - 29:"I/O error", - 30:"Socket is already connected", - 31:"Is a directory", - 32:"Too many symbolic links", - 33:"Too many open files", - 34:"Too many links", - 35:"Message too long", - 36:"Multihop attempted", - 37:"File or path name too long", - 38:"Network interface is not configured", - 39:"Connection reset by network", - 40:"Network is unreachable", - 41:"Too many open files in system", - 42:"No buffer space available", - 43:"No such device", - 44:"No such file or directory", - 45:"Exec format error", - 46:"No record locks available", - 47:"The link has been severed", - 48:"Not enough core", - 49:"No message of desired type", - 50:"Protocol not available", - 51:"No space left on device", - 52:"Function not implemented", - 53:"Socket is not connected", - 54:"Not a directory", - 55:"Directory not empty", - 56:"State not recoverable", - 57:"Socket operation on non-socket", - 59:"Not a typewriter", - 60:"No such device or address", - 61:"Value too large for defined data type", - 62:"Previous owner died", - 63:"Not super-user", - 64:"Broken pipe", - 65:"Protocol error", - 66:"Unknown protocol", - 67:"Protocol wrong type for socket", - 68:"Math result not representable", - 69:"Read only file system", - 70:"Illegal seek", - 71:"No such process", - 72:"Stale file handle", - 73:"Connection timed out", - 74:"Text file busy", - 75:"Cross-device link", - 100:"Device not a stream", - 101:"Bad font file fmt", - 102:"Invalid slot", - 103:"Invalid request code", - 104:"No anode", - 105:"Block device required", - 106:"Channel number out of range", - 107:"Level 3 halted", - 108:"Level 3 reset", - 109:"Link number out of range", - 110:"Protocol driver not attached", - 111:"No CSI structure available", - 112:"Level 2 halted", - 113:"Invalid exchange", - 114:"Invalid request descriptor", - 115:"Exchange full", - 116:"No data (for no delay io)", - 117:"Timer expired", - 118:"Out of streams resources", - 119:"Machine is not on the network", - 120:"Package not installed", - 121:"The object is remote", - 122:"Advertise error", - 123:"Srmount error", - 124:"Communication error on send", - 125:"Cross mount point (not really error)", - 126:"Given log. name not unique", - 127:"f.d. invalid for this operation", - 128:"Remote address changed", - 129:"Can access a needed shared lib", - 130:"Accessing a corrupted shared lib", - 131:".lib section in a.out corrupted", - 132:"Attempting to link in too many libs", - 133:"Attempting to exec a shared library", - 135:"Streams pipe error", - 136:"Too many users", - 137:"Socket type not supported", - 138:"Not supported", - 139:"Protocol family not supported", - 140:"Can't send after socket shutdown", - 141:"Too many references", - 142:"Host is down", - 148:"No medium (in tape drive)", - 156:"Level 2 not synchronized", - }; + + + var strError = (errno) => { + return UTF8ToString(_strerror(errno)); + }; var ERRNO_CODES = { 'EPERM': 63, @@ -2635,6 +2485,7 @@ function ImGui_ImplGlfw_EmscriptenOpenURL(url) { url = url ? UTF8ToString(url) : initialized:false, ignorePermissions:true, ErrnoError:class extends Error { + name = 'ErrnoError'; // We set the `name` property to be able to identify `FS.ErrnoError` // - the `name` is a standard ECMA-262 property of error objects. Kind of good to have it anyway. // - when using PROXYFS, an error can come from an underlying FS @@ -2642,10 +2493,7 @@ function ImGui_ImplGlfw_EmscriptenOpenURL(url) { url = url ? UTF8ToString(url) : // the test `err instanceof FS.ErrnoError` won't detect an error coming from another filesystem, causing bugs. // we'll use the reliable test `err.name == "ErrnoError"` instead constructor(errno) { - super(ERRNO_MESSAGES[errno]); - // TODO(sbc): Use the inline member declaration syntax once we - // support it in acorn and closure. - this.name = 'ErrnoError'; + super(runtimeInitialized ? strError(errno) : ''); this.errno = errno; for (var key in ERRNO_CODES) { if (ERRNO_CODES[key] === errno) { @@ -2659,12 +2507,10 @@ function ImGui_ImplGlfw_EmscriptenOpenURL(url) { url = url ? UTF8ToString(url) : }, filesystems:null, syncFSRequests:0, + readFiles:{ + }, FSStream:class { - constructor() { - // TODO(https://github.com/emscripten-core/emscripten/issues/21414): - // Use inline field declarations. - this.shared = {}; - } + shared = {}; get object() { return this.node; } @@ -2694,21 +2540,21 @@ function ImGui_ImplGlfw_EmscriptenOpenURL(url) { url = url ? UTF8ToString(url) : } }, FSNode:class { + node_ops = {}; + stream_ops = {}; + readMode = 292 | 73; + writeMode = 146; + mounted = null; constructor(parent, name, mode, rdev) { if (!parent) { parent = this; // root node sets parent to itself } this.parent = parent; this.mount = parent.mount; - this.mounted = null; this.id = FS.nextInode++; this.name = name; this.mode = mode; - this.node_ops = {}; - this.stream_ops = {}; this.rdev = rdev; - this.readMode = 292/*292*/ | 73/*73*/; - this.writeMode = 146/*146*/; } get read() { return (this.mode & this.readMode) === this.readMode; @@ -2974,6 +2820,7 @@ function ImGui_ImplGlfw_EmscriptenOpenURL(url) { url = url ? UTF8ToString(url) : }, getStream:(fd) => FS.streams[fd], createStream(stream, fd = -1) { + assert(fd >= -1); // clone it, so we can return an instance of FSStream stream = Object.assign(new FS.FSStream(), stream); @@ -3557,7 +3404,6 @@ function ImGui_ImplGlfw_EmscriptenOpenURL(url) { url = url ? UTF8ToString(url) : stream.stream_ops.open(stream); } if (Module['logReadFiles'] && !(flags & 1)) { - if (!FS.readFiles) FS.readFiles = {}; if (!(path in FS.readFiles)) { FS.readFiles[path] = 1; } @@ -3691,6 +3537,9 @@ function ImGui_ImplGlfw_EmscriptenOpenURL(url) { url = url ? UTF8ToString(url) : if (!stream.stream_ops.mmap) { throw new FS.ErrnoError(43); } + if (!length) { + throw new FS.ErrnoError(28); + } return stream.stream_ops.mmap(stream, length, position, prot, flags); }, msync(stream, buffer, offset, length, mmapFlags) { @@ -3719,7 +3568,7 @@ function ImGui_ImplGlfw_EmscriptenOpenURL(url) { url = url ? UTF8ToString(url) : var buf = new Uint8Array(length); FS.read(stream, buf, 0, length, 0); if (opts.encoding === 'utf8') { - ret = UTF8ArrayToString(buf, 0); + ret = UTF8ArrayToString(buf); } else if (opts.encoding === 'binary') { ret = buf; } @@ -3818,7 +3667,7 @@ function ImGui_ImplGlfw_EmscriptenOpenURL(url) { url = url ? UTF8ToString(url) : } }, {}, '/proc/self/fd'); }, - createStandardStreams() { + createStandardStreams(input, output, error) { // TODO deprecate the old functionality of a single // input / output callback and that utilizes FS.createDevice // and instead require a unique set of stream ops @@ -3827,18 +3676,18 @@ function ImGui_ImplGlfw_EmscriptenOpenURL(url) { url = url ? UTF8ToString(url) : // default tty devices. however, if the standard streams // have been overwritten we create a unique device for // them instead. - if (Module['stdin']) { - FS.createDevice('/dev', 'stdin', Module['stdin']); + if (input) { + FS.createDevice('/dev', 'stdin', input); } else { FS.symlink('/dev/tty', '/dev/stdin'); } - if (Module['stdout']) { - FS.createDevice('/dev', 'stdout', null, Module['stdout']); + if (output) { + FS.createDevice('/dev', 'stdout', null, output); } else { FS.symlink('/dev/tty', '/dev/stdout'); } - if (Module['stderr']) { - FS.createDevice('/dev', 'stderr', null, Module['stderr']); + if (error) { + FS.createDevice('/dev', 'stderr', null, error); } else { FS.symlink('/dev/tty1', '/dev/stderr'); } @@ -3871,18 +3720,18 @@ function ImGui_ImplGlfw_EmscriptenOpenURL(url) { url = url ? UTF8ToString(url) : }; }, init(input, output, error) { - assert(!FS.init.initialized, 'FS.init was previously called. If you want to initialize later with custom parameters, remove any earlier calls (note that one is automatically added to the generated code)'); - FS.init.initialized = true; + assert(!FS.initialized, 'FS.init was previously called. If you want to initialize later with custom parameters, remove any earlier calls (note that one is automatically added to the generated code)'); + FS.initialized = true; // Allow Module.stdin etc. to provide defaults, if none explicitly passed to us here - Module['stdin'] = input || Module['stdin']; - Module['stdout'] = output || Module['stdout']; - Module['stderr'] = error || Module['stderr']; + input ??= Module['stdin']; + output ??= Module['stdout']; + error ??= Module['stderr']; - FS.createStandardStreams(); + FS.createStandardStreams(input, output, error); }, quit() { - FS.init.initialized = false; + FS.initialized = false; // force-flush all streams, so we get musl std streams printed out _fflush(0); // close all of our streams @@ -3975,7 +3824,7 @@ function ImGui_ImplGlfw_EmscriptenOpenURL(url) { url = url ? UTF8ToString(url) : createDevice(parent, name, input, output) { var path = PATH.join2(typeof parent == 'string' ? parent : FS.getPath(parent), name); var mode = FS_getMode(!!input, !!output); - if (!FS.createDevice.major) FS.createDevice.major = 64; + FS.createDevice.major ??= 64; var dev = FS.makedev(FS.createDevice.major++, 0); // Create a fake device that a set of stream ops to emulate // the old behavior. @@ -4030,28 +3879,21 @@ function ImGui_ImplGlfw_EmscriptenOpenURL(url) { url = url ? UTF8ToString(url) : if (obj.isDevice || obj.isFolder || obj.link || obj.contents) return true; if (typeof XMLHttpRequest != 'undefined') { throw new Error("Lazy loading should have been performed (contents set) in createLazyFile, but it was not. Lazy loading only works in web workers. Use --embed-file or --preload-file in emcc on the main thread."); - } else if (read_) { - // Command-line. + } else { // Command-line. try { - // WARNING: Can't read binary files in V8's d8 or tracemonkey's js, as - // read() will try to parse UTF8. - obj.contents = intArrayFromString(read_(obj.url), true); + obj.contents = readBinary(obj.url); obj.usedBytes = obj.contents.length; } catch (e) { throw new FS.ErrnoError(29); } - } else { - throw new Error('Cannot load without read() or XMLHttpRequest.'); } }, createLazyFile(parent, name, url, canRead, canWrite) { // Lazy chunked Uint8Array (implements get and length from Uint8Array). // Actual getting is abstracted away for eventual reuse. class LazyUint8Array { - constructor() { - this.lengthKnown = false; - this.chunks = []; // Loaded chunks. Index is the chunk number - } + lengthKnown = false; + chunks = []; // Loaded chunks. Index is the chunk number get(idx) { if (idx > this.length-1 || idx < 0) { return undefined; @@ -4265,11 +4107,11 @@ function ImGui_ImplGlfw_EmscriptenOpenURL(url) { url = url ? UTF8ToString(url) : var mtime = stat.mtime.getTime(); var ctime = stat.ctime.getTime(); (tempI64 = [Math.floor(atime / 1000)>>>0,(tempDouble = Math.floor(atime / 1000),(+(Math.abs(tempDouble))) >= 1.0 ? (tempDouble > 0.0 ? (+(Math.floor((tempDouble)/4294967296.0)))>>>0 : (~~((+(Math.ceil((tempDouble - +(((~~(tempDouble)))>>>0))/4294967296.0)))))>>>0) : 0)], HEAP32[(((buf)+(40))>>2)] = tempI64[0],HEAP32[(((buf)+(44))>>2)] = tempI64[1]); - HEAPU32[(((buf)+(48))>>2)] = (atime % 1000) * 1000; + HEAPU32[(((buf)+(48))>>2)] = (atime % 1000) * 1000 * 1000; (tempI64 = [Math.floor(mtime / 1000)>>>0,(tempDouble = Math.floor(mtime / 1000),(+(Math.abs(tempDouble))) >= 1.0 ? (tempDouble > 0.0 ? (+(Math.floor((tempDouble)/4294967296.0)))>>>0 : (~~((+(Math.ceil((tempDouble - +(((~~(tempDouble)))>>>0))/4294967296.0)))))>>>0) : 0)], HEAP32[(((buf)+(56))>>2)] = tempI64[0],HEAP32[(((buf)+(60))>>2)] = tempI64[1]); - HEAPU32[(((buf)+(64))>>2)] = (mtime % 1000) * 1000; + HEAPU32[(((buf)+(64))>>2)] = (mtime % 1000) * 1000 * 1000; (tempI64 = [Math.floor(ctime / 1000)>>>0,(tempDouble = Math.floor(ctime / 1000),(+(Math.abs(tempDouble))) >= 1.0 ? (tempDouble > 0.0 ? (+(Math.floor((tempDouble)/4294967296.0)))>>>0 : (~~((+(Math.ceil((tempDouble - +(((~~(tempDouble)))>>>0))/4294967296.0)))))>>>0) : 0)], HEAP32[(((buf)+(72))>>2)] = tempI64[0],HEAP32[(((buf)+(76))>>2)] = tempI64[1]); - HEAPU32[(((buf)+(80))>>2)] = (ctime % 1000) * 1000; + HEAPU32[(((buf)+(80))>>2)] = (ctime % 1000) * 1000 * 1000; (tempI64 = [stat.ino>>>0,(tempDouble = stat.ino,(+(Math.abs(tempDouble))) >= 1.0 ? (tempDouble > 0.0 ? (+(Math.floor((tempDouble)/4294967296.0)))>>>0 : (~~((+(Math.ceil((tempDouble - +(((~~(tempDouble)))>>>0))/4294967296.0)))))>>>0) : 0)], HEAP32[(((buf)+(88))>>2)] = tempI64[0],HEAP32[(((buf)+(92))>>2)] = tempI64[1]); return 0; }, @@ -4497,14 +4339,13 @@ function ImGui_ImplGlfw_EmscriptenOpenURL(url) { url = url ? UTF8ToString(url) : // implement only the most common ones, and fallback to SIGINT switch (sig) { case 'SIGHUP': return 1; - case 'SIGINT': return 2; case 'SIGQUIT': return 3; case 'SIGFPE': return 8; case 'SIGKILL': return 9; case 'SIGALRM': return 14; case 'SIGTERM': return 15; + default: return 2; } - return 2; // SIGINT } return _W_EXITCODE(0, signalToNumber(ret.signal)); } @@ -4641,9 +4482,20 @@ function ImGui_ImplGlfw_EmscriptenOpenURL(url) { url = url ? UTF8ToString(url) : HEAP32[((daylight)>>2)] = Number(winterOffset != summerOffset); - var extractZone = (date) => date.toLocaleTimeString(undefined, {hour12:false, timeZoneName:'short'}).split(' ')[1]; - var winterName = extractZone(winter); - var summerName = extractZone(summer); + var extractZone = (timezoneOffset) => { + // Why inverse sign? + // Read here https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/getTimezoneOffset + var sign = timezoneOffset >= 0 ? "-" : "+"; + + var absOffset = Math.abs(timezoneOffset) + var hours = String(Math.floor(absOffset / 60)).padStart(2, "0"); + var minutes = String(absOffset % 60).padStart(2, "0"); + + return `UTC${sign}${hours}${minutes}`; + } + + var winterName = extractZone(winterOffset); + var summerName = extractZone(summerOffset); assert(winterName); assert(summerName); assert(lengthBytesUTF8(winterName) <= 16, `timezone name truncated to fit in TZNAME_MAX (${winterName})`); @@ -4956,9 +4808,7 @@ function ImGui_ImplGlfw_EmscriptenOpenURL(url) { url = url ? UTF8ToString(url) : // Insert the result into the buffer. argText = prefix + argText; - argText.split('').forEach(function(chr) { - ret.push(chr.charCodeAt(0)); - }); + argText.split('').forEach((chr) => ret.push(chr.charCodeAt(0))); break; } case 'f': case 'F': case 'e': case 'E': case 'g': case 'G': { @@ -5049,9 +4899,7 @@ function ImGui_ImplGlfw_EmscriptenOpenURL(url) { url = url ? UTF8ToString(url) : if (next < 97) argText = argText.toUpperCase(); // Insert the result into the buffer. - argText.split('').forEach(function(chr) { - ret.push(chr.charCodeAt(0)); - }); + argText.split('').forEach((chr) => ret.push(chr.charCodeAt(0))); break; } case 's': { @@ -5116,12 +4964,10 @@ function ImGui_ImplGlfw_EmscriptenOpenURL(url) { url = url ? UTF8ToString(url) : return ret; }; - function jsStackTrace() { - return new Error().stack.toString(); - } + var jsStackTrace = () => new Error().stack.toString(); /** @param {number=} flags */ - function getCallstack(flags) { + var getCallstack = (flags) => { var callstack = jsStackTrace(); // Find the symbols in the callstack that corresponds to the functions that @@ -5168,8 +5014,7 @@ function ImGui_ImplGlfw_EmscriptenOpenURL(url) { url = url ? UTF8ToString(url) : lineno = parts[3]; column = parts[4]; } else { - parts = newFirefoxRe.exec(line); - if (!parts) parts = firefoxRe.exec(line); + parts = newFirefoxRe.exec(line) || firefoxRe.exec(line); if (parts && parts.length >= 4) { symbolName = parts[1]; file = parts[2]; @@ -5207,7 +5052,7 @@ function ImGui_ImplGlfw_EmscriptenOpenURL(url) { url = url ? UTF8ToString(url) : // Trim extra whitespace at the end of the output. callstack = callstack.replace(/\s+$/, ''); return callstack; - } + }; var emscriptenLog = (flags, str) => { if (flags & 24) { str = str.replace(/\s+$/, ''); // Ensure the message and the callstack are joined cleanly with exactly one newline. @@ -5234,7 +5079,7 @@ function ImGui_ImplGlfw_EmscriptenOpenURL(url) { url = url ? UTF8ToString(url) : }; var _emscripten_log = (flags, format, varargs) => { var result = formatString(format, varargs); - var str = UTF8ArrayToString(result, 0); + var str = UTF8ArrayToString(result); emscriptenLog(flags, str); }; @@ -5245,9 +5090,10 @@ function ImGui_ImplGlfw_EmscriptenOpenURL(url) { url = url ? UTF8ToString(url) : // casing all heap size related code to treat 0 specially. 2147483648; + var growMemory = (size) => { var b = wasmMemory.buffer; - var pages = (size - b.byteLength + 65535) / 65536; + var pages = ((size - b.byteLength + 65535) / 65536) | 0; try { // round size grow request up to wasm page size (fixed 64KB per spec) wasmMemory.grow(pages); // .grow() takes a delta compared to the previous size @@ -5292,8 +5138,6 @@ function ImGui_ImplGlfw_EmscriptenOpenURL(url) { url = url ? UTF8ToString(url) : return false; } - var alignUp = (x, multiple) => x + (multiple - x % multiple) % multiple; - // Loop through potential heap size increases. If we attempt a too eager // reservation that fails, cut down on the attempted size and reserve a // smaller bump instead. (max 3 times, chosen somewhat arbitrarily) @@ -5302,7 +5146,7 @@ function ImGui_ImplGlfw_EmscriptenOpenURL(url) { url = url ? UTF8ToString(url) : // but limit overreserving (default to capping at +96MB overgrowth at most) overGrownHeapSize = Math.min(overGrownHeapSize, requestedSize + 100663296 ); - var newSize = Math.min(maxHeapSize, alignUp(Math.max(requestedSize, overGrownHeapSize), 65536)); + var newSize = Math.min(maxHeapSize, alignMemory(Math.max(requestedSize, overGrownHeapSize), 65536)); var replacement = growMemory(newSize); if (replacement) { @@ -5345,6 +5189,7 @@ function ImGui_ImplGlfw_EmscriptenOpenURL(url) { url = url ? UTF8ToString(url) : quit_(code, new ExitStatus(code)); }; + /** @suppress {duplicate } */ /** @param {boolean|number=} implicit */ var exitJS = (status, implicit) => { @@ -5385,20 +5230,66 @@ function ImGui_ImplGlfw_EmscriptenOpenURL(url) { url = url ? UTF8ToString(url) : } }; - /** @param {number=} timeout */ - var safeSetTimeout = (func, timeout) => { - - return setTimeout(() => { - - callUserCallback(func); - }, timeout); - }; - - + var _emscripten_set_main_loop_timing = (mode, value) => { + MainLoop.timingMode = mode; + MainLoop.timingValue = value; + if (!MainLoop.func) { + err('emscripten_set_main_loop_timing: Cannot set timing mode for main loop since a main loop does not exist! Call emscripten_set_main_loop first to set one up.'); + return 1; // Return non-zero on failure, can't set timing mode when there is no main loop. + } - var Browser = { - mainLoop:{ + if (!MainLoop.running) { + + MainLoop.running = true; + } + if (mode == 0) { + MainLoop.scheduler = function MainLoop_scheduler_setTimeout() { + var timeUntilNextTick = Math.max(0, MainLoop.tickStartTime + value - _emscripten_get_now())|0; + setTimeout(MainLoop.runner, timeUntilNextTick); // doing this each time means that on exception, we stop + }; + MainLoop.method = 'timeout'; + } else if (mode == 1) { + MainLoop.scheduler = function MainLoop_scheduler_rAF() { + MainLoop.requestAnimationFrame(MainLoop.runner); + }; + MainLoop.method = 'rAF'; + } else if (mode == 2) { + if (typeof MainLoop.setImmediate == 'undefined') { + if (typeof setImmediate == 'undefined') { + // Emulate setImmediate. (note: not a complete polyfill, we don't emulate clearImmediate() to keep code size to minimum, since not needed) + var setImmediates = []; + var emscriptenMainLoopMessageId = 'setimmediate'; + /** @param {Event} event */ + var MainLoop_setImmediate_messageHandler = (event) => { + // When called in current thread or Worker, the main loop ID is structured slightly different to accommodate for --proxy-to-worker runtime listening to Worker events, + // so check for both cases. + if (event.data === emscriptenMainLoopMessageId || event.data.target === emscriptenMainLoopMessageId) { + event.stopPropagation(); + setImmediates.shift()(); + } + }; + addEventListener("message", MainLoop_setImmediate_messageHandler, true); + MainLoop.setImmediate = /** @type{function(function(): ?, ...?): number} */((func) => { + setImmediates.push(func); + if (ENVIRONMENT_IS_WORKER) { + Module['setImmediates'] ??= []; + Module['setImmediates'].push(func); + postMessage({target: emscriptenMainLoopMessageId}); // In --proxy-to-worker, route the message via proxyClient.js + } else postMessage(emscriptenMainLoopMessageId, "*"); // On the main thread, can just send the message to itself. + }); + } else { + MainLoop.setImmediate = setImmediate; + } + } + MainLoop.scheduler = function MainLoop_scheduler_setImmediate() { + MainLoop.setImmediate(MainLoop.runner); + }; + MainLoop.method = 'immediate'; + } + return 0; + }; + var MainLoop = { running:false, scheduler:null, method:"", @@ -5409,996 +5300,403 @@ function ImGui_ImplGlfw_EmscriptenOpenURL(url) { url = url ? UTF8ToString(url) : timingValue:0, currentFrameNumber:0, queue:[], + preMainLoop:[], + postMainLoop:[], pause() { - Browser.mainLoop.scheduler = null; - // Incrementing this signals the previous main loop that it's now become old, and it must return. - Browser.mainLoop.currentlyRunningMainloop++; - }, + MainLoop.scheduler = null; + // Incrementing this signals the previous main loop that it's now become old, and it must return. + MainLoop.currentlyRunningMainloop++; + }, resume() { - Browser.mainLoop.currentlyRunningMainloop++; - var timingMode = Browser.mainLoop.timingMode; - var timingValue = Browser.mainLoop.timingValue; - var func = Browser.mainLoop.func; - Browser.mainLoop.func = null; - // do not set timing and call scheduler, we will do it on the next lines - setMainLoop(func, 0, false, Browser.mainLoop.arg, true); - _emscripten_set_main_loop_timing(timingMode, timingValue); - Browser.mainLoop.scheduler(); - }, + MainLoop.currentlyRunningMainloop++; + var timingMode = MainLoop.timingMode; + var timingValue = MainLoop.timingValue; + var func = MainLoop.func; + MainLoop.func = null; + // do not set timing and call scheduler, we will do it on the next lines + setMainLoop(func, 0, false, MainLoop.arg, true); + _emscripten_set_main_loop_timing(timingMode, timingValue); + MainLoop.scheduler(); + }, updateStatus() { - if (Module['setStatus']) { - var message = Module['statusMessage'] || 'Please wait...'; - var remaining = Browser.mainLoop.remainingBlockers; - var expected = Browser.mainLoop.expectedBlockers; - if (remaining) { - if (remaining < expected) { - Module['setStatus'](`{message} ({expected - remaining}/{expected})`); - } else { - Module['setStatus'](message); - } + if (Module['setStatus']) { + var message = Module['statusMessage'] || 'Please wait...'; + var remaining = MainLoop.remainingBlockers ?? 0; + var expected = MainLoop.expectedBlockers ?? 0; + if (remaining) { + if (remaining < expected) { + Module['setStatus'](`{message} ({expected - remaining}/{expected})`); } else { - Module['setStatus'](''); + Module['setStatus'](message); } + } else { + Module['setStatus'](''); } - }, + } + }, + init() { + Module['preMainLoop'] && MainLoop.preMainLoop.push(Module['preMainLoop']); + Module['postMainLoop'] && MainLoop.postMainLoop.push(Module['postMainLoop']); + }, runIter(func) { - if (ABORT) return; - if (Module['preMainLoop']) { - var preRet = Module['preMainLoop'](); - if (preRet === false) { - return; // |return false| skips a frame - } + if (ABORT) return; + for (var pre of MainLoop.preMainLoop) { + if (pre() === false) { + return; // |return false| skips a frame } - callUserCallback(func); - Module['postMainLoop']?.(); - }, - }, - isFullscreen:false, - pointerLock:false, - moduleContextCreatedCallbacks:[], - workers:[], - init() { - if (Browser.initted) return; - Browser.initted = true; + } + callUserCallback(func); + for (var post of MainLoop.postMainLoop) { + post(); + } + checkStackCookie(); + }, + nextRAF:0, + fakeRequestAnimationFrame(func) { + // try to keep 60fps between calls to here + var now = Date.now(); + if (MainLoop.nextRAF === 0) { + MainLoop.nextRAF = now + 1000/60; + } else { + while (now + 2 >= MainLoop.nextRAF) { // fudge a little, to avoid timer jitter causing us to do lots of delay:0 + MainLoop.nextRAF += 1000/60; + } + } + var delay = Math.max(MainLoop.nextRAF - now, 0); + setTimeout(func, delay); + }, + requestAnimationFrame(func) { + if (typeof requestAnimationFrame == 'function') { + requestAnimationFrame(func); + return; + } + var RAF = MainLoop.fakeRequestAnimationFrame; + RAF(func); + }, + }; - // Support for plugins that can process preloaded files. You can add more of these to - // your app by creating and appending to preloadPlugins. - // - // Each plugin is asked if it can handle a file based on the file's name. If it can, - // it is given the file's raw data. When it is done, it calls a callback with the file's - // (possibly modified) data. For example, a plugin might decompress a file, or it - // might create some side data structure for use later (like an Image element, etc.). - var imagePlugin = {}; - imagePlugin['canHandle'] = function imagePlugin_canHandle(name) { - return !Module.noImageDecoding && /\.(jpg|jpeg|png|bmp)$/i.test(name); - }; - imagePlugin['handle'] = function imagePlugin_handle(byteArray, name, onload, onerror) { - var b = new Blob([byteArray], { type: Browser.getMimetype(name) }); - if (b.size !== byteArray.length) { // Safari bug #118630 - // Safari's Blob can only take an ArrayBuffer - b = new Blob([(new Uint8Array(byteArray)).buffer], { type: Browser.getMimetype(name) }); - } - var url = URL.createObjectURL(b); - assert(typeof url == 'string', 'createObjectURL must return a url as a string'); - var img = new Image(); - img.onload = () => { - assert(img.complete, `Image ${name} could not be decoded`); - var canvas = /** @type {!HTMLCanvasElement} */ (document.createElement('canvas')); - canvas.width = img.width; - canvas.height = img.height; - var ctx = canvas.getContext('2d'); - ctx.drawImage(img, 0, 0); - preloadedImages[name] = canvas; - URL.revokeObjectURL(url); - onload?.(byteArray); - }; - img.onerror = (event) => { - err(`Image ${url} could not be decoded`); - onerror?.(); - }; - img.src = url; - }; - preloadPlugins.push(imagePlugin); + var _emscripten_get_now = () => performance.now(); - var audioPlugin = {}; - audioPlugin['canHandle'] = function audioPlugin_canHandle(name) { - return !Module.noAudioDecoding && name.substr(-4) in { '.ogg': 1, '.wav': 1, '.mp3': 1 }; - }; - audioPlugin['handle'] = function audioPlugin_handle(byteArray, name, onload, onerror) { - var done = false; - function finish(audio) { - if (done) return; - done = true; - preloadedAudios[name] = audio; - onload?.(byteArray); - } - function fail() { - if (done) return; - done = true; - preloadedAudios[name] = new Audio(); // empty shim - onerror?.(); - } - var b = new Blob([byteArray], { type: Browser.getMimetype(name) }); - var url = URL.createObjectURL(b); // XXX we never revoke this! - assert(typeof url == 'string', 'createObjectURL must return a url as a string'); - var audio = new Audio(); - audio.addEventListener('canplaythrough', () => finish(audio), false); // use addEventListener due to chromium bug 124926 - audio.onerror = function audio_onerror(event) { - if (done) return; - err(`warning: browser could not fully decode audio ${name}, trying slower base64 approach`); - function encode64(data) { - var BASE = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'; - var PAD = '='; - var ret = ''; - var leftchar = 0; - var leftbits = 0; - for (var i = 0; i < data.length; i++) { - leftchar = (leftchar << 8) | data[i]; - leftbits += 8; - while (leftbits >= 6) { - var curr = (leftchar >> (leftbits-6)) & 0x3f; - leftbits -= 6; - ret += BASE[curr]; - } - } - if (leftbits == 2) { - ret += BASE[(leftchar&3) << 4]; - ret += PAD + PAD; - } else if (leftbits == 4) { - ret += BASE[(leftchar&0xf) << 2]; - ret += PAD; - } - return ret; - } - audio.src = 'data:audio/x-' + name.substr(-3) + ';base64,' + encode64(byteArray); - finish(audio); // we don't wait for confirmation this worked - but it's worth trying - }; - audio.src = url; - // workaround for chrome bug 124926 - we do not always get oncanplaythrough or onerror - safeSetTimeout(() => { - finish(audio); // try to use it even though it is not necessarily ready to play - }, 10000); - }; - preloadPlugins.push(audioPlugin); - // Canvas event setup + /** + * @param {number=} arg + * @param {boolean=} noSetTiming + */ + var setMainLoop = (iterFunc, fps, simulateInfiniteLoop, arg, noSetTiming) => { + assert(!MainLoop.func, 'emscripten_set_main_loop: there can only be one main loop function at once: call emscripten_cancel_main_loop to cancel the previous one before setting a new one with different parameters.'); + MainLoop.func = iterFunc; + MainLoop.arg = arg; - function pointerLockChange() { - Browser.pointerLock = document['pointerLockElement'] === Module['canvas'] || - document['mozPointerLockElement'] === Module['canvas'] || - document['webkitPointerLockElement'] === Module['canvas'] || - document['msPointerLockElement'] === Module['canvas']; + var thisMainLoopId = MainLoop.currentlyRunningMainloop; + function checkIsRunning() { + if (thisMainLoopId < MainLoop.currentlyRunningMainloop) { + + maybeExit(); + return false; } - var canvas = Module['canvas']; - if (canvas) { - // forced aspect ratio can be enabled by defining 'forcedAspectRatio' on Module - // Module['forcedAspectRatio'] = 4 / 3; + return true; + } - canvas.requestPointerLock = canvas['requestPointerLock'] || - canvas['mozRequestPointerLock'] || - canvas['webkitRequestPointerLock'] || - canvas['msRequestPointerLock'] || - (() => {}); - canvas.exitPointerLock = document['exitPointerLock'] || - document['mozExitPointerLock'] || - document['webkitExitPointerLock'] || - document['msExitPointerLock'] || - (() => {}); // no-op if function does not exist - canvas.exitPointerLock = canvas.exitPointerLock.bind(document); + // We create the loop runner here but it is not actually running until + // _emscripten_set_main_loop_timing is called (which might happen a + // later time). This member signifies that the current runner has not + // yet been started so that we can call runtimeKeepalivePush when it + // gets it timing set for the first time. + MainLoop.running = false; + MainLoop.runner = function MainLoop_runner() { + if (ABORT) return; + if (MainLoop.queue.length > 0) { + var start = Date.now(); + var blocker = MainLoop.queue.shift(); + blocker.func(blocker.arg); + if (MainLoop.remainingBlockers) { + var remaining = MainLoop.remainingBlockers; + var next = remaining%1 == 0 ? remaining-1 : Math.floor(remaining); + if (blocker.counted) { + MainLoop.remainingBlockers = next; + } else { + // not counted, but move the progress along a tiny bit + next = next + 0.5; // do not steal all the next one's progress + MainLoop.remainingBlockers = (8*remaining + next)/9; + } + } + MainLoop.updateStatus(); - document.addEventListener('pointerlockchange', pointerLockChange, false); - document.addEventListener('mozpointerlockchange', pointerLockChange, false); - document.addEventListener('webkitpointerlockchange', pointerLockChange, false); - document.addEventListener('mspointerlockchange', pointerLockChange, false); + // catches pause/resume main loop from blocker execution + if (!checkIsRunning()) return; - if (Module['elementPointerLock']) { - canvas.addEventListener("click", (ev) => { - if (!Browser.pointerLock && Module['canvas'].requestPointerLock) { - Module['canvas'].requestPointerLock(); - ev.preventDefault(); - } - }, false); - } + setTimeout(MainLoop.runner, 0); + return; } - }, - createContext(/** @type {HTMLCanvasElement} */ canvas, useWebGL, setInModule, webGLContextAttributes) { - if (useWebGL && Module.ctx && canvas == Module.canvas) return Module.ctx; // no need to recreate GL context if it's already been created for this canvas. - var ctx; - var contextHandle; - if (useWebGL) { - // For GLES2/desktop GL compatibility, adjust a few defaults to be different to WebGL defaults, so that they align better with the desktop defaults. - var contextAttributes = { - antialias: false, - alpha: false, - majorVersion: (typeof WebGL2RenderingContext != 'undefined') ? 2 : 1, - }; + // catch pauses from non-main loop sources + if (!checkIsRunning()) return; - if (webGLContextAttributes) { - for (var attribute in webGLContextAttributes) { - contextAttributes[attribute] = webGLContextAttributes[attribute]; - } - } + // Implement very basic swap interval control + MainLoop.currentFrameNumber = MainLoop.currentFrameNumber + 1 | 0; + if (MainLoop.timingMode == 1 && MainLoop.timingValue > 1 && MainLoop.currentFrameNumber % MainLoop.timingValue != 0) { + // Not the scheduled time to render this frame - skip. + MainLoop.scheduler(); + return; + } else if (MainLoop.timingMode == 0) { + MainLoop.tickStartTime = _emscripten_get_now(); + } - // This check of existence of GL is here to satisfy Closure compiler, which yells if variable GL is referenced below but GL object is not - // actually compiled in because application is not doing any GL operations. TODO: Ideally if GL is not being used, this function - // Browser.createContext() should not even be emitted. - if (typeof GL != 'undefined') { - contextHandle = GL.createContext(canvas, contextAttributes); - if (contextHandle) { - ctx = GL.getContext(contextHandle).GLctx; - } - } - } else { - ctx = canvas.getContext('2d'); + if (MainLoop.method === 'timeout' && Module.ctx) { + warnOnce('Looks like you are rendering without using requestAnimationFrame for the main loop. You should use 0 for the frame rate in emscripten_set_main_loop in order to use requestAnimationFrame, as that can greatly improve your frame rates!'); + MainLoop.method = ''; // just warn once per call to set main loop } - if (!ctx) return null; + MainLoop.runIter(iterFunc); - if (setInModule) { - if (!useWebGL) assert(typeof GLctx == 'undefined', 'cannot set in module if GLctx is used, but we are a non-GL context that would replace it'); - Module.ctx = ctx; - if (useWebGL) GL.makeContextCurrent(contextHandle); - Module.useWebGL = useWebGL; - Browser.moduleContextCreatedCallbacks.forEach((callback) => callback()); - Browser.init(); - } - return ctx; - }, - destroyContext(canvas, useWebGL, setInModule) {}, - fullscreenHandlersInstalled:false, - lockPointer:undefined, - resizeCanvas:undefined, - requestFullscreen(lockPointer, resizeCanvas) { - Browser.lockPointer = lockPointer; - Browser.resizeCanvas = resizeCanvas; - if (typeof Browser.lockPointer == 'undefined') Browser.lockPointer = true; - if (typeof Browser.resizeCanvas == 'undefined') Browser.resizeCanvas = false; + // catch pauses from the main loop itself + if (!checkIsRunning()) return; - var canvas = Module['canvas']; - function fullscreenChange() { - Browser.isFullscreen = false; - var canvasContainer = canvas.parentNode; - if ((document['fullscreenElement'] || document['mozFullScreenElement'] || - document['msFullscreenElement'] || document['webkitFullscreenElement'] || - document['webkitCurrentFullScreenElement']) === canvasContainer) { - canvas.exitFullscreen = Browser.exitFullscreen; - if (Browser.lockPointer) canvas.requestPointerLock(); - Browser.isFullscreen = true; - if (Browser.resizeCanvas) { - Browser.setFullscreenCanvasSize(); - } else { - Browser.updateCanvasDimensions(canvas); - } - } else { - // remove the full screen specific parent of the canvas again to restore the HTML structure from before going full screen - canvasContainer.parentNode.insertBefore(canvas, canvasContainer); - canvasContainer.parentNode.removeChild(canvasContainer); + MainLoop.scheduler(); + } - if (Browser.resizeCanvas) { - Browser.setWindowedCanvasSize(); - } else { - Browser.updateCanvasDimensions(canvas); - } - } - Module['onFullScreen']?.(Browser.isFullscreen); - Module['onFullscreen']?.(Browser.isFullscreen); + if (!noSetTiming) { + if (fps && fps > 0) { + _emscripten_set_main_loop_timing(0, 1000.0 / fps); + } else { + // Do rAF by rendering each frame (no decimating) + _emscripten_set_main_loop_timing(1, 1); } - if (!Browser.fullscreenHandlersInstalled) { - Browser.fullscreenHandlersInstalled = true; - document.addEventListener('fullscreenchange', fullscreenChange, false); - document.addEventListener('mozfullscreenchange', fullscreenChange, false); - document.addEventListener('webkitfullscreenchange', fullscreenChange, false); - document.addEventListener('MSFullscreenChange', fullscreenChange, false); - } + MainLoop.scheduler(); + } - // create a new parent to ensure the canvas has no siblings. this allows browsers to optimize full screen performance when its parent is the full screen root - var canvasContainer = document.createElement("div"); - canvas.parentNode.insertBefore(canvasContainer, canvas); - canvasContainer.appendChild(canvas); + if (simulateInfiniteLoop) { + throw 'unwind'; + } + }; - // use parent of canvas as full screen root to allow aspect ratio correction (Firefox stretches the root to screen size) - canvasContainer.requestFullscreen = canvasContainer['requestFullscreen'] || - canvasContainer['mozRequestFullScreen'] || - canvasContainer['msRequestFullscreen'] || - (canvasContainer['webkitRequestFullscreen'] ? () => canvasContainer['webkitRequestFullscreen'](Element['ALLOW_KEYBOARD_INPUT']) : null) || - (canvasContainer['webkitRequestFullScreen'] ? () => canvasContainer['webkitRequestFullScreen'](Element['ALLOW_KEYBOARD_INPUT']) : null); + var wasmTableMirror = []; - canvasContainer.requestFullscreen(); - }, - requestFullScreen() { - abort('Module.requestFullScreen has been replaced by Module.requestFullscreen (without a capital S)'); - }, - exitFullscreen() { - // This is workaround for chrome. Trying to exit from fullscreen - // not in fullscreen state will cause "TypeError: Document not active" - // in chrome. See https://github.com/emscripten-core/emscripten/pull/8236 - if (!Browser.isFullscreen) { - return false; + /** @type {WebAssembly.Table} */ + var wasmTable; + var getWasmTableEntry = (funcPtr) => { + var func = wasmTableMirror[funcPtr]; + if (!func) { + if (funcPtr >= wasmTableMirror.length) wasmTableMirror.length = funcPtr + 1; + wasmTableMirror[funcPtr] = func = wasmTable.get(funcPtr); + } + assert(wasmTable.get(funcPtr) == func, 'JavaScript-side Wasm function table mirror is out of date!'); + return func; + }; + var _emscripten_set_main_loop = (func, fps, simulateInfiniteLoop) => { + var iterFunc = getWasmTableEntry(func); + setMainLoop(iterFunc, fps, simulateInfiniteLoop); + }; + + var JSEvents = { + removeAllEventListeners() { + while (JSEvents.eventHandlers.length) { + JSEvents._removeHandler(JSEvents.eventHandlers.length - 1); } - - var CFS = document['exitFullscreen'] || - document['cancelFullScreen'] || - document['mozCancelFullScreen'] || - document['msExitFullscreen'] || - document['webkitCancelFullScreen'] || - (() => {}); - CFS.apply(document, []); - return true; + JSEvents.deferredCalls = []; }, - nextRAF:0, - fakeRequestAnimationFrame(func) { - // try to keep 60fps between calls to here - var now = Date.now(); - if (Browser.nextRAF === 0) { - Browser.nextRAF = now + 1000/60; - } else { - while (now + 2 >= Browser.nextRAF) { // fudge a little, to avoid timer jitter causing us to do lots of delay:0 - Browser.nextRAF += 1000/60; + inEventHandler:0, + deferredCalls:[], + deferCall(targetFunction, precedence, argsList) { + function arraysHaveEqualContent(arrA, arrB) { + if (arrA.length != arrB.length) return false; + + for (var i in arrA) { + if (arrA[i] != arrB[i]) return false; } + return true; } - var delay = Math.max(Browser.nextRAF - now, 0); - setTimeout(func, delay); - }, - requestAnimationFrame(func) { - if (typeof requestAnimationFrame == 'function') { - requestAnimationFrame(func); - return; + // Test if the given call was already queued, and if so, don't add it again. + for (var call of JSEvents.deferredCalls) { + if (call.targetFunction == targetFunction && arraysHaveEqualContent(call.argsList, argsList)) { + return; + } } - var RAF = Browser.fakeRequestAnimationFrame; - RAF(func); - }, - safeSetTimeout(func, timeout) { - // Legacy function, this is used by the SDL2 port so we need to keep it - // around at least until that is updated. - // See https://github.com/libsdl-org/SDL/pull/6304 - return safeSetTimeout(func, timeout); - }, - safeRequestAnimationFrame(func) { - - return Browser.requestAnimationFrame(() => { - - callUserCallback(func); + JSEvents.deferredCalls.push({ + targetFunction, + precedence, + argsList }); + + JSEvents.deferredCalls.sort((x,y) => x.precedence < y.precedence); }, - getMimetype(name) { - return { - 'jpg': 'image/jpeg', - 'jpeg': 'image/jpeg', - 'png': 'image/png', - 'bmp': 'image/bmp', - 'ogg': 'audio/ogg', - 'wav': 'audio/wav', - 'mp3': 'audio/mpeg' - }[name.substr(name.lastIndexOf('.')+1)]; - }, - getUserMedia(func) { - window.getUserMedia ||= navigator['getUserMedia'] || - navigator['mozGetUserMedia']; - window.getUserMedia(func); + removeDeferredCalls(targetFunction) { + JSEvents.deferredCalls = JSEvents.deferredCalls.filter((call) => call.targetFunction != targetFunction); }, - getMovementX(event) { - return event['movementX'] || - event['mozMovementX'] || - event['webkitMovementX'] || - 0; + canPerformEventHandlerRequests() { + if (navigator.userActivation) { + // Verify against transient activation status from UserActivation API + // whether it is possible to perform a request here without needing to defer. See + // https://developer.mozilla.org/en-US/docs/Web/Security/User_activation#transient_activation + // and https://caniuse.com/mdn-api_useractivation + // At the time of writing, Firefox does not support this API: https://bugzilla.mozilla.org/show_bug.cgi?id=1791079 + return navigator.userActivation.isActive; + } + + return JSEvents.inEventHandler && JSEvents.currentEventHandler.allowsDeferredCalls; }, - getMovementY(event) { - return event['movementY'] || - event['mozMovementY'] || - event['webkitMovementY'] || - 0; + runDeferredCalls() { + if (!JSEvents.canPerformEventHandlerRequests()) { + return; + } + var deferredCalls = JSEvents.deferredCalls; + JSEvents.deferredCalls = []; + for (var call of deferredCalls) { + call.targetFunction(...call.argsList); + } }, - getMouseWheelDelta(event) { - var delta = 0; - switch (event.type) { - case 'DOMMouseScroll': - // 3 lines make up a step - delta = event.detail / 3; - break; - case 'mousewheel': - // 120 units make up a step - delta = event.wheelDelta / 120; - break; - case 'wheel': - delta = event.deltaY - switch (event.deltaMode) { - case 0: - // DOM_DELTA_PIXEL: 100 pixels make up a step - delta /= 100; - break; - case 1: - // DOM_DELTA_LINE: 3 lines make up a step - delta /= 3; - break; - case 2: - // DOM_DELTA_PAGE: A page makes up 80 steps - delta *= 80; - break; - default: - throw 'unrecognized mouse wheel delta mode: ' + event.deltaMode; - } - break; - default: - throw 'unrecognized mouse wheel event: ' + event.type; + eventHandlers:[], + removeAllHandlersOnTarget:(target, eventTypeString) => { + for (var i = 0; i < JSEvents.eventHandlers.length; ++i) { + if (JSEvents.eventHandlers[i].target == target && + (!eventTypeString || eventTypeString == JSEvents.eventHandlers[i].eventTypeString)) { + JSEvents._removeHandler(i--); + } } - return delta; }, - mouseX:0, - mouseY:0, - mouseMovementX:0, - mouseMovementY:0, - touches:{ - }, - lastTouches:{ - }, - calculateMouseCoords(pageX, pageY) { - // Calculate the movement based on the changes - // in the coordinates. - var rect = Module["canvas"].getBoundingClientRect(); - var cw = Module["canvas"].width; - var ch = Module["canvas"].height; - - // Neither .scrollX or .pageXOffset are defined in a spec, but - // we prefer .scrollX because it is currently in a spec draft. - // (see: http://www.w3.org/TR/2013/WD-cssom-view-20131217/) - var scrollX = ((typeof window.scrollX != 'undefined') ? window.scrollX : window.pageXOffset); - var scrollY = ((typeof window.scrollY != 'undefined') ? window.scrollY : window.pageYOffset); - // If this assert lands, it's likely because the browser doesn't support scrollX or pageXOffset - // and we have no viable fallback. - assert((typeof scrollX != 'undefined') && (typeof scrollY != 'undefined'), 'Unable to retrieve scroll position, mouse positions likely broken.'); - var adjustedX = pageX - (scrollX + rect.left); - var adjustedY = pageY - (scrollY + rect.top); - - // the canvas might be CSS-scaled compared to its backbuffer; - // SDL-using content will want mouse coordinates in terms - // of backbuffer units. - adjustedX = adjustedX * (cw / rect.width); - adjustedY = adjustedY * (ch / rect.height); + _removeHandler(i) { + var h = JSEvents.eventHandlers[i]; + h.target.removeEventListener(h.eventTypeString, h.eventListenerFunc, h.useCapture); + JSEvents.eventHandlers.splice(i, 1); + }, + registerOrRemoveHandler(eventHandler) { + if (!eventHandler.target) { + err('registerOrRemoveHandler: the target element for event handler registration does not exist, when processing the following event handler registration:'); + console.dir(eventHandler); + return -4; + } + if (eventHandler.callbackfunc) { + eventHandler.eventListenerFunc = function(event) { + // Increment nesting count for the event handler. + ++JSEvents.inEventHandler; + JSEvents.currentEventHandler = eventHandler; + // Process any old deferred calls the user has placed. + JSEvents.runDeferredCalls(); + // Process the actual event, calls back to user C code handler. + eventHandler.handlerFunc(event); + // Process any new deferred calls that were placed right now from this event handler. + JSEvents.runDeferredCalls(); + // Out of event handler - restore nesting count. + --JSEvents.inEventHandler; + }; - return { x: adjustedX, y: adjustedY }; + eventHandler.target.addEventListener(eventHandler.eventTypeString, + eventHandler.eventListenerFunc, + eventHandler.useCapture); + JSEvents.eventHandlers.push(eventHandler); + } else { + for (var i = 0; i < JSEvents.eventHandlers.length; ++i) { + if (JSEvents.eventHandlers[i].target == eventHandler.target + && JSEvents.eventHandlers[i].eventTypeString == eventHandler.eventTypeString) { + JSEvents._removeHandler(i--); + } + } + } + return 0; }, - setMouseCoords(pageX, pageY) { - const {x, y} = Browser.calculateMouseCoords(pageX, pageY); - Browser.mouseMovementX = x - Browser.mouseX; - Browser.mouseMovementY = y - Browser.mouseY; - Browser.mouseX = x; - Browser.mouseY = y; + getNodeNameForTarget(target) { + if (!target) return ''; + if (target == window) return '#window'; + if (target == screen) return '#screen'; + return target?.nodeName || ''; }, - calculateMouseEvent(event) { // event should be mousemove, mousedown or mouseup - if (Browser.pointerLock) { - // When the pointer is locked, calculate the coordinates - // based on the movement of the mouse. - // Workaround for Firefox bug 764498 - if (event.type != 'mousemove' && - ('mozMovementX' in event)) { - Browser.mouseMovementX = Browser.mouseMovementY = 0; - } else { - Browser.mouseMovementX = Browser.getMovementX(event); - Browser.mouseMovementY = Browser.getMovementY(event); - } + fullscreenEnabled() { + return document.fullscreenEnabled + // Safari 13.0.3 on macOS Catalina 10.15.1 still ships with prefixed webkitFullscreenEnabled. + // TODO: If Safari at some point ships with unprefixed version, update the version check above. + || document.webkitFullscreenEnabled + ; + }, + }; - // add the mouse delta to the current absolute mouse position - Browser.mouseX += Browser.mouseMovementX; - Browser.mouseY += Browser.mouseMovementY; - } else { - if (event.type === 'touchstart' || event.type === 'touchend' || event.type === 'touchmove') { - var touch = event.touch; - if (touch === undefined) { - return; // the "touch" property is only defined in SDL - - } - var coords = Browser.calculateMouseCoords(touch.pageX, touch.pageY); - - if (event.type === 'touchstart') { - Browser.lastTouches[touch.identifier] = coords; - Browser.touches[touch.identifier] = coords; - } else if (event.type === 'touchend' || event.type === 'touchmove') { - var last = Browser.touches[touch.identifier]; - last ||= coords; - Browser.lastTouches[touch.identifier] = last; - Browser.touches[touch.identifier] = coords; - } - return; - } - - Browser.setMouseCoords(event.pageX, event.pageY); - } - }, - resizeListeners:[], - updateResizeListeners() { - var canvas = Module['canvas']; - Browser.resizeListeners.forEach((listener) => listener(canvas.width, canvas.height)); - }, - setCanvasSize(width, height, noUpdates) { - var canvas = Module['canvas']; - Browser.updateCanvasDimensions(canvas, width, height); - if (!noUpdates) Browser.updateResizeListeners(); - }, - windowedWidth:0, - windowedHeight:0, - setFullscreenCanvasSize() { - // check if SDL is available - if (typeof SDL != "undefined") { - var flags = HEAPU32[((SDL.screen)>>2)]; - flags = flags | 0x00800000; // set SDL_FULLSCREEN flag - HEAP32[((SDL.screen)>>2)] = flags; - } - Browser.updateCanvasDimensions(Module['canvas']); - Browser.updateResizeListeners(); - }, - setWindowedCanvasSize() { - // check if SDL is available - if (typeof SDL != "undefined") { - var flags = HEAPU32[((SDL.screen)>>2)]; - flags = flags & ~0x00800000; // clear SDL_FULLSCREEN flag - HEAP32[((SDL.screen)>>2)] = flags; - } - Browser.updateCanvasDimensions(Module['canvas']); - Browser.updateResizeListeners(); - }, - updateCanvasDimensions(canvas, wNative, hNative) { - if (wNative && hNative) { - canvas.widthNative = wNative; - canvas.heightNative = hNative; - } else { - wNative = canvas.widthNative; - hNative = canvas.heightNative; - } - var w = wNative; - var h = hNative; - if (Module['forcedAspectRatio'] && Module['forcedAspectRatio'] > 0) { - if (w/h < Module['forcedAspectRatio']) { - w = Math.round(h * Module['forcedAspectRatio']); - } else { - h = Math.round(w / Module['forcedAspectRatio']); - } - } - if (((document['fullscreenElement'] || document['mozFullScreenElement'] || - document['msFullscreenElement'] || document['webkitFullscreenElement'] || - document['webkitCurrentFullScreenElement']) === canvas.parentNode) && (typeof screen != 'undefined')) { - var factor = Math.min(screen.width / w, screen.height / h); - w = Math.round(w * factor); - h = Math.round(h * factor); - } - if (Browser.resizeCanvas) { - if (canvas.width != w) canvas.width = w; - if (canvas.height != h) canvas.height = h; - if (typeof canvas.style != 'undefined') { - canvas.style.removeProperty( "width"); - canvas.style.removeProperty("height"); - } - } else { - if (canvas.width != wNative) canvas.width = wNative; - if (canvas.height != hNative) canvas.height = hNative; - if (typeof canvas.style != 'undefined') { - if (w != wNative || h != hNative) { - canvas.style.setProperty( "width", w + "px", "important"); - canvas.style.setProperty("height", h + "px", "important"); - } else { - canvas.style.removeProperty( "width"); - canvas.style.removeProperty("height"); - } - } - } - }, - }; - var _emscripten_set_main_loop_timing = (mode, value) => { - Browser.mainLoop.timingMode = mode; - Browser.mainLoop.timingValue = value; - - if (!Browser.mainLoop.func) { - err('emscripten_set_main_loop_timing: Cannot set timing mode for main loop since a main loop does not exist! Call emscripten_set_main_loop first to set one up.'); - return 1; // Return non-zero on failure, can't set timing mode when there is no main loop. - } - - if (!Browser.mainLoop.running) { - - Browser.mainLoop.running = true; - } - if (mode == 0) { - Browser.mainLoop.scheduler = function Browser_mainLoop_scheduler_setTimeout() { - var timeUntilNextTick = Math.max(0, Browser.mainLoop.tickStartTime + value - _emscripten_get_now())|0; - setTimeout(Browser.mainLoop.runner, timeUntilNextTick); // doing this each time means that on exception, we stop - }; - Browser.mainLoop.method = 'timeout'; - } else if (mode == 1) { - Browser.mainLoop.scheduler = function Browser_mainLoop_scheduler_rAF() { - Browser.requestAnimationFrame(Browser.mainLoop.runner); - }; - Browser.mainLoop.method = 'rAF'; - } else if (mode == 2) { - if (typeof Browser.setImmediate == 'undefined') { - if (typeof setImmediate == 'undefined') { - // Emulate setImmediate. (note: not a complete polyfill, we don't emulate clearImmediate() to keep code size to minimum, since not needed) - var setImmediates = []; - var emscriptenMainLoopMessageId = 'setimmediate'; - /** @param {Event} event */ - var Browser_setImmediate_messageHandler = (event) => { - // When called in current thread or Worker, the main loop ID is structured slightly different to accommodate for --proxy-to-worker runtime listening to Worker events, - // so check for both cases. - if (event.data === emscriptenMainLoopMessageId || event.data.target === emscriptenMainLoopMessageId) { - event.stopPropagation(); - setImmediates.shift()(); - } - }; - addEventListener("message", Browser_setImmediate_messageHandler, true); - Browser.setImmediate = /** @type{function(function(): ?, ...?): number} */(function Browser_emulated_setImmediate(func) { - setImmediates.push(func); - if (ENVIRONMENT_IS_WORKER) { - if (Module['setImmediates'] === undefined) Module['setImmediates'] = []; - Module['setImmediates'].push(func); - postMessage({target: emscriptenMainLoopMessageId}); // In --proxy-to-worker, route the message via proxyClient.js - } else postMessage(emscriptenMainLoopMessageId, "*"); // On the main thread, can just send the message to itself. - }); - } else { - Browser.setImmediate = setImmediate; - } - } - Browser.mainLoop.scheduler = function Browser_mainLoop_scheduler_setImmediate() { - Browser.setImmediate(Browser.mainLoop.runner); - }; - Browser.mainLoop.method = 'immediate'; - } - return 0; - }; - - var _emscripten_get_now; - // Modern environment where performance.now() is supported: - // N.B. a shorter form "_emscripten_get_now = performance.now;" is - // unfortunately not allowed even in current browsers (e.g. FF Nightly 75). - _emscripten_get_now = () => performance.now(); - ; - - - /** - * @param {number=} arg - * @param {boolean=} noSetTiming - */ - var setMainLoop = (browserIterationFunc, fps, simulateInfiniteLoop, arg, noSetTiming) => { - assert(!Browser.mainLoop.func, 'emscripten_set_main_loop: there can only be one main loop function at once: call emscripten_cancel_main_loop to cancel the previous one before setting a new one with different parameters.'); - Browser.mainLoop.func = browserIterationFunc; - Browser.mainLoop.arg = arg; - - // Closure compiler bug(?): Closure does not see that the assignment - // var thisMainLoopId = Browser.mainLoop.currentlyRunningMainloop - // is a value copy of a number (even with the JSDoc @type annotation) - // but optimizeis the code as if the assignment was a reference assignment, - // which results in Browser.mainLoop.pause() not working. Hence use a - // workaround to make Closure believe this is a value copy that should occur: - // (TODO: Minimize this down to a small test case and report - was unable - // to reproduce in a small written test case) - /** @type{number} */ - var thisMainLoopId = (() => Browser.mainLoop.currentlyRunningMainloop)(); - function checkIsRunning() { - if (thisMainLoopId < Browser.mainLoop.currentlyRunningMainloop) { - - return false; - } - return true; - } - - // We create the loop runner here but it is not actually running until - // _emscripten_set_main_loop_timing is called (which might happen a - // later time). This member signifies that the current runner has not - // yet been started so that we can call runtimeKeepalivePush when it - // gets it timing set for the first time. - Browser.mainLoop.running = false; - Browser.mainLoop.runner = function Browser_mainLoop_runner() { - if (ABORT) return; - if (Browser.mainLoop.queue.length > 0) { - var start = Date.now(); - var blocker = Browser.mainLoop.queue.shift(); - blocker.func(blocker.arg); - if (Browser.mainLoop.remainingBlockers) { - var remaining = Browser.mainLoop.remainingBlockers; - var next = remaining%1 == 0 ? remaining-1 : Math.floor(remaining); - if (blocker.counted) { - Browser.mainLoop.remainingBlockers = next; - } else { - // not counted, but move the progress along a tiny bit - next = next + 0.5; // do not steal all the next one's progress - Browser.mainLoop.remainingBlockers = (8*remaining + next)/9; - } - } - Browser.mainLoop.updateStatus(); - // catches pause/resume main loop from blocker execution - if (!checkIsRunning()) return; - - setTimeout(Browser.mainLoop.runner, 0); - return; - } - // catch pauses from non-main loop sources - if (!checkIsRunning()) return; + /** @type {Object} */ + var specialHTMLTargets = [0, typeof document != 'undefined' ? document : 0, typeof window != 'undefined' ? window : 0]; + var getBoundingClientRect = (e) => specialHTMLTargets.indexOf(e) < 0 ? e.getBoundingClientRect() : {'left':0,'top':0}; - // Implement very basic swap interval control - Browser.mainLoop.currentFrameNumber = Browser.mainLoop.currentFrameNumber + 1 | 0; - if (Browser.mainLoop.timingMode == 1 && Browser.mainLoop.timingValue > 1 && Browser.mainLoop.currentFrameNumber % Browser.mainLoop.timingValue != 0) { - // Not the scheduled time to render this frame - skip. - Browser.mainLoop.scheduler(); - return; - } else if (Browser.mainLoop.timingMode == 0) { - Browser.mainLoop.tickStartTime = _emscripten_get_now(); - } + var fillMouseEventData = (eventStruct, e, target) => { + assert(eventStruct % 4 == 0); + HEAPF64[((eventStruct)>>3)] = e.timeStamp; + var idx = ((eventStruct)>>2); + HEAP32[idx + 2] = e.screenX; + HEAP32[idx + 3] = e.screenY; + HEAP32[idx + 4] = e.clientX; + HEAP32[idx + 5] = e.clientY; + HEAP8[eventStruct + 24] = e.ctrlKey; + HEAP8[eventStruct + 25] = e.shiftKey; + HEAP8[eventStruct + 26] = e.altKey; + HEAP8[eventStruct + 27] = e.metaKey; + HEAP16[idx*2 + 14] = e.button; + HEAP16[idx*2 + 15] = e.buttons; + + HEAP32[idx + 8] = e["movementX"] + ; - // Signal GL rendering layer that processing of a new frame is about to start. This helps it optimize - // VBO double-buffering and reduce GPU stalls. - GL.newRenderingFrameStarted(); + HEAP32[idx + 9] = e["movementY"] + ; - if (Browser.mainLoop.method === 'timeout' && Module.ctx) { - warnOnce('Looks like you are rendering without using requestAnimationFrame for the main loop. You should use 0 for the frame rate in emscripten_set_main_loop in order to use requestAnimationFrame, as that can greatly improve your frame rates!'); - Browser.mainLoop.method = ''; // just warn once per call to set main loop - } + // Note: rect contains doubles (truncated to placate SAFE_HEAP, which is the same behaviour when writing to HEAP32 anyway) + var rect = getBoundingClientRect(target); + HEAP32[idx + 10] = e.clientX - (rect.left | 0); + HEAP32[idx + 11] = e.clientY - (rect.top | 0); - Browser.mainLoop.runIter(browserIterationFunc); + }; - checkStackCookie(); + var maybeCStringToJsString = (cString) => { + // "cString > 2" checks if the input is a number, and isn't of the special + // values we accept here, EMSCRIPTEN_EVENT_TARGET_* (which map to 0, 1, 2). + // In other words, if cString > 2 then it's a pointer to a valid place in + // memory, and points to a C string. + return cString > 2 ? UTF8ToString(cString) : cString; + }; - // catch pauses from the main loop itself - if (!checkIsRunning()) return; + var findEventTarget = (target) => { + target = maybeCStringToJsString(target); + var domElement = specialHTMLTargets[target] || (typeof document != 'undefined' ? document.querySelector(target) : undefined); + return domElement; + }; - // Queue new audio data. This is important to be right after the main loop invocation, so that we will immediately be able - // to queue the newest produced audio samples. - // TODO: Consider adding pre- and post- rAF callbacks so that GL.newRenderingFrameStarted() and SDL.audio.queueNewAudioData() - // do not need to be hardcoded into this function, but can be more generic. - if (typeof SDL == 'object') SDL.audio?.queueNewAudioData?.(); - Browser.mainLoop.scheduler(); - } + var registerWheelEventCallback = (target, userData, useCapture, callbackfunc, eventTypeId, eventTypeString, targetThread) => { + JSEvents.wheelEvent ||= _malloc(96); - if (!noSetTiming) { - if (fps && fps > 0) { - _emscripten_set_main_loop_timing(0, 1000.0 / fps); - } else { - // Do rAF by rendering each frame (no decimating) - _emscripten_set_main_loop_timing(1, 1); - } + // The DOM Level 3 events spec event 'wheel' + var wheelHandlerFunc = (e = event) => { + var wheelEvent = JSEvents.wheelEvent; + fillMouseEventData(wheelEvent, e, target); + HEAPF64[(((wheelEvent)+(64))>>3)] = e["deltaX"]; + HEAPF64[(((wheelEvent)+(72))>>3)] = e["deltaY"]; + HEAPF64[(((wheelEvent)+(80))>>3)] = e["deltaZ"]; + HEAP32[(((wheelEvent)+(88))>>2)] = e["deltaMode"]; + if (getWasmTableEntry(callbackfunc)(eventTypeId, wheelEvent, userData)) e.preventDefault(); + }; - Browser.mainLoop.scheduler(); - } + var eventHandler = { + target, + allowsDeferredCalls: true, + eventTypeString, + callbackfunc, + handlerFunc: wheelHandlerFunc, + useCapture + }; + return JSEvents.registerOrRemoveHandler(eventHandler); + }; - if (simulateInfiniteLoop) { - throw 'unwind'; + var _emscripten_set_wheel_callback_on_thread = (target, userData, useCapture, callbackfunc, targetThread) => { + target = findEventTarget(target); + if (!target) return -4; + if (typeof target.onwheel != 'undefined') { + return registerWheelEventCallback(target, userData, useCapture, callbackfunc, 9, "wheel", targetThread); + } else { + return -1; } }; - - - var wasmTableMirror = []; - - /** @type {WebAssembly.Table} */ - var wasmTable; - var getWasmTableEntry = (funcPtr) => { - var func = wasmTableMirror[funcPtr]; - if (!func) { - if (funcPtr >= wasmTableMirror.length) wasmTableMirror.length = funcPtr + 1; - wasmTableMirror[funcPtr] = func = wasmTable.get(funcPtr); - } - assert(wasmTable.get(funcPtr) == func, 'JavaScript-side Wasm function table mirror is out of date!'); - return func; - }; - var _emscripten_set_main_loop = (func, fps, simulateInfiniteLoop) => { - var browserIterationFunc = getWasmTableEntry(func); - setMainLoop(browserIterationFunc, fps, simulateInfiniteLoop); - }; - - var JSEvents = { - removeAllEventListeners() { - while (JSEvents.eventHandlers.length) { - JSEvents._removeHandler(JSEvents.eventHandlers.length - 1); - } - JSEvents.deferredCalls = []; - }, - inEventHandler:0, - deferredCalls:[], - deferCall(targetFunction, precedence, argsList) { - function arraysHaveEqualContent(arrA, arrB) { - if (arrA.length != arrB.length) return false; - - for (var i in arrA) { - if (arrA[i] != arrB[i]) return false; - } - return true; - } - // Test if the given call was already queued, and if so, don't add it again. - for (var i in JSEvents.deferredCalls) { - var call = JSEvents.deferredCalls[i]; - if (call.targetFunction == targetFunction && arraysHaveEqualContent(call.argsList, argsList)) { - return; - } - } - JSEvents.deferredCalls.push({ - targetFunction, - precedence, - argsList - }); - - JSEvents.deferredCalls.sort((x,y) => x.precedence < y.precedence); - }, - removeDeferredCalls(targetFunction) { - for (var i = 0; i < JSEvents.deferredCalls.length; ++i) { - if (JSEvents.deferredCalls[i].targetFunction == targetFunction) { - JSEvents.deferredCalls.splice(i, 1); - --i; - } - } - }, - canPerformEventHandlerRequests() { - if (navigator.userActivation) { - // Verify against transient activation status from UserActivation API - // whether it is possible to perform a request here without needing to defer. See - // https://developer.mozilla.org/en-US/docs/Web/Security/User_activation#transient_activation - // and https://caniuse.com/mdn-api_useractivation - // At the time of writing, Firefox does not support this API: https://bugzilla.mozilla.org/show_bug.cgi?id=1791079 - return navigator.userActivation.isActive; - } - - return JSEvents.inEventHandler && JSEvents.currentEventHandler.allowsDeferredCalls; - }, - runDeferredCalls() { - if (!JSEvents.canPerformEventHandlerRequests()) { - return; - } - for (var i = 0; i < JSEvents.deferredCalls.length; ++i) { - var call = JSEvents.deferredCalls[i]; - JSEvents.deferredCalls.splice(i, 1); - --i; - call.targetFunction(...call.argsList); - } - }, - eventHandlers:[], - removeAllHandlersOnTarget:(target, eventTypeString) => { - for (var i = 0; i < JSEvents.eventHandlers.length; ++i) { - if (JSEvents.eventHandlers[i].target == target && - (!eventTypeString || eventTypeString == JSEvents.eventHandlers[i].eventTypeString)) { - JSEvents._removeHandler(i--); - } - } - }, - _removeHandler(i) { - var h = JSEvents.eventHandlers[i]; - h.target.removeEventListener(h.eventTypeString, h.eventListenerFunc, h.useCapture); - JSEvents.eventHandlers.splice(i, 1); - }, - registerOrRemoveHandler(eventHandler) { - if (!eventHandler.target) { - err('registerOrRemoveHandler: the target element for event handler registration does not exist, when processing the following event handler registration:'); - console.dir(eventHandler); - return -4; - } - if (eventHandler.callbackfunc) { - eventHandler.eventListenerFunc = function(event) { - // Increment nesting count for the event handler. - ++JSEvents.inEventHandler; - JSEvents.currentEventHandler = eventHandler; - // Process any old deferred calls the user has placed. - JSEvents.runDeferredCalls(); - // Process the actual event, calls back to user C code handler. - eventHandler.handlerFunc(event); - // Process any new deferred calls that were placed right now from this event handler. - JSEvents.runDeferredCalls(); - // Out of event handler - restore nesting count. - --JSEvents.inEventHandler; - }; - - eventHandler.target.addEventListener(eventHandler.eventTypeString, - eventHandler.eventListenerFunc, - eventHandler.useCapture); - JSEvents.eventHandlers.push(eventHandler); - } else { - for (var i = 0; i < JSEvents.eventHandlers.length; ++i) { - if (JSEvents.eventHandlers[i].target == eventHandler.target - && JSEvents.eventHandlers[i].eventTypeString == eventHandler.eventTypeString) { - JSEvents._removeHandler(i--); - } - } - } - return 0; - }, - getNodeNameForTarget(target) { - if (!target) return ''; - if (target == window) return '#window'; - if (target == screen) return '#screen'; - return target?.nodeName || ''; - }, - fullscreenEnabled() { - return document.fullscreenEnabled - // Safari 13.0.3 on macOS Catalina 10.15.1 still ships with prefixed webkitFullscreenEnabled. - // TODO: If Safari at some point ships with unprefixed version, update the version check above. - || document.webkitFullscreenEnabled - ; - }, - }; - - - - /** @type {Object} */ - var specialHTMLTargets = [0, typeof document != 'undefined' ? document : 0, typeof window != 'undefined' ? window : 0]; - var getBoundingClientRect = (e) => specialHTMLTargets.indexOf(e) < 0 ? e.getBoundingClientRect() : {'left':0,'top':0}; - - var fillMouseEventData = (eventStruct, e, target) => { - assert(eventStruct % 4 == 0); - HEAPF64[((eventStruct)>>3)] = e.timeStamp; - var idx = ((eventStruct)>>2); - HEAP32[idx + 2] = e.screenX; - HEAP32[idx + 3] = e.screenY; - HEAP32[idx + 4] = e.clientX; - HEAP32[idx + 5] = e.clientY; - HEAP32[idx + 6] = e.ctrlKey; - HEAP32[idx + 7] = e.shiftKey; - HEAP32[idx + 8] = e.altKey; - HEAP32[idx + 9] = e.metaKey; - HEAP16[idx*2 + 20] = e.button; - HEAP16[idx*2 + 21] = e.buttons; - - HEAP32[idx + 11] = e["movementX"] - ; - - HEAP32[idx + 12] = e["movementY"] - ; - - // Note: rect contains doubles (truncated to placate SAFE_HEAP, which is the same behaviour when writing to HEAP32 anyway) - var rect = getBoundingClientRect(target); - HEAP32[idx + 13] = e.clientX - (rect.left | 0); - HEAP32[idx + 14] = e.clientY - (rect.top | 0); - - }; - - var maybeCStringToJsString = (cString) => { - // "cString > 2" checks if the input is a number, and isn't of the special - // values we accept here, EMSCRIPTEN_EVENT_TARGET_* (which map to 0, 1, 2). - // In other words, if cString > 2 then it's a pointer to a valid place in - // memory, and points to a C string. - return cString > 2 ? UTF8ToString(cString) : cString; - }; - - var findEventTarget = (target) => { - target = maybeCStringToJsString(target); - var domElement = specialHTMLTargets[target] || (typeof document != 'undefined' ? document.querySelector(target) : undefined); - return domElement; - }; - - - var registerWheelEventCallback = (target, userData, useCapture, callbackfunc, eventTypeId, eventTypeString, targetThread) => { - if (!JSEvents.wheelEvent) JSEvents.wheelEvent = _malloc(104); - - // The DOM Level 3 events spec event 'wheel' - var wheelHandlerFunc = (e = event) => { - var wheelEvent = JSEvents.wheelEvent; - fillMouseEventData(wheelEvent, e, target); - HEAPF64[(((wheelEvent)+(72))>>3)] = e["deltaX"]; - HEAPF64[(((wheelEvent)+(80))>>3)] = e["deltaY"]; - HEAPF64[(((wheelEvent)+(88))>>3)] = e["deltaZ"]; - HEAP32[(((wheelEvent)+(96))>>2)] = e["deltaMode"]; - if (getWasmTableEntry(callbackfunc)(eventTypeId, wheelEvent, userData)) e.preventDefault(); - }; - - var eventHandler = { - target, - allowsDeferredCalls: true, - eventTypeString, - callbackfunc, - handlerFunc: wheelHandlerFunc, - useCapture - }; - return JSEvents.registerOrRemoveHandler(eventHandler); - }; - - var _emscripten_set_wheel_callback_on_thread = (target, userData, useCapture, callbackfunc, targetThread) => { - target = findEventTarget(target); - if (!target) return -4; - if (typeof target.onwheel != 'undefined') { - return registerWheelEventCallback(target, userData, useCapture, callbackfunc, 9, "wheel", targetThread); - } else { - return -1; - } - }; - - var ENV = { - }; + + var ENV = { + }; var getExecutableName = () => { return thisProgram || './this.program'; @@ -6535,6 +5833,10 @@ function ImGui_ImplGlfw_EmscriptenOpenURL(url) { url = url ? UTF8ToString(url) : var curr = FS.write(stream, HEAP8, ptr, len, offset); if (curr < 0) return -1; ret += curr; + if (curr < len) { + // No more space to write. + break; + } if (typeof offset != 'undefined') { offset += curr; } @@ -6555,9 +5857,14 @@ function ImGui_ImplGlfw_EmscriptenOpenURL(url) { url = url ? UTF8ToString(url) : } } + var GLctx; + var webgl_enable_ANGLE_instanced_arrays = (ctx) => { // Extension available in WebGL 1 from Firefox 26 and Google Chrome 30 onwards. Core feature in WebGL 2. var ext = ctx.getExtension('ANGLE_instanced_arrays'); + // Because this extension is a core function in WebGL 2, assign the extension entry points in place of + // where the core functions will reside in WebGL 2. This way the calling code can call these without + // having to dynamically branch depending if running against WebGL 1 or WebGL 2. if (ext) { ctx['vertexAttribDivisor'] = (index, divisor) => ext['vertexAttribDivisorANGLE'](index, divisor); ctx['drawArraysInstanced'] = (mode, first, count, primcount) => ext['drawArraysInstancedANGLE'](mode, first, count, primcount); @@ -6596,6 +5903,18 @@ function ImGui_ImplGlfw_EmscriptenOpenURL(url) { url = url ? UTF8ToString(url) : return !!(ctx.mdibvbi = ctx.getExtension('WEBGL_multi_draw_instanced_base_vertex_base_instance')); }; + var webgl_enable_EXT_polygon_offset_clamp = (ctx) => { + return !!(ctx.extPolygonOffsetClamp = ctx.getExtension('EXT_polygon_offset_clamp')); + }; + + var webgl_enable_EXT_clip_control = (ctx) => { + return !!(ctx.extClipControl = ctx.getExtension('EXT_clip_control')); + }; + + var webgl_enable_WEBGL_polygon_mode = (ctx) => { + return !!(ctx.webglPolygonMode = ctx.getExtension('WEBGL_polygon_mode')); + }; + var webgl_enable_WEBGL_multi_draw = (ctx) => { // Closure is expected to be allowed to minify the '.multiDrawWebgl' property, so not accessing it quoted. return !!(ctx.multiDrawWebgl = ctx.getExtension('WEBGL_multi_draw')); @@ -6630,9 +5949,11 @@ function ImGui_ImplGlfw_EmscriptenOpenURL(url) { url = url ? UTF8ToString(url) : 'NV_shader_noperspective_interpolation', 'WEBGL_clip_cull_distance', // WebGL 1 and WebGL 2 extensions + 'EXT_clip_control', 'EXT_color_buffer_half_float', 'EXT_depth_clamp', 'EXT_float_blend', + 'EXT_polygon_offset_clamp', 'EXT_texture_compression_bptc', 'EXT_texture_compression_rgtc', 'EXT_texture_filter_anisotropic', @@ -6648,11 +5969,17 @@ function ImGui_ImplGlfw_EmscriptenOpenURL(url) { url = url ? UTF8ToString(url) : 'WEBGL_debug_shaders', 'WEBGL_lose_context', 'WEBGL_multi_draw', + 'WEBGL_polygon_mode' ]; // .getSupportedExtensions() can return null if context is lost, so coerce to empty array. return (ctx.getSupportedExtensions() || []).filter(ext => supportedExtensions.includes(ext)); }; + var registerPreMainLoop = (f) => { + // Does nothing unless $MainLoop is included/used. + typeof MainLoop != 'undefined' && MainLoop.preMainLoop.push(f); + }; + var GL = { counter:1, @@ -6958,6 +6285,11 @@ function ImGui_ImplGlfw_EmscriptenOpenURL(url) { url = url ? UTF8ToString(url) : // Detect the presence of a few extensions manually, ction GL interop // layer itself will need to know if they exist. + // Extensions that are available in both WebGL 1 and WebGL 2 + webgl_enable_WEBGL_multi_draw(GLctx); + webgl_enable_EXT_polygon_offset_clamp(GLctx); + webgl_enable_EXT_clip_control(GLctx); + webgl_enable_WEBGL_polygon_mode(GLctx); // Extensions that are only available in WebGL 1 (the calls will be no-ops // if called on a WebGL 2 context active) webgl_enable_ANGLE_instanced_arrays(GLctx); @@ -6982,8 +6314,6 @@ function ImGui_ImplGlfw_EmscriptenOpenURL(url) { url = url ? UTF8ToString(url) : GLctx.disjointTimerQueryExt = GLctx.getExtension("EXT_disjoint_timer_query"); } - webgl_enable_WEBGL_multi_draw(GLctx); - getEmscriptenSupportedExtensions(GLctx).forEach((ext) => { // WEBGL_lose_context, WEBGL_debug_renderer_info and WEBGL_debug_shaders // are not enabled by default. @@ -7163,6 +6493,7 @@ function ImGui_ImplGlfw_EmscriptenOpenURL(url) { url = url ? UTF8ToString(url) : var _glDrawElements = (mode, count, type, indices) => { var buf; + var vertexes = 0; if (!GLctx.currentElementArrayBufferBinding) { var size = GL.calcBufLength(1, type, 0, count); buf = GL.getTempIndexBuffer(size); @@ -7170,12 +6501,34 @@ function ImGui_ImplGlfw_EmscriptenOpenURL(url) { url = url ? UTF8ToString(url) : GLctx.bufferSubData(0x8893 /*GL_ELEMENT_ARRAY_BUFFER*/, 0, HEAPU8.subarray(indices, indices + size)); + + // Calculating vertex count if shader's attribute data is on client side + if (count > 0) { + for (var i = 0; i < GL.currentContext.maxVertexAttribs; ++i) { + var cb = GL.currentContext.clientBuffers[i]; + if (cb.clientside && cb.enabled) { + let arrayClass; + switch(type) { + case 0x1401 /* GL_UNSIGNED_BYTE */: arrayClass = Uint8Array; break; + case 0x1403 /* GL_UNSIGNED_SHORT */: arrayClass = Uint16Array; break; + case 0x1405 /* GL_UNSIGNED_INT */: arrayClass = Uint32Array; break; + default: + GL.recordError(0x502 /* GL_INVALID_OPERATION */); + return; + } + + vertexes = new arrayClass(HEAPU8.buffer, indices, count).reduce((max, current) => Math.max(max, current)) + 1; + break; + } + } + } + // the index is now 0 indices = 0; } // bind any client-side buffers - GL.preDrawHandleClientVertexAttribBindings(count); + GL.preDrawHandleClientVertexAttribBindings(vertexes); GLctx.drawElements(mode, count, type, indices); @@ -7416,21 +6769,24 @@ function ImGui_ImplGlfw_EmscriptenOpenURL(url) { url = url ? UTF8ToString(url) : HEAP32[((p)>>2)] = log.length + 1; } else if (pname == 0x8B87 /* GL_ACTIVE_UNIFORM_MAX_LENGTH */) { if (!program.maxUniformLength) { - for (var i = 0; i < GLctx.getProgramParameter(program, 0x8B86/*GL_ACTIVE_UNIFORMS*/); ++i) { + var numActiveUniforms = GLctx.getProgramParameter(program, 0x8B86/*GL_ACTIVE_UNIFORMS*/); + for (var i = 0; i < numActiveUniforms; ++i) { program.maxUniformLength = Math.max(program.maxUniformLength, GLctx.getActiveUniform(program, i).name.length+1); } } HEAP32[((p)>>2)] = program.maxUniformLength; } else if (pname == 0x8B8A /* GL_ACTIVE_ATTRIBUTE_MAX_LENGTH */) { if (!program.maxAttributeLength) { - for (var i = 0; i < GLctx.getProgramParameter(program, 0x8B89/*GL_ACTIVE_ATTRIBUTES*/); ++i) { + var numActiveAttributes = GLctx.getProgramParameter(program, 0x8B89/*GL_ACTIVE_ATTRIBUTES*/); + for (var i = 0; i < numActiveAttributes; ++i) { program.maxAttributeLength = Math.max(program.maxAttributeLength, GLctx.getActiveAttrib(program, i).name.length+1); } } HEAP32[((p)>>2)] = program.maxAttributeLength; } else if (pname == 0x8A35 /* GL_ACTIVE_UNIFORM_BLOCK_MAX_NAME_LENGTH */) { if (!program.maxUniformBlockNameLength) { - for (var i = 0; i < GLctx.getProgramParameter(program, 0x8A36/*GL_ACTIVE_UNIFORM_BLOCKS*/); ++i) { + var numActiveUniformBlocks = GLctx.getProgramParameter(program, 0x8A36/*GL_ACTIVE_UNIFORM_BLOCKS*/); + for (var i = 0; i < numActiveUniformBlocks; ++i) { program.maxUniformBlockNameLength = Math.max(program.maxUniformBlockNameLength, GLctx.getActiveUniformBlockName(program, i).length+1); } } @@ -7495,7 +6851,8 @@ function ImGui_ImplGlfw_EmscriptenOpenURL(url) { url = url ? UTF8ToString(url) : // maps integer locations back to uniform name strings, so that we can lazily fetch uniform array locations program.uniformArrayNamesById = {}; - for (i = 0; i < GLctx.getProgramParameter(program, 0x8B86/*GL_ACTIVE_UNIFORMS*/); ++i) { + var numActiveUniforms = GLctx.getProgramParameter(program, 0x8B86/*GL_ACTIVE_UNIFORMS*/); + for (i = 0; i < numActiveUniforms; ++i) { var u = GLctx.getActiveUniform(program, i); var nm = u.name; var sz = u.size; @@ -7700,114 +7057,631 @@ function ImGui_ImplGlfw_EmscriptenOpenURL(url) { url = url ? UTF8ToString(url) : GLctx.texImage2D(target, level, internalFormat, width, height, border, format, type, pixels); return; } - if (pixels) { - var heap = heapObjectForWebGLType(type); - var index = toTypedArrayIndex(pixels, heap); - GLctx.texImage2D(target, level, internalFormat, width, height, border, format, type, heap, index); - return; + if (pixels) { + var heap = heapObjectForWebGLType(type); + var index = toTypedArrayIndex(pixels, heap); + GLctx.texImage2D(target, level, internalFormat, width, height, border, format, type, heap, index); + return; + } + } + var pixelData = pixels ? emscriptenWebGLGetTexPixelData(type, format, width, height, pixels, internalFormat) : null; + GLctx.texImage2D(target, level, internalFormat, width, height, border, format, type, pixelData); + }; + + var _glTexParameteri = (x0, x1, x2) => GLctx.texParameteri(x0, x1, x2); + + var webglGetUniformLocation = (location) => { + var p = GLctx.currentProgram; + + if (p) { + var webglLoc = p.uniformLocsById[location]; + // p.uniformLocsById[location] stores either an integer, or a + // WebGLUniformLocation. + // If an integer, we have not yet bound the location, so do it now. The + // integer value specifies the array index we should bind to. + if (typeof webglLoc == 'number') { + p.uniformLocsById[location] = webglLoc = GLctx.getUniformLocation(p, p.uniformArrayNamesById[location] + (webglLoc > 0 ? `[${webglLoc}]` : '')); + } + // Else an already cached WebGLUniformLocation, return it. + return webglLoc; + } else { + GL.recordError(0x502/*GL_INVALID_OPERATION*/); + } + }; + + var _glUniform1i = (location, v0) => { + GLctx.uniform1i(webglGetUniformLocation(location), v0); + }; + + + var miniTempWebGLFloatBuffers = []; + + var _glUniformMatrix4fv = (location, count, transpose, value) => { + + if (GL.currentContext.version >= 2) { + count && GLctx.uniformMatrix4fv(webglGetUniformLocation(location), !!transpose, HEAPF32, ((value)>>2), count*16); + return; + } + + if (count <= 18) { + // avoid allocation when uploading few enough uniforms + var view = miniTempWebGLFloatBuffers[16*count]; + // hoist the heap out of the loop for size and for pthreads+growth. + var heap = HEAPF32; + value = ((value)>>2); + count *= 16; + for (var i = 0; i < count; i += 16) { + var dst = value + i; + view[i] = heap[dst]; + view[i + 1] = heap[dst + 1]; + view[i + 2] = heap[dst + 2]; + view[i + 3] = heap[dst + 3]; + view[i + 4] = heap[dst + 4]; + view[i + 5] = heap[dst + 5]; + view[i + 6] = heap[dst + 6]; + view[i + 7] = heap[dst + 7]; + view[i + 8] = heap[dst + 8]; + view[i + 9] = heap[dst + 9]; + view[i + 10] = heap[dst + 10]; + view[i + 11] = heap[dst + 11]; + view[i + 12] = heap[dst + 12]; + view[i + 13] = heap[dst + 13]; + view[i + 14] = heap[dst + 14]; + view[i + 15] = heap[dst + 15]; + } + } else + { + var view = HEAPF32.subarray((((value)>>2)), ((value+count*64)>>2)); + } + GLctx.uniformMatrix4fv(webglGetUniformLocation(location), !!transpose, view); + }; + + var _glUseProgram = (program) => { + program = GL.programs[program]; + GLctx.useProgram(program); + // Record the currently active program so that we can access the uniform + // mapping table of that program. + GLctx.currentProgram = program; + }; + + var _glVertexAttribPointer = (index, size, type, normalized, stride, ptr) => { + var cb = GL.currentContext.clientBuffers[index]; + if (!GLctx.currentArrayBufferBinding) { + cb.size = size; + cb.type = type; + cb.normalized = normalized; + cb.stride = stride; + cb.ptr = ptr; + cb.clientside = true; + cb.vertexAttribPointerAdaptor = function(index, size, type, normalized, stride, ptr) { + this.vertexAttribPointer(index, size, type, normalized, stride, ptr); + }; + return; + } + cb.clientside = false; + GLctx.vertexAttribPointer(index, size, type, !!normalized, stride, ptr); + }; + + var _glViewport = (x0, x1, x2, x3) => GLctx.viewport(x0, x1, x2, x3); + + + + + /** @param {number=} timeout */ + var safeSetTimeout = (func, timeout) => { + + return setTimeout(() => { + + callUserCallback(func); + }, timeout); + }; + + + + var Browser = { + useWebGL:false, + isFullscreen:false, + pointerLock:false, + moduleContextCreatedCallbacks:[], + workers:[], + init() { + if (Browser.initted) return; + Browser.initted = true; + + // Support for plugins that can process preloaded files. You can add more of these to + // your app by creating and appending to preloadPlugins. + // + // Each plugin is asked if it can handle a file based on the file's name. If it can, + // it is given the file's raw data. When it is done, it calls a callback with the file's + // (possibly modified) data. For example, a plugin might decompress a file, or it + // might create some side data structure for use later (like an Image element, etc.). + + var imagePlugin = {}; + imagePlugin['canHandle'] = function imagePlugin_canHandle(name) { + return !Module['noImageDecoding'] && /\.(jpg|jpeg|png|bmp|webp)$/i.test(name); + }; + imagePlugin['handle'] = function imagePlugin_handle(byteArray, name, onload, onerror) { + var b = new Blob([byteArray], { type: Browser.getMimetype(name) }); + if (b.size !== byteArray.length) { // Safari bug #118630 + // Safari's Blob can only take an ArrayBuffer + b = new Blob([(new Uint8Array(byteArray)).buffer], { type: Browser.getMimetype(name) }); + } + var url = URL.createObjectURL(b); + assert(typeof url == 'string', 'createObjectURL must return a url as a string'); + var img = new Image(); + img.onload = () => { + assert(img.complete, `Image ${name} could not be decoded`); + var canvas = /** @type {!HTMLCanvasElement} */ (document.createElement('canvas')); + canvas.width = img.width; + canvas.height = img.height; + var ctx = canvas.getContext('2d'); + ctx.drawImage(img, 0, 0); + preloadedImages[name] = canvas; + URL.revokeObjectURL(url); + onload?.(byteArray); + }; + img.onerror = (event) => { + err(`Image ${url} could not be decoded`); + onerror?.(); + }; + img.src = url; + }; + preloadPlugins.push(imagePlugin); + + var audioPlugin = {}; + audioPlugin['canHandle'] = function audioPlugin_canHandle(name) { + return !Module['noAudioDecoding'] && name.substr(-4) in { '.ogg': 1, '.wav': 1, '.mp3': 1 }; + }; + audioPlugin['handle'] = function audioPlugin_handle(byteArray, name, onload, onerror) { + var done = false; + function finish(audio) { + if (done) return; + done = true; + preloadedAudios[name] = audio; + onload?.(byteArray); + } + function fail() { + if (done) return; + done = true; + preloadedAudios[name] = new Audio(); // empty shim + onerror?.(); + } + var b = new Blob([byteArray], { type: Browser.getMimetype(name) }); + var url = URL.createObjectURL(b); // XXX we never revoke this! + assert(typeof url == 'string', 'createObjectURL must return a url as a string'); + var audio = new Audio(); + audio.addEventListener('canplaythrough', () => finish(audio), false); // use addEventListener due to chromium bug 124926 + audio.onerror = function audio_onerror(event) { + if (done) return; + err(`warning: browser could not fully decode audio ${name}, trying slower base64 approach`); + function encode64(data) { + var BASE = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'; + var PAD = '='; + var ret = ''; + var leftchar = 0; + var leftbits = 0; + for (var i = 0; i < data.length; i++) { + leftchar = (leftchar << 8) | data[i]; + leftbits += 8; + while (leftbits >= 6) { + var curr = (leftchar >> (leftbits-6)) & 0x3f; + leftbits -= 6; + ret += BASE[curr]; + } + } + if (leftbits == 2) { + ret += BASE[(leftchar&3) << 4]; + ret += PAD + PAD; + } else if (leftbits == 4) { + ret += BASE[(leftchar&0xf) << 2]; + ret += PAD; + } + return ret; + } + audio.src = 'data:audio/x-' + name.substr(-3) + ';base64,' + encode64(byteArray); + finish(audio); // we don't wait for confirmation this worked - but it's worth trying + }; + audio.src = url; + // workaround for chrome bug 124926 - we do not always get oncanplaythrough or onerror + safeSetTimeout(() => { + finish(audio); // try to use it even though it is not necessarily ready to play + }, 10000); + }; + preloadPlugins.push(audioPlugin); + + // Canvas event setup + + function pointerLockChange() { + Browser.pointerLock = document['pointerLockElement'] === Module['canvas'] || + document['mozPointerLockElement'] === Module['canvas'] || + document['webkitPointerLockElement'] === Module['canvas'] || + document['msPointerLockElement'] === Module['canvas']; + } + var canvas = Module['canvas']; + if (canvas) { + // forced aspect ratio can be enabled by defining 'forcedAspectRatio' on Module + // Module['forcedAspectRatio'] = 4 / 3; + + canvas.requestPointerLock = canvas['requestPointerLock'] || + canvas['mozRequestPointerLock'] || + canvas['webkitRequestPointerLock'] || + canvas['msRequestPointerLock'] || + (() => {}); + canvas.exitPointerLock = document['exitPointerLock'] || + document['mozExitPointerLock'] || + document['webkitExitPointerLock'] || + document['msExitPointerLock'] || + (() => {}); // no-op if function does not exist + canvas.exitPointerLock = canvas.exitPointerLock.bind(document); + + document.addEventListener('pointerlockchange', pointerLockChange, false); + document.addEventListener('mozpointerlockchange', pointerLockChange, false); + document.addEventListener('webkitpointerlockchange', pointerLockChange, false); + document.addEventListener('mspointerlockchange', pointerLockChange, false); + + if (Module['elementPointerLock']) { + canvas.addEventListener("click", (ev) => { + if (!Browser.pointerLock && Module['canvas'].requestPointerLock) { + Module['canvas'].requestPointerLock(); + ev.preventDefault(); + } + }, false); + } + } + }, + createContext(/** @type {HTMLCanvasElement} */ canvas, useWebGL, setInModule, webGLContextAttributes) { + if (useWebGL && Module.ctx && canvas == Module.canvas) return Module.ctx; // no need to recreate GL context if it's already been created for this canvas. + + var ctx; + var contextHandle; + if (useWebGL) { + // For GLES2/desktop GL compatibility, adjust a few defaults to be different to WebGL defaults, so that they align better with the desktop defaults. + var contextAttributes = { + antialias: false, + alpha: false, + majorVersion: (typeof WebGL2RenderingContext != 'undefined') ? 2 : 1, + }; + + if (webGLContextAttributes) { + for (var attribute in webGLContextAttributes) { + contextAttributes[attribute] = webGLContextAttributes[attribute]; + } + } + + // This check of existence of GL is here to satisfy Closure compiler, which yells if variable GL is referenced below but GL object is not + // actually compiled in because application is not doing any GL operations. TODO: Ideally if GL is not being used, this function + // Browser.createContext() should not even be emitted. + if (typeof GL != 'undefined') { + contextHandle = GL.createContext(canvas, contextAttributes); + if (contextHandle) { + ctx = GL.getContext(contextHandle).GLctx; + } + } + } else { + ctx = canvas.getContext('2d'); + } + + if (!ctx) return null; + + if (setInModule) { + if (!useWebGL) assert(typeof GLctx == 'undefined', 'cannot set in module if GLctx is used, but we are a non-GL context that would replace it'); + Module.ctx = ctx; + if (useWebGL) GL.makeContextCurrent(contextHandle); + Browser.useWebGL = useWebGL; + Browser.moduleContextCreatedCallbacks.forEach((callback) => callback()); + Browser.init(); + } + return ctx; + }, + fullscreenHandlersInstalled:false, + lockPointer:undefined, + resizeCanvas:undefined, + requestFullscreen(lockPointer, resizeCanvas) { + Browser.lockPointer = lockPointer; + Browser.resizeCanvas = resizeCanvas; + if (typeof Browser.lockPointer == 'undefined') Browser.lockPointer = true; + if (typeof Browser.resizeCanvas == 'undefined') Browser.resizeCanvas = false; + + var canvas = Module['canvas']; + function fullscreenChange() { + Browser.isFullscreen = false; + var canvasContainer = canvas.parentNode; + if ((document['fullscreenElement'] || document['mozFullScreenElement'] || + document['msFullscreenElement'] || document['webkitFullscreenElement'] || + document['webkitCurrentFullScreenElement']) === canvasContainer) { + canvas.exitFullscreen = Browser.exitFullscreen; + if (Browser.lockPointer) canvas.requestPointerLock(); + Browser.isFullscreen = true; + if (Browser.resizeCanvas) { + Browser.setFullscreenCanvasSize(); + } else { + Browser.updateCanvasDimensions(canvas); + } + } else { + // remove the full screen specific parent of the canvas again to restore the HTML structure from before going full screen + canvasContainer.parentNode.insertBefore(canvas, canvasContainer); + canvasContainer.parentNode.removeChild(canvasContainer); + + if (Browser.resizeCanvas) { + Browser.setWindowedCanvasSize(); + } else { + Browser.updateCanvasDimensions(canvas); + } + } + Module['onFullScreen']?.(Browser.isFullscreen); + Module['onFullscreen']?.(Browser.isFullscreen); + } + + if (!Browser.fullscreenHandlersInstalled) { + Browser.fullscreenHandlersInstalled = true; + document.addEventListener('fullscreenchange', fullscreenChange, false); + document.addEventListener('mozfullscreenchange', fullscreenChange, false); + document.addEventListener('webkitfullscreenchange', fullscreenChange, false); + document.addEventListener('MSFullscreenChange', fullscreenChange, false); + } + + // create a new parent to ensure the canvas has no siblings. this allows browsers to optimize full screen performance when its parent is the full screen root + var canvasContainer = document.createElement("div"); + canvas.parentNode.insertBefore(canvasContainer, canvas); + canvasContainer.appendChild(canvas); + + // use parent of canvas as full screen root to allow aspect ratio correction (Firefox stretches the root to screen size) + canvasContainer.requestFullscreen = canvasContainer['requestFullscreen'] || + canvasContainer['mozRequestFullScreen'] || + canvasContainer['msRequestFullscreen'] || + (canvasContainer['webkitRequestFullscreen'] ? () => canvasContainer['webkitRequestFullscreen'](Element['ALLOW_KEYBOARD_INPUT']) : null) || + (canvasContainer['webkitRequestFullScreen'] ? () => canvasContainer['webkitRequestFullScreen'](Element['ALLOW_KEYBOARD_INPUT']) : null); + + canvasContainer.requestFullscreen(); + }, + requestFullScreen() { + abort('Module.requestFullScreen has been replaced by Module.requestFullscreen (without a capital S)'); + }, + exitFullscreen() { + // This is workaround for chrome. Trying to exit from fullscreen + // not in fullscreen state will cause "TypeError: Document not active" + // in chrome. See https://github.com/emscripten-core/emscripten/pull/8236 + if (!Browser.isFullscreen) { + return false; + } + + var CFS = document['exitFullscreen'] || + document['cancelFullScreen'] || + document['mozCancelFullScreen'] || + document['msExitFullscreen'] || + document['webkitCancelFullScreen'] || + (() => {}); + CFS.apply(document, []); + return true; + }, + safeSetTimeout(func, timeout) { + // Legacy function, this is used by the SDL2 port so we need to keep it + // around at least until that is updated. + // See https://github.com/libsdl-org/SDL/pull/6304 + return safeSetTimeout(func, timeout); + }, + getMimetype(name) { + return { + 'jpg': 'image/jpeg', + 'jpeg': 'image/jpeg', + 'png': 'image/png', + 'bmp': 'image/bmp', + 'ogg': 'audio/ogg', + 'wav': 'audio/wav', + 'mp3': 'audio/mpeg' + }[name.substr(name.lastIndexOf('.')+1)]; + }, + getUserMedia(func) { + window.getUserMedia ||= navigator['getUserMedia'] || + navigator['mozGetUserMedia']; + window.getUserMedia(func); + }, + getMovementX(event) { + return event['movementX'] || + event['mozMovementX'] || + event['webkitMovementX'] || + 0; + }, + getMovementY(event) { + return event['movementY'] || + event['mozMovementY'] || + event['webkitMovementY'] || + 0; + }, + getMouseWheelDelta(event) { + var delta = 0; + switch (event.type) { + case 'DOMMouseScroll': + // 3 lines make up a step + delta = event.detail / 3; + break; + case 'mousewheel': + // 120 units make up a step + delta = event.wheelDelta / 120; + break; + case 'wheel': + delta = event.deltaY + switch (event.deltaMode) { + case 0: + // DOM_DELTA_PIXEL: 100 pixels make up a step + delta /= 100; + break; + case 1: + // DOM_DELTA_LINE: 3 lines make up a step + delta /= 3; + break; + case 2: + // DOM_DELTA_PAGE: A page makes up 80 steps + delta *= 80; + break; + default: + throw 'unrecognized mouse wheel delta mode: ' + event.deltaMode; + } + break; + default: + throw 'unrecognized mouse wheel event: ' + event.type; + } + return delta; + }, + mouseX:0, + mouseY:0, + mouseMovementX:0, + mouseMovementY:0, + touches:{ + }, + lastTouches:{ + }, + calculateMouseCoords(pageX, pageY) { + // Calculate the movement based on the changes + // in the coordinates. + var rect = Module["canvas"].getBoundingClientRect(); + var cw = Module["canvas"].width; + var ch = Module["canvas"].height; + + // Neither .scrollX or .pageXOffset are defined in a spec, but + // we prefer .scrollX because it is currently in a spec draft. + // (see: http://www.w3.org/TR/2013/WD-cssom-view-20131217/) + var scrollX = ((typeof window.scrollX != 'undefined') ? window.scrollX : window.pageXOffset); + var scrollY = ((typeof window.scrollY != 'undefined') ? window.scrollY : window.pageYOffset); + // If this assert lands, it's likely because the browser doesn't support scrollX or pageXOffset + // and we have no viable fallback. + assert((typeof scrollX != 'undefined') && (typeof scrollY != 'undefined'), 'Unable to retrieve scroll position, mouse positions likely broken.'); + var adjustedX = pageX - (scrollX + rect.left); + var adjustedY = pageY - (scrollY + rect.top); + + // the canvas might be CSS-scaled compared to its backbuffer; + // SDL-using content will want mouse coordinates in terms + // of backbuffer units. + adjustedX = adjustedX * (cw / rect.width); + adjustedY = adjustedY * (ch / rect.height); + + return { x: adjustedX, y: adjustedY }; + }, + setMouseCoords(pageX, pageY) { + const {x, y} = Browser.calculateMouseCoords(pageX, pageY); + Browser.mouseMovementX = x - Browser.mouseX; + Browser.mouseMovementY = y - Browser.mouseY; + Browser.mouseX = x; + Browser.mouseY = y; + }, + calculateMouseEvent(event) { // event should be mousemove, mousedown or mouseup + if (Browser.pointerLock) { + // When the pointer is locked, calculate the coordinates + // based on the movement of the mouse. + // Workaround for Firefox bug 764498 + if (event.type != 'mousemove' && + ('mozMovementX' in event)) { + Browser.mouseMovementX = Browser.mouseMovementY = 0; + } else { + Browser.mouseMovementX = Browser.getMovementX(event); + Browser.mouseMovementY = Browser.getMovementY(event); + } + + // add the mouse delta to the current absolute mouse position + Browser.mouseX += Browser.mouseMovementX; + Browser.mouseY += Browser.mouseMovementY; + } else { + if (event.type === 'touchstart' || event.type === 'touchend' || event.type === 'touchmove') { + var touch = event.touch; + if (touch === undefined) { + return; // the "touch" property is only defined in SDL + + } + var coords = Browser.calculateMouseCoords(touch.pageX, touch.pageY); + + if (event.type === 'touchstart') { + Browser.lastTouches[touch.identifier] = coords; + Browser.touches[touch.identifier] = coords; + } else if (event.type === 'touchend' || event.type === 'touchmove') { + var last = Browser.touches[touch.identifier]; + last ||= coords; + Browser.lastTouches[touch.identifier] = last; + Browser.touches[touch.identifier] = coords; + } + return; + } + + Browser.setMouseCoords(event.pageX, event.pageY); + } + }, + resizeListeners:[], + updateResizeListeners() { + var canvas = Module['canvas']; + Browser.resizeListeners.forEach((listener) => listener(canvas.width, canvas.height)); + }, + setCanvasSize(width, height, noUpdates) { + var canvas = Module['canvas']; + Browser.updateCanvasDimensions(canvas, width, height); + if (!noUpdates) Browser.updateResizeListeners(); + }, + windowedWidth:0, + windowedHeight:0, + setFullscreenCanvasSize() { + // check if SDL is available + if (typeof SDL != "undefined") { + var flags = HEAPU32[((SDL.screen)>>2)]; + flags = flags | 0x00800000; // set SDL_FULLSCREEN flag + HEAP32[((SDL.screen)>>2)] = flags; + } + Browser.updateCanvasDimensions(Module['canvas']); + Browser.updateResizeListeners(); + }, + setWindowedCanvasSize() { + // check if SDL is available + if (typeof SDL != "undefined") { + var flags = HEAPU32[((SDL.screen)>>2)]; + flags = flags & ~0x00800000; // clear SDL_FULLSCREEN flag + HEAP32[((SDL.screen)>>2)] = flags; + } + Browser.updateCanvasDimensions(Module['canvas']); + Browser.updateResizeListeners(); + }, + updateCanvasDimensions(canvas, wNative, hNative) { + if (wNative && hNative) { + canvas.widthNative = wNative; + canvas.heightNative = hNative; + } else { + wNative = canvas.widthNative; + hNative = canvas.heightNative; + } + var w = wNative; + var h = hNative; + if (Module['forcedAspectRatio'] && Module['forcedAspectRatio'] > 0) { + if (w/h < Module['forcedAspectRatio']) { + w = Math.round(h * Module['forcedAspectRatio']); + } else { + h = Math.round(w / Module['forcedAspectRatio']); + } } - } - var pixelData = pixels ? emscriptenWebGLGetTexPixelData(type, format, width, height, pixels, internalFormat) : null; - GLctx.texImage2D(target, level, internalFormat, width, height, border, format, type, pixelData); - }; - - var _glTexParameteri = (x0, x1, x2) => GLctx.texParameteri(x0, x1, x2); - - var webglGetUniformLocation = (location) => { - var p = GLctx.currentProgram; - - if (p) { - var webglLoc = p.uniformLocsById[location]; - // p.uniformLocsById[location] stores either an integer, or a - // WebGLUniformLocation. - // If an integer, we have not yet bound the location, so do it now. The - // integer value specifies the array index we should bind to. - if (typeof webglLoc == 'number') { - p.uniformLocsById[location] = webglLoc = GLctx.getUniformLocation(p, p.uniformArrayNamesById[location] + (webglLoc > 0 ? `[${webglLoc}]` : '')); + if (((document['fullscreenElement'] || document['mozFullScreenElement'] || + document['msFullscreenElement'] || document['webkitFullscreenElement'] || + document['webkitCurrentFullScreenElement']) === canvas.parentNode) && (typeof screen != 'undefined')) { + var factor = Math.min(screen.width / w, screen.height / h); + w = Math.round(w * factor); + h = Math.round(h * factor); } - // Else an already cached WebGLUniformLocation, return it. - return webglLoc; - } else { - GL.recordError(0x502/*GL_INVALID_OPERATION*/); - } - }; - - var _glUniform1i = (location, v0) => { - GLctx.uniform1i(webglGetUniformLocation(location), v0); - }; - - - var miniTempWebGLFloatBuffers = []; - - var _glUniformMatrix4fv = (location, count, transpose, value) => { - - if (GL.currentContext.version >= 2) { - count && GLctx.uniformMatrix4fv(webglGetUniformLocation(location), !!transpose, HEAPF32, ((value)>>2), count*16); - return; - } - - if (count <= 18) { - // avoid allocation when uploading few enough uniforms - var view = miniTempWebGLFloatBuffers[16*count]; - // hoist the heap out of the loop for size and for pthreads+growth. - var heap = HEAPF32; - value = ((value)>>2); - for (var i = 0; i < 16 * count; i += 16) { - var dst = value + i; - view[i] = heap[dst]; - view[i + 1] = heap[dst + 1]; - view[i + 2] = heap[dst + 2]; - view[i + 3] = heap[dst + 3]; - view[i + 4] = heap[dst + 4]; - view[i + 5] = heap[dst + 5]; - view[i + 6] = heap[dst + 6]; - view[i + 7] = heap[dst + 7]; - view[i + 8] = heap[dst + 8]; - view[i + 9] = heap[dst + 9]; - view[i + 10] = heap[dst + 10]; - view[i + 11] = heap[dst + 11]; - view[i + 12] = heap[dst + 12]; - view[i + 13] = heap[dst + 13]; - view[i + 14] = heap[dst + 14]; - view[i + 15] = heap[dst + 15]; + if (Browser.resizeCanvas) { + if (canvas.width != w) canvas.width = w; + if (canvas.height != h) canvas.height = h; + if (typeof canvas.style != 'undefined') { + canvas.style.removeProperty( "width"); + canvas.style.removeProperty("height"); + } + } else { + if (canvas.width != wNative) canvas.width = wNative; + if (canvas.height != hNative) canvas.height = hNative; + if (typeof canvas.style != 'undefined') { + if (w != wNative || h != hNative) { + canvas.style.setProperty( "width", w + "px", "important"); + canvas.style.setProperty("height", h + "px", "important"); + } else { + canvas.style.removeProperty( "width"); + canvas.style.removeProperty("height"); + } + } } - } else - { - var view = HEAPF32.subarray((((value)>>2)), ((value+count*64)>>2)); - } - GLctx.uniformMatrix4fv(webglGetUniformLocation(location), !!transpose, view); - }; - - var _glUseProgram = (program) => { - program = GL.programs[program]; - GLctx.useProgram(program); - // Record the currently active program so that we can access the uniform - // mapping table of that program. - GLctx.currentProgram = program; - }; - - var _glVertexAttribPointer = (index, size, type, normalized, stride, ptr) => { - var cb = GL.currentContext.clientBuffers[index]; - if (!GLctx.currentArrayBufferBinding) { - cb.size = size; - cb.type = type; - cb.normalized = normalized; - cb.stride = stride; - cb.ptr = ptr; - cb.clientside = true; - cb.vertexAttribPointerAdaptor = function(index, size, type, normalized, stride, ptr) { - this.vertexAttribPointer(index, size, type, normalized, stride, ptr); - }; - return; - } - cb.clientside = false; - GLctx.vertexAttribPointer(index, size, type, !!normalized, stride, ptr); - }; - - var _glViewport = (x0, x1, x2, x3) => GLctx.viewport(x0, x1, x2, x3); - - - + }, + }; /** @constructor */ function GLFW_Window(id, width, height, framebufferWidth, framebufferHeight, title, monitor, share) { @@ -7858,6 +7732,7 @@ function ImGui_ImplGlfw_EmscriptenOpenURL(url) { url = url ? UTF8ToString(url) : + var stringToNewUTF8 = (str) => { var size = lengthBytesUTF8(str) + 1; var ret = _malloc(size); @@ -8356,9 +8231,9 @@ function ImGui_ImplGlfw_EmscriptenOpenURL(url) { url = url ? UTF8ToString(url) : lastGamepadStateFrame:null, refreshJoysticks:() => { // Produce a new Gamepad API sample if we are ticking a new game frame, or if not using emscripten_set_main_loop() at all to drive animation. - if (Browser.mainLoop.currentFrameNumber !== GLFW.lastGamepadStateFrame || !Browser.mainLoop.currentFrameNumber) { + if (MainLoop.currentFrameNumber !== GLFW.lastGamepadStateFrame || !MainLoop.currentFrameNumber) { GLFW.lastGamepadState = navigator.getGamepads ? navigator.getGamepads() : (navigator.webkitGetGamepads || []); - GLFW.lastGamepadStateFrame = Browser.mainLoop.currentFrameNumber; + GLFW.lastGamepadStateFrame = MainLoop.currentFrameNumber; for (var joy = 0; joy < GLFW.lastGamepadState.length; ++joy) { var gamepad = GLFW.lastGamepadState[joy]; @@ -8728,7 +8603,7 @@ function ImGui_ImplGlfw_EmscriptenOpenURL(url) { url = url ? UTF8ToString(url) : for (var i = 0; i < GLFW.windows.length; i++) if (GLFW.windows[i] !== null) return; - Module.ctx = Browser.destroyContext(Module['canvas'], true, true); + delete Module.ctx; }, swapBuffers:(winid) => { }, @@ -8766,8 +8641,8 @@ function ImGui_ImplGlfw_EmscriptenOpenURL(url) { url = url ? UTF8ToString(url) : Browser.updateResizeListeners(); } } - if (Module['onFullScreen']) Module['onFullScreen'](Browser.isFullscreen); - if (Module['onFullscreen']) Module['onFullscreen'](Browser.isFullscreen); + Module['onFullScreen']?.(Browser.isFullscreen); + Module['onFullscreen']?.(Browser.isFullscreen); } if (!Browser.fullscreenHandlersInstalled) { @@ -9143,308 +9018,6 @@ function ImGui_ImplGlfw_EmscriptenOpenURL(url) { url = url ? UTF8ToString(url) : GLFW.hints[target] = hint; }; - - var arraySum = (array, index) => { - var sum = 0; - for (var i = 0; i <= index; sum += array[i++]) { - // no-op - } - return sum; - }; - - - var MONTH_DAYS_LEAP = [31,29,31,30,31,30,31,31,30,31,30,31]; - - var MONTH_DAYS_REGULAR = [31,28,31,30,31,30,31,31,30,31,30,31]; - var addDays = (date, days) => { - var newDate = new Date(date.getTime()); - while (days > 0) { - var leap = isLeapYear(newDate.getFullYear()); - var currentMonth = newDate.getMonth(); - var daysInCurrentMonth = (leap ? MONTH_DAYS_LEAP : MONTH_DAYS_REGULAR)[currentMonth]; - - if (days > daysInCurrentMonth-newDate.getDate()) { - // we spill over to next month - days -= (daysInCurrentMonth-newDate.getDate()+1); - newDate.setDate(1); - if (currentMonth < 11) { - newDate.setMonth(currentMonth+1) - } else { - newDate.setMonth(0); - newDate.setFullYear(newDate.getFullYear()+1); - } - } else { - // we stay in current month - newDate.setDate(newDate.getDate()+days); - return newDate; - } - } - - return newDate; - }; - - - - - var writeArrayToMemory = (array, buffer) => { - assert(array.length >= 0, 'writeArrayToMemory array must have a length (should be an array or typed array)') - HEAP8.set(array, buffer); - }; - - var _strftime = (s, maxsize, format, tm) => { - // size_t strftime(char *restrict s, size_t maxsize, const char *restrict format, const struct tm *restrict timeptr); - // http://pubs.opengroup.org/onlinepubs/009695399/functions/strftime.html - - var tm_zone = HEAPU32[(((tm)+(40))>>2)]; - - var date = { - tm_sec: HEAP32[((tm)>>2)], - tm_min: HEAP32[(((tm)+(4))>>2)], - tm_hour: HEAP32[(((tm)+(8))>>2)], - tm_mday: HEAP32[(((tm)+(12))>>2)], - tm_mon: HEAP32[(((tm)+(16))>>2)], - tm_year: HEAP32[(((tm)+(20))>>2)], - tm_wday: HEAP32[(((tm)+(24))>>2)], - tm_yday: HEAP32[(((tm)+(28))>>2)], - tm_isdst: HEAP32[(((tm)+(32))>>2)], - tm_gmtoff: HEAP32[(((tm)+(36))>>2)], - tm_zone: tm_zone ? UTF8ToString(tm_zone) : '' - }; - - - var pattern = UTF8ToString(format); - - // expand format - var EXPANSION_RULES_1 = { - '%c': '%a %b %d %H:%M:%S %Y', // Replaced by the locale's appropriate date and time representation - e.g., Mon Aug 3 14:02:01 2013 - '%D': '%m/%d/%y', // Equivalent to %m / %d / %y - '%F': '%Y-%m-%d', // Equivalent to %Y - %m - %d - '%h': '%b', // Equivalent to %b - '%r': '%I:%M:%S %p', // Replaced by the time in a.m. and p.m. notation - '%R': '%H:%M', // Replaced by the time in 24-hour notation - '%T': '%H:%M:%S', // Replaced by the time - '%x': '%m/%d/%y', // Replaced by the locale's appropriate date representation - '%X': '%H:%M:%S', // Replaced by the locale's appropriate time representation - // Modified Conversion Specifiers - '%Ec': '%c', // Replaced by the locale's alternative appropriate date and time representation. - '%EC': '%C', // Replaced by the name of the base year (period) in the locale's alternative representation. - '%Ex': '%m/%d/%y', // Replaced by the locale's alternative date representation. - '%EX': '%H:%M:%S', // Replaced by the locale's alternative time representation. - '%Ey': '%y', // Replaced by the offset from %EC (year only) in the locale's alternative representation. - '%EY': '%Y', // Replaced by the full alternative year representation. - '%Od': '%d', // Replaced by the day of the month, using the locale's alternative numeric symbols, filled as needed with leading zeros if there is any alternative symbol for zero; otherwise, with leading characters. - '%Oe': '%e', // Replaced by the day of the month, using the locale's alternative numeric symbols, filled as needed with leading characters. - '%OH': '%H', // Replaced by the hour (24-hour clock) using the locale's alternative numeric symbols. - '%OI': '%I', // Replaced by the hour (12-hour clock) using the locale's alternative numeric symbols. - '%Om': '%m', // Replaced by the month using the locale's alternative numeric symbols. - '%OM': '%M', // Replaced by the minutes using the locale's alternative numeric symbols. - '%OS': '%S', // Replaced by the seconds using the locale's alternative numeric symbols. - '%Ou': '%u', // Replaced by the weekday as a number in the locale's alternative representation (Monday=1). - '%OU': '%U', // Replaced by the week number of the year (Sunday as the first day of the week, rules corresponding to %U ) using the locale's alternative numeric symbols. - '%OV': '%V', // Replaced by the week number of the year (Monday as the first day of the week, rules corresponding to %V ) using the locale's alternative numeric symbols. - '%Ow': '%w', // Replaced by the number of the weekday (Sunday=0) using the locale's alternative numeric symbols. - '%OW': '%W', // Replaced by the week number of the year (Monday as the first day of the week) using the locale's alternative numeric symbols. - '%Oy': '%y', // Replaced by the year (offset from %C ) using the locale's alternative numeric symbols. - }; - for (var rule in EXPANSION_RULES_1) { - pattern = pattern.replace(new RegExp(rule, 'g'), EXPANSION_RULES_1[rule]); - } - - var WEEKDAYS = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday']; - var MONTHS = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December']; - - function leadingSomething(value, digits, character) { - var str = typeof value == 'number' ? value.toString() : (value || ''); - while (str.length < digits) { - str = character[0]+str; - } - return str; - } - - function leadingNulls(value, digits) { - return leadingSomething(value, digits, '0'); - } - - function compareByDay(date1, date2) { - function sgn(value) { - return value < 0 ? -1 : (value > 0 ? 1 : 0); - } - - var compare; - if ((compare = sgn(date1.getFullYear()-date2.getFullYear())) === 0) { - if ((compare = sgn(date1.getMonth()-date2.getMonth())) === 0) { - compare = sgn(date1.getDate()-date2.getDate()); - } - } - return compare; - } - - function getFirstWeekStartDate(janFourth) { - switch (janFourth.getDay()) { - case 0: // Sunday - return new Date(janFourth.getFullYear()-1, 11, 29); - case 1: // Monday - return janFourth; - case 2: // Tuesday - return new Date(janFourth.getFullYear(), 0, 3); - case 3: // Wednesday - return new Date(janFourth.getFullYear(), 0, 2); - case 4: // Thursday - return new Date(janFourth.getFullYear(), 0, 1); - case 5: // Friday - return new Date(janFourth.getFullYear()-1, 11, 31); - case 6: // Saturday - return new Date(janFourth.getFullYear()-1, 11, 30); - } - } - - function getWeekBasedYear(date) { - var thisDate = addDays(new Date(date.tm_year+1900, 0, 1), date.tm_yday); - - var janFourthThisYear = new Date(thisDate.getFullYear(), 0, 4); - var janFourthNextYear = new Date(thisDate.getFullYear()+1, 0, 4); - - var firstWeekStartThisYear = getFirstWeekStartDate(janFourthThisYear); - var firstWeekStartNextYear = getFirstWeekStartDate(janFourthNextYear); - - if (compareByDay(firstWeekStartThisYear, thisDate) <= 0) { - // this date is after the start of the first week of this year - if (compareByDay(firstWeekStartNextYear, thisDate) <= 0) { - return thisDate.getFullYear()+1; - } - return thisDate.getFullYear(); - } - return thisDate.getFullYear()-1; - } - - var EXPANSION_RULES_2 = { - '%a': (date) => WEEKDAYS[date.tm_wday].substring(0,3) , - '%A': (date) => WEEKDAYS[date.tm_wday], - '%b': (date) => MONTHS[date.tm_mon].substring(0,3), - '%B': (date) => MONTHS[date.tm_mon], - '%C': (date) => { - var year = date.tm_year+1900; - return leadingNulls((year/100)|0,2); - }, - '%d': (date) => leadingNulls(date.tm_mday, 2), - '%e': (date) => leadingSomething(date.tm_mday, 2, ' '), - '%g': (date) => { - // %g, %G, and %V give values according to the ISO 8601:2000 standard week-based year. - // In this system, weeks begin on a Monday and week 1 of the year is the week that includes - // January 4th, which is also the week that includes the first Thursday of the year, and - // is also the first week that contains at least four days in the year. - // If the first Monday of January is the 2nd, 3rd, or 4th, the preceding days are part of - // the last week of the preceding year; thus, for Saturday 2nd January 1999, - // %G is replaced by 1998 and %V is replaced by 53. If December 29th, 30th, - // or 31st is a Monday, it and any following days are part of week 1 of the following year. - // Thus, for Tuesday 30th December 1997, %G is replaced by 1998 and %V is replaced by 01. - - return getWeekBasedYear(date).toString().substring(2); - }, - '%G': getWeekBasedYear, - '%H': (date) => leadingNulls(date.tm_hour, 2), - '%I': (date) => { - var twelveHour = date.tm_hour; - if (twelveHour == 0) twelveHour = 12; - else if (twelveHour > 12) twelveHour -= 12; - return leadingNulls(twelveHour, 2); - }, - '%j': (date) => { - // Day of the year (001-366) - return leadingNulls(date.tm_mday + arraySum(isLeapYear(date.tm_year+1900) ? MONTH_DAYS_LEAP : MONTH_DAYS_REGULAR, date.tm_mon-1), 3); - }, - '%m': (date) => leadingNulls(date.tm_mon+1, 2), - '%M': (date) => leadingNulls(date.tm_min, 2), - '%n': () => '\n', - '%p': (date) => { - if (date.tm_hour >= 0 && date.tm_hour < 12) { - return 'AM'; - } - return 'PM'; - }, - '%S': (date) => leadingNulls(date.tm_sec, 2), - '%t': () => '\t', - '%u': (date) => date.tm_wday || 7, - '%U': (date) => { - var days = date.tm_yday + 7 - date.tm_wday; - return leadingNulls(Math.floor(days / 7), 2); - }, - '%V': (date) => { - // Replaced by the week number of the year (Monday as the first day of the week) - // as a decimal number [01,53]. If the week containing 1 January has four - // or more days in the new year, then it is considered week 1. - // Otherwise, it is the last week of the previous year, and the next week is week 1. - // Both January 4th and the first Thursday of January are always in week 1. [ tm_year, tm_wday, tm_yday] - var val = Math.floor((date.tm_yday + 7 - (date.tm_wday + 6) % 7 ) / 7); - // If 1 Jan is just 1-3 days past Monday, the previous week - // is also in this year. - if ((date.tm_wday + 371 - date.tm_yday - 2) % 7 <= 2) { - val++; - } - if (!val) { - val = 52; - // If 31 December of prev year a Thursday, or Friday of a - // leap year, then the prev year has 53 weeks. - var dec31 = (date.tm_wday + 7 - date.tm_yday - 1) % 7; - if (dec31 == 4 || (dec31 == 5 && isLeapYear(date.tm_year%400-1))) { - val++; - } - } else if (val == 53) { - // If 1 January is not a Thursday, and not a Wednesday of a - // leap year, then this year has only 52 weeks. - var jan1 = (date.tm_wday + 371 - date.tm_yday) % 7; - if (jan1 != 4 && (jan1 != 3 || !isLeapYear(date.tm_year))) - val = 1; - } - return leadingNulls(val, 2); - }, - '%w': (date) => date.tm_wday, - '%W': (date) => { - var days = date.tm_yday + 7 - ((date.tm_wday + 6) % 7); - return leadingNulls(Math.floor(days / 7), 2); - }, - '%y': (date) => { - // Replaced by the last two digits of the year as a decimal number [00,99]. [ tm_year] - return (date.tm_year+1900).toString().substring(2); - }, - // Replaced by the year as a decimal number (for example, 1997). [ tm_year] - '%Y': (date) => date.tm_year+1900, - '%z': (date) => { - // Replaced by the offset from UTC in the ISO 8601:2000 standard format ( +hhmm or -hhmm ). - // For example, "-0430" means 4 hours 30 minutes behind UTC (west of Greenwich). - var off = date.tm_gmtoff; - var ahead = off >= 0; - off = Math.abs(off) / 60; - // convert from minutes into hhmm format (which means 60 minutes = 100 units) - off = (off / 60)*100 + (off % 60); - return (ahead ? '+' : '-') + String("0000" + off).slice(-4); - }, - '%Z': (date) => date.tm_zone, - '%%': () => '%' - }; - - // Replace %% with a pair of NULLs (which cannot occur in a C string), then - // re-inject them after processing. - pattern = pattern.replace(/%%/g, '\0\0') - for (var rule in EXPANSION_RULES_2) { - if (pattern.includes(rule)) { - pattern = pattern.replace(new RegExp(rule, 'g'), EXPANSION_RULES_2[rule](date)); - } - } - pattern = pattern.replace(/\0\0/g, '%') - - var bytes = intArrayFromString(pattern, false); - if (bytes.length > maxsize) { - return 0; - } - - writeArrayToMemory(bytes, s); - return bytes.length-1; - }; - var _strftime_l = (s, maxsize, format, tm, loc) => { - return _strftime(s, maxsize, format, tm); // no locale support yet - }; - @@ -9465,6 +9038,10 @@ function ImGui_ImplGlfw_EmscriptenOpenURL(url) { url = url ? UTF8ToString(url) : return func; }; + var writeArrayToMemory = (array, buffer) => { + assert(array.length >= 0, 'writeArrayToMemory array must have a length (should be an array or typed array)') + HEAP8.set(array, buffer); + }; @@ -9483,7 +9060,6 @@ function ImGui_ImplGlfw_EmscriptenOpenURL(url) { url = url ? UTF8ToString(url) : 'string': (str) => { var ret = 0; if (str !== null && str !== undefined && str !== 0) { // null string - // at most 4 bytes per UTF-8 code point, +1 for the trailing '\0' ret = stringToUTF8OnStack(str); } return ret; @@ -9497,7 +9073,6 @@ function ImGui_ImplGlfw_EmscriptenOpenURL(url) { url = url ? UTF8ToString(url) : function convertReturnValue(ret) { if (returnType === 'string') { - return UTF8ToString(ret); } if (returnType === 'boolean') return Boolean(ret); @@ -9571,24 +9146,39 @@ function ImGui_ImplGlfw_EmscriptenOpenURL(url) { url = url ? UTF8ToString(url) : Module['getExceptionMessage'] = getExceptionMessage; FS.createPreloadedFile = FS_createPreloadedFile; - FS.staticInit();Module["FS_createPath"] = FS.createPath;Module["FS_createDataFile"] = FS.createDataFile;Module["FS_createPreloadedFile"] = FS.createPreloadedFile;Module["FS_unlink"] = FS.unlink;Module["FS_createLazyFile"] = FS.createLazyFile;Module["FS_createDevice"] = FS.createDevice;; + FS.staticInit(); + // Set module methods based on EXPORTED_RUNTIME_METHODS + Module["FS_createPath"] = FS.createPath; + Module["FS_createDataFile"] = FS.createDataFile; + Module["FS_createPreloadedFile"] = FS.createPreloadedFile; + Module["FS_unlink"] = FS.unlink; + Module["FS_createLazyFile"] = FS.createLazyFile; + Module["FS_createDevice"] = FS.createDevice; + ; + + Module["requestAnimationFrame"] = MainLoop.requestAnimationFrame; + Module["pauseMainLoop"] = MainLoop.pause; + Module["resumeMainLoop"] = MainLoop.resume; + MainLoop.init();; + + // Signal GL rendering layer that processing of a new frame is about to + // start. This helps it optimize VBO double-buffering and reduce GPU stalls. + registerPreMainLoop(() => GL.newRenderingFrameStarted()); + ; +var miniTempWebGLFloatBuffersStorage = new Float32Array(288); + // Create GL_POOL_TEMP_BUFFERS_SIZE+1 temporary buffers, for uploads of size 0 through GL_POOL_TEMP_BUFFERS_SIZE inclusive + for (/**@suppress{duplicate}*/var i = 0; i <= 288; ++i) { + miniTempWebGLFloatBuffers[i] = miniTempWebGLFloatBuffersStorage.subarray(0, i); + }; // exports Module["requestFullscreen"] = Browser.requestFullscreen; Module["requestFullScreen"] = Browser.requestFullScreen; - Module["requestAnimationFrame"] = Browser.requestAnimationFrame; Module["setCanvasSize"] = Browser.setCanvasSize; - Module["pauseMainLoop"] = Browser.mainLoop.pause; - Module["resumeMainLoop"] = Browser.mainLoop.resume; Module["getUserMedia"] = Browser.getUserMedia; Module["createContext"] = Browser.createContext; var preloadedImages = {}; var preloadedAudios = {};; -var GLctx;; -var miniTempWebGLFloatBuffersStorage = new Float32Array(288); - for (/**@suppress{duplicate}*/var i = 0; i < 288; ++i) { - miniTempWebGLFloatBuffers[i] = miniTempWebGLFloatBuffersStorage.subarray(0, i); - }; function checkIncomingModuleAPI() { ignoredModuleProp('fetchSettings'); } @@ -9898,9 +9488,7 @@ var wasmImports = { /** @export */ invoke_viiiiiiiiii, /** @export */ - invoke_viiiiiiiiiiiiiii, - /** @export */ - strftime_l: _strftime_l + invoke_viiiiiiiiiiiiiii }; var wasmExports = createWasm(); var ___wasm_call_ctors = createExportWrapper('__wasm_call_ctors', 0); @@ -9912,6 +9500,7 @@ var _cpp_LoadThemeZip = Module['_cpp_LoadThemeZip'] = createExportWrapper('cpp_L var _main = Module['_main'] = createExportWrapper('__main_argc_argv', 2); var ___cxa_free_exception = createExportWrapper('__cxa_free_exception', 1); var _fflush = createExportWrapper('fflush', 1); +var _strerror = createExportWrapper('strerror', 1); var _setThrew = createExportWrapper('setThrew', 2); var __emscripten_tempret_set = createExportWrapper('_emscripten_tempret_set', 1); var _emscripten_stack_init = () => (_emscripten_stack_init = wasmExports['emscripten_stack_init'])(); @@ -9925,7 +9514,7 @@ var ___cxa_decrement_exception_refcount = createExportWrapper('__cxa_decrement_e var ___cxa_increment_exception_refcount = createExportWrapper('__cxa_increment_exception_refcount', 1); var ___get_exception_message = createExportWrapper('__get_exception_message', 3); var ___cxa_can_catch = createExportWrapper('__cxa_can_catch', 3); -var ___cxa_is_pointer_type = createExportWrapper('__cxa_is_pointer_type', 1); +var ___cxa_get_exception_ptr = createExportWrapper('__cxa_get_exception_ptr', 1); var dynCall_iijii = Module['dynCall_iijii'] = createExportWrapper('dynCall_iijii', 6); var dynCall_jiji = Module['dynCall_jiji'] = createExportWrapper('dynCall_jiji', 5); var dynCall_viijii = Module['dynCall_viijii'] = createExportWrapper('dynCall_viijii', 7); @@ -10399,11 +9988,15 @@ var missingLibrarySymbols = [ 'setImmediateWrapped', 'clearImmediateWrapped', 'polyfillSetImmediate', + 'registerPostMainLoop', 'getPromise', 'makePromise', 'idsToPromises', 'makePromiseCallback', 'Browser_asyncPrepareDataCounter', + 'safeRequestAnimationFrame', + 'arraySum', + 'addDays', 'getSocketFromFD', 'getSocketAddress', 'FS_mkdirTree', @@ -10460,21 +10053,11 @@ var unexportedSymbols = [ 'getHeapMax', 'growMemory', 'ENV', - 'MONTH_DAYS_REGULAR', - 'MONTH_DAYS_LEAP', - 'MONTH_DAYS_REGULAR_CUMULATIVE', - 'MONTH_DAYS_LEAP_CUMULATIVE', - 'isLeapYear', - 'ydayFromDate', - 'arraySum', - 'addDays', 'ERRNO_CODES', - 'ERRNO_MESSAGES', + 'strError', 'DNS', 'Protocols', 'Sockets', - 'initRandomFill', - 'randomFill', 'timers', 'warnOnce', 'emscriptenLog', @@ -10532,7 +10115,10 @@ var unexportedSymbols = [ 'getEnvStrings', 'doReadv', 'doWritev', + 'initRandomFill', + 'randomFill', 'safeSetTimeout', + 'registerPreMainLoop', 'promiseMap', 'uncaughtExceptionCount', 'exceptionLast', @@ -10544,9 +10130,14 @@ var unexportedSymbols = [ 'decrementExceptionRefcount', 'getExceptionMessage', 'Browser', - 'setMainLoop', 'getPreloadedImageData__data', 'wget', + 'MONTH_DAYS_REGULAR', + 'MONTH_DAYS_LEAP', + 'MONTH_DAYS_REGULAR_CUMULATIVE', + 'MONTH_DAYS_LEAP_CUMULATIVE', + 'isLeapYear', + 'ydayFromDate', 'SYSCALLS', 'preloadPlugins', 'FS_modeStringToFlags', @@ -10568,6 +10159,9 @@ var unexportedSymbols = [ 'webgl_enable_OES_vertex_array_object', 'webgl_enable_WEBGL_draw_buffers', 'webgl_enable_WEBGL_multi_draw', + 'webgl_enable_EXT_polygon_offset_clamp', + 'webgl_enable_EXT_clip_control', + 'webgl_enable_WEBGL_polygon_mode', 'GL', 'emscriptenWebGLGet', 'computeUnpackAlignedImageSize', @@ -10649,7 +10243,7 @@ function run(args = arguments_) { return; } - stackCheckInit(); + stackCheckInit(); preRun(); @@ -10671,7 +10265,7 @@ function run(args = arguments_) { preMain(); - if (Module['onRuntimeInitialized']) Module['onRuntimeInitialized'](); + Module['onRuntimeInitialized']?.(); if (shouldRunNow) callMain(args); @@ -10680,10 +10274,8 @@ function run(args = arguments_) { if (Module['setStatus']) { Module['setStatus']('Running...'); - setTimeout(function() { - setTimeout(function() { - Module['setStatus'](''); - }, 1); + setTimeout(() => { + setTimeout(() => Module['setStatus'](''), 1); doRun(); }, 1); } else @@ -10714,7 +10306,7 @@ function checkUnflushedContent() { try { // it doesn't matter if it fails _fflush(0); // also flush in the JS FS layer - ['stdout', 'stderr'].forEach(function(name) { + ['stdout', 'stderr'].forEach((name) => { var info = FS.analyzePath('/dev/' + name); if (!info) return; var stream = info.object; diff --git a/docs/udesigner.wasm b/docs/udesigner.wasm index 22cd0bb..fbf678b 100644 Binary files a/docs/udesigner.wasm and b/docs/udesigner.wasm differ diff --git a/libs/Atmosphere-libs b/libs/Atmosphere-libs index bb76786..989fb7b 160000 --- a/libs/Atmosphere-libs +++ b/libs/Atmosphere-libs @@ -1 +1 @@ -Subproject commit bb767869105d0eb5c38425f54bf20614639a078d +Subproject commit 989fb7be0c68bf229fe6789428b6c448b6de142a diff --git a/projects/uLoader/uLoader_applet.json b/projects/uLoader/uLoader_applet.json index e102494..41a882f 100644 --- a/projects/uLoader/uLoader_applet.json +++ b/projects/uLoader/uLoader_applet.json @@ -160,7 +160,8 @@ { "type": "debug_flags", "value": { - "allow_debug": true, + "allow_debug": false, + "force_debug_prod": false, "force_debug": true } }, diff --git a/projects/uLoader/uLoader_application.json b/projects/uLoader/uLoader_application.json index beb6c8c..5c4fc65 100644 --- a/projects/uLoader/uLoader_application.json +++ b/projects/uLoader/uLoader_application.json @@ -160,7 +160,8 @@ { "type": "debug_flags", "value": { - "allow_debug": true, + "allow_debug": false, + "force_debug_prod": false, "force_debug": true } } diff --git a/projects/uManager/Makefile b/projects/uManager/Makefile index 4987766..ee693bb 100644 --- a/projects/uManager/Makefile +++ b/projects/uManager/Makefile @@ -170,11 +170,13 @@ all: $(BUILD) $(BUILD): @[ -d $@ ] || mkdir -p $@ + @$(MAKE) -C $(LIBUCOMMON) @$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile #--------------------------------------------------------------------------------- clean: @echo clean ... + @$(MAKE) clean -C $(LIBUCOMMON) ifeq ($(strip $(APP_JSON)),) @rm -fr $(BUILD) $(TARGET).nro $(TARGET).nacp $(TARGET).elf else diff --git a/projects/uMenu/romfs/lang/fr.json b/projects/uMenu/romfs/lang/fr.json index 2f06c14..23291b6 100644 --- a/projects/uMenu/romfs/lang/fr.json +++ b/projects/uMenu/romfs/lang/fr.json @@ -38,7 +38,7 @@ "app_take_over_done": "Cette application a été sélectionner pour lancer le Homebrew.", "app_unexpected_error": "Oups ! Quelque-chose a planté, n'a pas pu démarrer ou s'est terminé de manière inattendue...", "ulaunch_about": "uLaunch est un remplacement étendu, orienté vers le Homebrew et open-source du menu HOME.\nVeuillez noter que certaines fonctionalités du menu HOME doivent encore être implementé.\n\nSi vous recherchez des thèmes, consultez le subreddit r/uLaunchThemes.\nSi vous souhaitez contribuer, consultez le dépôt GitHub d'uLaunch", - "suspended_app": "Suspended application", + "suspended_app": "Application en suspens", "suspended_app_close": "Voulez-vous vraiment fermer l'application en suspens? Toutes les données non-sauvegardées seront perdues.", "hb_launch": "Lancer Homebrew", "hb_launch_conf": "Comment voulez-vous lancer cet Homebrew?", @@ -185,7 +185,7 @@ "app_verify_wait": "Cette application est actuellement en cours de vérification...", "amiibo_entry_info": "Que voulez-vous faire ?", "amiibo_entry_nickname_owner_settings": "Modifier surnom/propriétaire", - "amiibo_entry_game_data_erase": "Erase game data", + "amiibo_entry_game_data_erase": "Effacer données de jeu", "amiibo_entry_restore": "Restorer fichiers corrompus", "amiibo_entry_format": "Formatter", "lockscreen_info": "Appuyez sur n'importe quelle touche pour déverrouiller" diff --git a/projects/uMenu/uMenu.json b/projects/uMenu/uMenu.json index e6a0004..117dfee 100644 --- a/projects/uMenu/uMenu.json +++ b/projects/uMenu/uMenu.json @@ -143,13 +143,6 @@ { "type": "min_kernel_version", "value": "0x30" - }, - { - "type": "debug_flags", - "value": { - "allow_debug": true, - "force_debug": false - } } ] } \ No newline at end of file