diff options
Diffstat (limited to 'src')
41 files changed, 5985 insertions, 1456 deletions
diff --git a/src/closure-externs.js b/src/closure-externs.js index a82aa669..fe6d84aa 100644 --- a/src/closure-externs.js +++ b/src/closure-externs.js @@ -108,3 +108,63 @@ var flags = {}; flags.binary; +/** + * @fileoverview Definitions for W3C's Gamepad specification. + * @see http://www.w3.org/TR/gamepad/ + * @externs + */ + +/** + * @typedef {{id: string, index: number, timestamp: number, axes: Array.<number>, buttons: Array.<number>}} + */ +var Gamepad; + +/** +* @type {Array.<number>} +*/ +Gamepad.buttons; + +/** +* @type {Array.<number>} +*/ +Gamepad.axes; + +/** +* @type {number} +*/ +Gamepad.index; + +/** +* @type {string} +*/ +Gamepad.id; + +/** +* @type {number} +*/ +Gamepad.timestamp; + +/** + * @return {Array.<Gamepad>} + */ +navigator.getGamepads = function() {}; + +/** + * @return {Array.<Gamepad>} + */ +navigator.webkitGetGamepads = function() {}; + +/** + * @return {Array.<Gamepad>} + */ +navigator.webkitGamepads = function() {}; + +/** + * @return {Array.<Gamepad>} + */ +navigator.mozGamepads = function() {}; + +/** + * @return {Array.<Gamepad>} + */ +navigator.gamepads = function() {}; diff --git a/src/compiler.js b/src/compiler.js index e4ce1c88..17a8e83c 100644 --- a/src/compiler.js +++ b/src/compiler.js @@ -183,9 +183,6 @@ if (SAFE_HEAP) USE_BSS = 0; // must initialize heap for safe heap // Settings sanity checks assert(!(USE_TYPED_ARRAYS === 2 && QUANTUM_SIZE !== 4), 'For USE_TYPED_ARRAYS == 2, must have normal QUANTUM_SIZE of 4'); -if (ASM_JS) { - assert(!ALLOW_MEMORY_GROWTH, 'Cannot grow asm.js heap'); -} assert(!(!NAMED_GLOBALS && BUILD_AS_SHARED_LIB), 'shared libraries must have named globals'); // Output some info and warnings based on settings diff --git a/src/deps_info.json b/src/deps_info.json new file mode 100644 index 00000000..b38ffd00 --- /dev/null +++ b/src/deps_info.json @@ -0,0 +1,7 @@ +{ + "uuid_compare": ["memcmp"], + "SDL_Init": ["malloc", "free"], + "SDL_GL_GetProcAddress": ["emscripten_GetProcAddress"], + "eglGetProcAddress": ["emscripten_GetProcAddress"] +} + diff --git a/src/embind/emval.js b/src/embind/emval.js index 0d075188..039f1d61 100644 --- a/src/embind/emval.js +++ b/src/embind/emval.js @@ -3,7 +3,7 @@ /*global new_*/ /*global createNamedFunction*/ /*global readLatin1String, writeStringToMemory*/ -/*global requireRegisteredType, throwBindingError*/ +/*global requireRegisteredType, throwBindingError, runDestructors*/ /*jslint sub:true*/ /* The symbols 'fromWireType' and 'toWireType' must be accessed via array notation to be closure-safe since craftInvokerFunction crafts functions as strings that can't be closured. */ var Module = Module || {}; @@ -79,6 +79,12 @@ function __emval_decref(handle) { } } +function __emval_run_destructors(handle) { + var destructors = _emval_handle_array[handle].value; + runDestructors(destructors); + __emval_decref(handle); +} + function __emval_new_array() { return __emval_register([]); } @@ -199,11 +205,12 @@ function __emval_set_property(handle, key, value) { _emval_handle_array[handle].value[_emval_handle_array[key].value] = _emval_handle_array[value].value; } -function __emval_as(handle, returnType) { +function __emval_as(handle, returnType, destructorsRef) { requireHandle(handle); returnType = requireRegisteredType(returnType, 'emval::as'); var destructors = []; - // caller owns destructing + var rd = __emval_register(destructors); + HEAP32[destructorsRef >> 2] = rd; return returnType['toWireType'](destructors, _emval_handle_array[handle].value); } @@ -242,14 +249,20 @@ function lookupTypes(argCount, argTypes, argWireTypes) { return a; } +function allocateDestructors(destructorsRef) { + var destructors = []; + HEAP32[destructorsRef >> 2] = __emval_register(destructors); + return destructors; +} + function __emval_get_method_caller(argCount, argTypes) { var types = lookupTypes(argCount, argTypes); var retType = types[0]; var signatureName = retType.name + "_$" + types.slice(1).map(function (t) { return t.name; }).join("_") + "$"; - var args1 = ["addFunction", "createNamedFunction", "requireHandle", "getStringOrSymbol", "_emval_handle_array", "retType"]; - var args2 = [Runtime.addFunction, createNamedFunction, requireHandle, getStringOrSymbol, _emval_handle_array, retType]; + var args1 = ["addFunction", "createNamedFunction", "requireHandle", "getStringOrSymbol", "_emval_handle_array", "retType", "allocateDestructors"]; + var args2 = [Runtime.addFunction, createNamedFunction, requireHandle, getStringOrSymbol, _emval_handle_array, retType, allocateDestructors]; var argsList = ""; // 'arg0, arg1, arg2, ... , argN' var argsListWired = ""; // 'arg0Wired, ..., argNWired' @@ -261,16 +274,17 @@ function __emval_get_method_caller(argCount, argTypes) { } var invokerFnBody = - "return addFunction(createNamedFunction('" + signatureName + "', function (handle, name" + argsListWired + ") {\n" + - "requireHandle(handle);\n" + - "name = getStringOrSymbol(name);\n"; + "return addFunction(createNamedFunction('" + signatureName + "', function (handle, name, destructorsRef" + argsListWired + ") {\n" + + " requireHandle(handle);\n" + + " name = getStringOrSymbol(name);\n"; for (var i = 0; i < argCount - 1; ++i) { - invokerFnBody += "var arg" + i + " = argType" + i + ".fromWireType(arg" + i + "Wired);\n"; + invokerFnBody += " var arg" + i + " = argType" + i + ".fromWireType(arg" + i + "Wired);\n"; } invokerFnBody += - "var obj = _emval_handle_array[handle].value;\n" + - "return retType.toWireType(null, obj[name](" + argsList + "));\n" + + " var obj = _emval_handle_array[handle].value;\n" + + " var rv = obj[name](" + argsList + ");\n" + + " return retType.toWireType(allocateDestructors(destructorsRef), rv);\n" + "}));\n"; args1.push(invokerFnBody); diff --git a/src/headless.js b/src/headless.js index e5458641..5880c087 100644 --- a/src/headless.js +++ b/src/headless.js @@ -82,6 +82,16 @@ var window = { } listeners.push(func); }, + removeEventListener: function(id, func) { + var listeners = this.eventListeners[id]; + if (!listeners) return; + for (var i = 0; i < listeners.length; i++) { + if (listeners[i] === func) { + listeners.splice(i, 1); + return; + } + } + }, callEventListeners: function(id) { var listeners = this.eventListeners[id]; if (listeners) { @@ -101,6 +111,7 @@ var document = { headless: true, eventListeners: {}, addEventListener: window.addEventListener, + removeEventListener: window.removeEventListener, callEventListeners: window.callEventListeners, getElementById: function(id) { switch(id) { @@ -144,6 +155,7 @@ var document = { }, eventListeners: {}, addEventListener: document.addEventListener, + removeEventListener: document.removeEventListener, callEventListeners: document.callEventListeners, }; }; diff --git a/src/headlessCanvas.js b/src/headlessCanvas.js index 4951aed8..1eefe48e 100644 --- a/src/headlessCanvas.js +++ b/src/headlessCanvas.js @@ -446,6 +446,7 @@ function headlessCanvas() { case /* GL_MAX_FRAGMENT_UNIFORM_VECTORS */ 0x8DFD: return 4096; case /* GL_MAX_VARYING_VECTORS */ 0x8DFC: return 32; case /* GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS */ 0x8B4D: return 32; + case /* GL_ARRAY_BUFFER_BINDING */ 0x8894: return 0; default: console.log('getParameter ' + pname + '?'); return 0; } }, @@ -597,9 +598,12 @@ function headlessCanvas() { }); }, exitPointerLock: function(){}, - style: {}, + style: { + setProperty: function(){} + }, eventListeners: {}, addEventListener: function(){}, + removeEventListener: function(){}, requestFullScreen: function() { document.fullscreenElement = document.getElementById('canvas'); window.setTimeout(function() { diff --git a/src/intertyper.js b/src/intertyper.js index 10822e48..323787ac 100644 --- a/src/intertyper.js +++ b/src/intertyper.js @@ -348,9 +348,11 @@ function intertyper(lines, sidePass, baseLineNums) { if (token1Text == 'triple') { var triple = item.tokens[3].text; triple = triple.substr(1, triple.length-2); - var expected = TARGET_LE32 ? 'le32-unknown-nacl' : 'i386-pc-linux-gnu'; + var expected = TARGET_ASMJS_UNKNOWN_EMSCRIPTEN ? 'asmjs-unknown-emscripten' : 'i386-pc-linux-gnu'; if (triple !== expected) { - warn('using an unexpected LLVM triple: ' + [triple, ' !== ', expected] + ' (are you using emcc for everything and not clang?)'); + if (!(TARGET_ASMJS_UNKNOWN_EMSCRIPTEN && triple === 'le32-unknown-nacl')) { + warn('using an unexpected LLVM triple: ' + [triple, ' !== ', expected] + ' (are you using emcc for everything and not clang?)'); + } } } return null; @@ -688,7 +690,7 @@ function intertyper(lines, sidePass, baseLineNums) { Types.hasInlineJS = true; warnOnce('inline JavaScript using asm() will cause the code to no longer fall in the asm.js subset of JavaScript, which can reduce performance - consider using emscripten_run_script'); } - assert(TARGET_LE32, 'inline js is only supported in le32'); + assert(TARGET_ASMJS_UNKNOWN_EMSCRIPTEN, 'inline js is only supported in asmjs-unknown-emscripten'); // Inline assembly is just JavaScript that we paste into the code item.intertype = 'value'; if (tokensLeft[0].text == 'sideeffect') tokensLeft.splice(0, 1); @@ -848,7 +850,7 @@ function intertyper(lines, sidePass, baseLineNums) { }).filter(function(param) { return param.value && param.value.ident != 'undef' }); return item; } - // 'phi' + // 'va_arg' function va_argHandler(item) { item.intertype = 'va_arg'; var segments = splitTokenList(item.tokens.slice(1)); diff --git a/src/jsifier.js b/src/jsifier.js index 726a5eda..503f0b71 100644 --- a/src/jsifier.js +++ b/src/jsifier.js @@ -224,6 +224,7 @@ function JSify(data, functionsOnly) { // globalVariable function globalVariableHandler(item) { + function needsPostSet(value) { if (typeof value !== 'string') return false; // (' is ok, as it is something we can indexize later into a concrete int: ('{{ FI_ ... @@ -274,7 +275,9 @@ function JSify(data, functionsOnly) { constant = Runtime.alignMemory(calcAllocatedSize(item.type)); } else { if (item.external) { - if (Runtime.isNumberType(item.type) || isPointerType(item.type)) { + if (LibraryManager.library[item.ident.slice(1)]) { + constant = LibraryManager.library[item.ident.slice(1)]; + } else if (Runtime.isNumberType(item.type) || isPointerType(item.type)) { constant = zeros(Runtime.getNativeFieldSize(item.type)); } else { constant = makeEmptyStruct(item.type); @@ -282,22 +285,23 @@ function JSify(data, functionsOnly) { } else { constant = parseConst(item.value, item.type, item.ident); } - assert(typeof constant === 'object');//, [typeof constant, JSON.stringify(constant), item.external]); // This is a flattened object. We need to find its idents, so they can be assigned to later - var structTypes = null; - constant.forEach(function(value, i) { - if (needsPostSet(value)) { // ident, or expression containing an ident - if (!structTypes) structTypes = generateStructTypes(item.type); - itemsDict.GlobalVariablePostSet.push({ - intertype: 'GlobalVariablePostSet', - JS: makeSetValue(makeGlobalUse(item.ident), i, value, structTypes[i], false, true) + ';' // ignore=true, since e.g. rtti and statics cause lots of safe_heap errors - }); - constant[i] = '0'; - } else { - if (typeof value === 'string') constant[i] = deParenCarefully(value); - } - }); + if (typeof constant === 'object') { + var structTypes = null; + constant.forEach(function(value, i) { + if (needsPostSet(value)) { // ident, or expression containing an ident + if (!structTypes) structTypes = generateStructTypes(item.type); + itemsDict.GlobalVariablePostSet.push({ + intertype: 'GlobalVariablePostSet', + JS: makeSetValue(makeGlobalUse(item.ident), i, value, structTypes[i], false, true) + ';' // ignore=true, since e.g. rtti and statics cause lots of safe_heap errors + }); + constant[i] = '0'; + } else { + if (typeof value === 'string') constant[i] = deParenCarefully(value); + } + }); + } if (item.external) { // External variables in shared libraries should not be declared as @@ -312,14 +316,18 @@ function JSify(data, functionsOnly) { } // ensure alignment - var extra = Runtime.alignMemory(constant.length) - constant.length; - if (item.ident.substr(0, 5) == '__ZTV') extra += Runtime.alignMemory(QUANTUM_SIZE); - while (extra-- > 0) constant.push(0); + if (typeof constant === 'object') { + var extra = Runtime.alignMemory(constant.length) - constant.length; + if (item.ident.substr(0, 5) == '__ZTV') extra += Runtime.alignMemory(QUANTUM_SIZE); + while (extra-- > 0) constant.push(0); + } } // NOTE: This is the only place that could potentially create static // allocations in a shared library. - constant = makePointer(constant, null, allocator, item.type, index); + if (typeof constant !== 'string') { + constant = makePointer(constant, null, allocator, item.type, index); + } var js = (index !== null ? '' : item.ident + '=') + constant; if (js) js += ';'; @@ -376,7 +384,7 @@ function JSify(data, functionsOnly) { functionStubSigs[item.ident] = Functions.getSignature(item.returnType.text, item.params.map(function(arg) { return arg.type }), false); } - function addFromLibrary(ident) { + function addFromLibrary(ident, notDep) { if (ident in addedLibraryItems) return ''; addedLibraryItems[ident] = true; @@ -387,6 +395,15 @@ function JSify(data, functionsOnly) { // Note: We don't return the dependencies here. Be careful not to end up where this matters if (('_' + ident) in Functions.implementedFunctions) return ''; + if (!LibraryManager.library.hasOwnProperty(ident) && !LibraryManager.library.hasOwnProperty(ident + '__inline')) { + if (notDep) { + if (ERROR_ON_UNDEFINED_SYMBOLS) error('unresolved symbol: ' + ident); + else if (VERBOSE || (WARN_ON_UNDEFINED_SYMBOLS && !LINKABLE)) warn('unresolved symbol: ' + ident); + } + // emit a stub that will fail at runtime + LibraryManager.library[shortident] = new Function("Module['printErr']('missing function: " + shortident + "'); abort(-1);"); + } + var snippet = LibraryManager.library[ident]; var redirectedIdent = null; var deps = LibraryManager.library[ident + '__deps'] || []; @@ -448,7 +465,7 @@ function JSify(data, functionsOnly) { } else { ident = '_' + ident; } - if (VERBOSE) printErr('adding ' + ident + ' and deps ' + deps); + if (VERBOSE) printErr('adding ' + ident + ' and deps ' + deps + ' : ' + (snippet + '').substr(0, 40)); var depsText = (deps ? '\n' + deps.map(addFromLibrary).filter(function(x) { return x != '' }).join('\n') : ''); var contentText = isFunction ? snippet : ('var ' + ident + '=' + snippet + ';'); if (ASM_JS) { @@ -478,7 +495,6 @@ function JSify(data, functionsOnly) { item.JS = ''; } else { // If this is not linkable, anything not in the library is definitely missing - var cancel = false; if (item.ident in DEAD_FUNCTIONS) { if (LibraryManager.library[shortident + '__asm']) { warn('cannot kill asm library function ' + item.ident); @@ -488,17 +504,7 @@ function JSify(data, functionsOnly) { delete LibraryManager.library[shortident + '__deps']; } } - if (!LINKABLE && !LibraryManager.library.hasOwnProperty(shortident) && !LibraryManager.library.hasOwnProperty(shortident + '__inline')) { - if (ERROR_ON_UNDEFINED_SYMBOLS) error('unresolved symbol: ' + shortident); - else if (VERBOSE || WARN_ON_UNDEFINED_SYMBOLS) warn('unresolved symbol: ' + shortident); - if (ASM_JS) { - // emit a stub that will fail during runtime. this allows asm validation to succeed. - LibraryManager.library[shortident] = new Function("Module['printErr']('missing function: " + shortident + "'); abort(-1);"); - } else { - cancel = true; // emit nothing, not even var X = undefined; - } - } - item.JS = cancel ? ';' : addFromLibrary(shortident); + item.JS = addFromLibrary(shortident, true); } } @@ -1223,7 +1229,7 @@ function JSify(data, functionsOnly) { // in an assignment var disabled = DISABLE_EXCEPTION_CATCHING == 2 && !(item.funcData.ident in EXCEPTION_CATCHING_WHITELIST); var phiSets = calcPhiSets(item); - var call_ = makeFunctionCall(item, item.params, item.funcData, item.type, ASM_JS && !disabled, !!item.assignTo || !item.standalone, true); + var call_ = makeFunctionCall(item, item.params, item.funcData, item.type, ASM_JS && !disabled, !!item.assignTo || !item.standalone, !disabled); var ret; @@ -1409,13 +1415,13 @@ function JSify(data, functionsOnly) { } } function va_argHandler(item) { - assert(TARGET_LE32); + assert(TARGET_ASMJS_UNKNOWN_EMSCRIPTEN); var ident = item.value.ident; var move = Runtime.STACK_ALIGN; // store current list offset in tempInt, advance list offset by STACK_ALIGN, return list entry stored at tempInt return '(tempInt=' + makeGetValue(ident, Runtime.QUANTUM_SIZE, '*') + ',' + - makeSetValue(ident, Runtime.QUANTUM_SIZE, 'tempInt + ' + move, '*', null, null, null, null, ',') + ',' + + makeSetValue(ident, Runtime.QUANTUM_SIZE, asmCoercion('tempInt + ' + move, 'i32'), '*', null, null, null, null, ',') + ',' + makeGetValue(makeGetValue(ident, 0, '*'), 'tempInt', item.type) + ')'; } @@ -1706,7 +1712,7 @@ function JSify(data, functionsOnly) { if ((phase == 'pre' || phase == 'glue') && !Variables.generatedGlobalBase && !BUILD_AS_SHARED_LIB) { Variables.generatedGlobalBase = true; // Globals are done, here is the rest of static memory - assert((TARGET_LE32 && Runtime.GLOBAL_BASE == 8) || (TARGET_X86 && Runtime.GLOBAL_BASE == 4)); // this is assumed in e.g. relocations for linkable modules + assert((TARGET_ASMJS_UNKNOWN_EMSCRIPTEN && Runtime.GLOBAL_BASE == 8) || (TARGET_X86 && Runtime.GLOBAL_BASE == 4)); // this is assumed in e.g. relocations for linkable modules if (!SIDE_MODULE) { print('STATIC_BASE = ' + Runtime.GLOBAL_BASE + ';\n'); print('STATICTOP = STATIC_BASE + ' + Runtime.alignMemory(Variables.nextIndexedOffset) + ';\n'); @@ -1844,7 +1850,7 @@ function JSify(data, functionsOnly) { // rest of the output that we started to print out earlier (see comment on the // "Final shape that will be created"). if (PRECISE_I64_MATH && Types.preciseI64MathUsed) { - if (!INCLUDE_FULL_LIBRARY) { + if (!INCLUDE_FULL_LIBRARY && !SIDE_MODULE && !BUILD_AS_SHARED_LIB) { // first row are utilities called from generated code, second are needed from fastLong ['i64Add', 'i64Subtract', 'bitshift64Shl', 'bitshift64Lshr', 'bitshift64Ashr', 'llvm_ctlz_i32', 'llvm_cttz_i32'].forEach(function(func) { @@ -1866,6 +1872,7 @@ function JSify(data, functionsOnly) { } }); } + // these may be duplicated in side modules and the main module without issue print(read('fastLong.js')); print('// EMSCRIPTEN_END_FUNCS\n'); print(read('long.js')); diff --git a/src/library.js b/src/library.js index e71cccf0..8aac2bb2 100644 --- a/src/library.js +++ b/src/library.js @@ -52,32 +52,33 @@ LibraryManager.library = { ___setErrNo(ERRNO_CODES.ENOTDIR); return 0; } - var err = _open(dirname, {{{ cDefine('O_RDONLY') }}}, allocate([0, 0, 0, 0], 'i32', ALLOC_STACK)); - // open returns 0 on failure, not -1 - return err === -1 ? 0 : err; + var fd = _open(dirname, {{{ cDefine('O_RDONLY') }}}, allocate([0, 0, 0, 0], 'i32', ALLOC_STACK)); + return fd === -1 ? 0 : FS.getPtrForStream(FS.getStream(fd)); }, - closedir__deps: ['$FS', '__setErrNo', '$ERRNO_CODES', 'close'], + closedir__deps: ['$FS', '__setErrNo', '$ERRNO_CODES', 'close', 'fileno'], closedir: function(dirp) { // int closedir(DIR *dirp); // http://pubs.opengroup.org/onlinepubs/007908799/xsh/closedir.html - return _close(dirp); + var fd = _fileno(dirp); + return _close(fd); }, telldir__deps: ['$FS', '__setErrNo', '$ERRNO_CODES'], telldir: function(dirp) { // long int telldir(DIR *dirp); // http://pubs.opengroup.org/onlinepubs/007908799/xsh/telldir.html - var stream = FS.getStream(dirp); + var stream = FS.getStreamFromPtr(dirp); if (!stream || !FS.isDir(stream.node.mode)) { ___setErrNo(ERRNO_CODES.EBADF); return -1; } return stream.position; }, - seekdir__deps: ['$FS', '__setErrNo', '$ERRNO_CODES', 'lseek'], + seekdir__deps: ['$FS', '__setErrNo', '$ERRNO_CODES', 'lseek', 'fileno'], seekdir: function(dirp, loc) { // void seekdir(DIR *dirp, long int loc); // http://pubs.opengroup.org/onlinepubs/007908799/xsh/seekdir.html - _lseek(dirp, loc, {{{ cDefine('SEEK_SET') }}}); + var fd = _fileno(dirp); + _lseek(fd, loc, {{{ cDefine('SEEK_SET') }}}); }, rewinddir__deps: ['seekdir'], rewinddir: function(dirp) { @@ -89,7 +90,7 @@ LibraryManager.library = { readdir_r: function(dirp, entry, result) { // int readdir_r(DIR *dirp, struct dirent *entry, struct dirent **result); // http://pubs.opengroup.org/onlinepubs/007908799/xsh/readdir_r.html - var stream = FS.getStream(dirp); + var stream = FS.getStreamFromPtr(dirp); if (!stream) { return ___setErrNo(ERRNO_CODES.EBADF); } @@ -100,7 +101,7 @@ LibraryManager.library = { return FS.handleFSError(e); } if (stream.position < 0 || stream.position >= entries.length) { - {{{ makeSetValue('result', '0', '0', 'i8*') }}} + {{{ makeSetValue('result', '0', '0', 'i8*') }}}; return 0; } var id; @@ -118,23 +119,23 @@ LibraryManager.library = { FS.isLink(child.mode) ? 10 : // DT_LNK, symbolic link. 8; // DT_REG, regular file. } - {{{ makeSetValue('entry', C_STRUCTS.dirent.d_ino, 'id', 'i32') }}} - {{{ makeSetValue('entry', C_STRUCTS.dirent.d_off, 'offset', 'i32') }}} - {{{ makeSetValue('entry', C_STRUCTS.dirent.d_reclen, 'name.length + 1', 'i32') }}} + {{{ makeSetValue('entry', C_STRUCTS.dirent.d_ino, 'id', 'i32') }}}; + {{{ makeSetValue('entry', C_STRUCTS.dirent.d_off, 'offset', 'i32') }}}; + {{{ makeSetValue('entry', C_STRUCTS.dirent.d_reclen, 'name.length + 1', 'i32') }}}; for (var i = 0; i < name.length; i++) { - {{{ makeSetValue('entry + ' + C_STRUCTS.dirent.d_name, 'i', 'name.charCodeAt(i)', 'i8') }}} + {{{ makeSetValue('entry + ' + C_STRUCTS.dirent.d_name, 'i', 'name.charCodeAt(i)', 'i8') }}}; } - {{{ makeSetValue('entry + ' + C_STRUCTS.dirent.d_name, 'i', '0', 'i8') }}} - {{{ makeSetValue('entry', C_STRUCTS.dirent.d_type, 'type', 'i8') }}} - {{{ makeSetValue('result', '0', 'entry', 'i8*') }}} + {{{ makeSetValue('entry + ' + C_STRUCTS.dirent.d_name, 'i', '0', 'i8') }}}; + {{{ makeSetValue('entry', C_STRUCTS.dirent.d_type, 'type', 'i8') }}}; + {{{ makeSetValue('result', '0', 'entry', 'i8*') }}}; stream.position++; return 0; }, - readdir__deps: ['readdir_r', '__setErrNo', '$ERRNO_CODES'], + readdir__deps: ['readdir_r', '__setErrNo', '$ERRNO_CODES', 'malloc'], readdir: function(dirp) { // struct dirent *readdir(DIR *dirp); // http://pubs.opengroup.org/onlinepubs/007908799/xsh/readdir_r.html - var stream = FS.getStream(dirp); + var stream = FS.getStreamFromPtr(dirp); if (!stream) { ___setErrNo(ERRNO_CODES.EBADF); return 0; @@ -149,8 +150,6 @@ LibraryManager.library = { } return {{{ makeGetValue(0, '_readdir.result', 'i8*') }}}; }, - __01readdir64_: 'readdir', - // TODO: Check if we need to link any other aliases. // ========================================================================== // utime.h @@ -164,7 +163,7 @@ LibraryManager.library = { if (times) { // NOTE: We don't keep track of access timestamps. var offset = {{{ C_STRUCTS.utimbuf.modtime }}}; - time = {{{ makeGetValue('times', 'offset', 'i32') }}} + time = {{{ makeGetValue('times', 'offset', 'i32') }}}; time *= 1000; } else { time = Date.now(); @@ -207,13 +206,13 @@ LibraryManager.library = { var length = i; if (allSlashes) { // All slashes result in a single slash. - {{{ makeSetValue('path', '1', '0', 'i8') }}} + {{{ makeSetValue('path', '1', '0', 'i8') }}}; return [path, -1]; } else { // Strip trailing slashes. while (slashPositions.length && slashPositions[slashPositions.length - 1] == length - 1) { - {{{ makeSetValue('path', 'slashPositions.pop(i)', '0', 'i8') }}} + {{{ makeSetValue('path', 'slashPositions.pop(i)', '0', 'i8') }}}; length--; } return [path, slashPositions.pop()]; @@ -227,16 +226,15 @@ LibraryManager.library = { var result = ___libgenSplitName(path); return result[0] + result[1] + 1; }, - __xpg_basename: 'basename', dirname__deps: ['__libgenSplitName'], dirname: function(path) { // char *dirname(char *path); // http://pubs.opengroup.org/onlinepubs/007908799/xsh/dirname.html var result = ___libgenSplitName(path); if (result[1] == 0) { - {{{ makeSetValue('result[0]', 1, '0', 'i8') }}} + {{{ makeSetValue('result[0]', 1, '0', 'i8') }}}; } else if (result[1] !== -1) { - {{{ makeSetValue('result[0]', 'result[1]', '0', 'i8') }}} + {{{ makeSetValue('result[0]', 'result[1]', '0', 'i8') }}}; } return result[0]; }, @@ -257,22 +255,22 @@ LibraryManager.library = { {{{ makeSetValue('buf', C_STRUCTS.stat.st_dev, 'stat.dev', 'i32') }}}; {{{ makeSetValue('buf', C_STRUCTS.stat.__st_dev_padding, '0', 'i32') }}}; {{{ makeSetValue('buf', C_STRUCTS.stat.__st_ino_truncated, 'stat.ino', 'i32') }}}; - {{{ makeSetValue('buf', C_STRUCTS.stat.st_mode, 'stat.mode', 'i32') }}} - {{{ makeSetValue('buf', C_STRUCTS.stat.st_nlink, 'stat.nlink', 'i32') }}} - {{{ makeSetValue('buf', C_STRUCTS.stat.st_uid, 'stat.uid', 'i32') }}} - {{{ makeSetValue('buf', C_STRUCTS.stat.st_gid, 'stat.gid', 'i32') }}} - {{{ makeSetValue('buf', C_STRUCTS.stat.st_rdev, 'stat.rdev', 'i32') }}} + {{{ makeSetValue('buf', C_STRUCTS.stat.st_mode, 'stat.mode', 'i32') }}}; + {{{ makeSetValue('buf', C_STRUCTS.stat.st_nlink, 'stat.nlink', 'i32') }}}; + {{{ makeSetValue('buf', C_STRUCTS.stat.st_uid, 'stat.uid', 'i32') }}}; + {{{ makeSetValue('buf', C_STRUCTS.stat.st_gid, 'stat.gid', 'i32') }}}; + {{{ makeSetValue('buf', C_STRUCTS.stat.st_rdev, 'stat.rdev', 'i32') }}}; {{{ makeSetValue('buf', C_STRUCTS.stat.__st_rdev_padding, '0', 'i32') }}}; - {{{ makeSetValue('buf', C_STRUCTS.stat.st_size, 'stat.size', 'i32') }}} - {{{ makeSetValue('buf', C_STRUCTS.stat.st_blksize, '4096', 'i32') }}} - {{{ makeSetValue('buf', C_STRUCTS.stat.st_blocks, 'stat.blocks', 'i32') }}} - {{{ makeSetValue('buf', C_STRUCTS.stat.st_atim.tv_sec, 'Math.floor(stat.atime.getTime() / 1000)', 'i32') }}} - {{{ makeSetValue('buf', C_STRUCTS.stat.st_atim.tv_nsec, '0', 'i32') }}} - {{{ makeSetValue('buf', C_STRUCTS.stat.st_mtim.tv_sec, 'Math.floor(stat.mtime.getTime() / 1000)', 'i32') }}} - {{{ makeSetValue('buf', C_STRUCTS.stat.st_mtim.tv_nsec, '0', 'i32') }}} - {{{ makeSetValue('buf', C_STRUCTS.stat.st_ctim.tv_sec, 'Math.floor(stat.ctime.getTime() / 1000)', 'i32') }}} - {{{ makeSetValue('buf', C_STRUCTS.stat.st_ctim.tv_nsec, '0', 'i32') }}} - {{{ makeSetValue('buf', C_STRUCTS.stat.st_ino, 'stat.ino', 'i32') }}} + {{{ makeSetValue('buf', C_STRUCTS.stat.st_size, 'stat.size', 'i32') }}}; + {{{ makeSetValue('buf', C_STRUCTS.stat.st_blksize, '4096', 'i32') }}}; + {{{ makeSetValue('buf', C_STRUCTS.stat.st_blocks, 'stat.blocks', 'i32') }}}; + {{{ makeSetValue('buf', C_STRUCTS.stat.st_atim.tv_sec, 'Math.floor(stat.atime.getTime() / 1000)', 'i32') }}}; + {{{ makeSetValue('buf', C_STRUCTS.stat.st_atim.tv_nsec, '0', 'i32') }}}; + {{{ makeSetValue('buf', C_STRUCTS.stat.st_mtim.tv_sec, 'Math.floor(stat.mtime.getTime() / 1000)', 'i32') }}}; + {{{ makeSetValue('buf', C_STRUCTS.stat.st_mtim.tv_nsec, '0', 'i32') }}}; + {{{ makeSetValue('buf', C_STRUCTS.stat.st_ctim.tv_sec, 'Math.floor(stat.ctime.getTime() / 1000)', 'i32') }}}; + {{{ makeSetValue('buf', C_STRUCTS.stat.st_ctim.tv_nsec, '0', 'i32') }}}; + {{{ makeSetValue('buf', C_STRUCTS.stat.st_ino, 'stat.ino', 'i32') }}}; return 0; } catch (e) { FS.handleFSError(e); @@ -395,14 +393,6 @@ LibraryManager.library = { _umask.cmask = newMask; return oldMask; }, - stat64: 'stat', - fstat64: 'fstat', - lstat64: 'lstat', - __01fstat64_: 'fstat', - __01stat64_: 'stat', - __01lstat64_: 'lstat', - - // TODO: Check if other aliases are needed. // ========================================================================== // sys/statvfs.h @@ -414,17 +404,17 @@ LibraryManager.library = { // int statvfs(const char *restrict path, struct statvfs *restrict buf); // NOTE: None of the constants here are true. We're just returning safe and // sane values. - {{{ makeSetValue('buf', C_STRUCTS.statvfs.f_bsize, '4096', 'i32') }}} - {{{ makeSetValue('buf', C_STRUCTS.statvfs.f_frsize, '4096', 'i32') }}} - {{{ makeSetValue('buf', C_STRUCTS.statvfs.f_blocks, '1000000', 'i32') }}} - {{{ makeSetValue('buf', C_STRUCTS.statvfs.f_bfree, '500000', 'i32') }}} - {{{ makeSetValue('buf', C_STRUCTS.statvfs.f_bavail, '500000', 'i32') }}} - {{{ makeSetValue('buf', C_STRUCTS.statvfs.f_files, 'FS.nextInode', 'i32') }}} - {{{ makeSetValue('buf', C_STRUCTS.statvfs.f_ffree, '1000000', 'i32') }}} - {{{ makeSetValue('buf', C_STRUCTS.statvfs.f_favail, '1000000', 'i32') }}} - {{{ makeSetValue('buf', C_STRUCTS.statvfs.f_fsid, '42', 'i32') }}} - {{{ makeSetValue('buf', C_STRUCTS.statvfs.f_flag, '2', 'i32') }}} // ST_NOSUID - {{{ makeSetValue('buf', C_STRUCTS.statvfs.f_namemax, '255', 'i32') }}} + {{{ makeSetValue('buf', C_STRUCTS.statvfs.f_bsize, '4096', 'i32') }}}; + {{{ makeSetValue('buf', C_STRUCTS.statvfs.f_frsize, '4096', 'i32') }}}; + {{{ makeSetValue('buf', C_STRUCTS.statvfs.f_blocks, '1000000', 'i32') }}}; + {{{ makeSetValue('buf', C_STRUCTS.statvfs.f_bfree, '500000', 'i32') }}}; + {{{ makeSetValue('buf', C_STRUCTS.statvfs.f_bavail, '500000', 'i32') }}}; + {{{ makeSetValue('buf', C_STRUCTS.statvfs.f_files, 'FS.nextInode', 'i32') }}}; + {{{ makeSetValue('buf', C_STRUCTS.statvfs.f_ffree, '1000000', 'i32') }}}; + {{{ makeSetValue('buf', C_STRUCTS.statvfs.f_favail, '1000000', 'i32') }}}; + {{{ makeSetValue('buf', C_STRUCTS.statvfs.f_fsid, '42', 'i32') }}}; + {{{ makeSetValue('buf', C_STRUCTS.statvfs.f_flag, '2', 'i32') }}}; // ST_NOSUID + {{{ makeSetValue('buf', C_STRUCTS.statvfs.f_namemax, '255', 'i32') }}}; return 0; }, fstatvfs__deps: ['statvfs'], @@ -433,8 +423,6 @@ LibraryManager.library = { // http://pubs.opengroup.org/onlinepubs/009604499/functions/statvfs.html return _statvfs(0, buf); }, - __01statvfs64_: 'statvfs', - __01fstatvfs64_: 'fstatvfs', // ========================================================================== // fcntl.h @@ -515,7 +503,7 @@ LibraryManager.library = { var arg = {{{ makeGetValue('varargs', 0, 'i32') }}}; var offset = {{{ C_STRUCTS.flock.l_type }}}; // We're always unlocked. - {{{ makeSetValue('arg', 'offset', cDefine('F_UNLCK'), 'i16') }}} + {{{ makeSetValue('arg', 'offset', cDefine('F_UNLCK'), 'i16') }}}; return 0; case {{{ cDefine('F_SETLK') }}}: case {{{ cDefine('F_SETLKW') }}}: @@ -571,6 +559,25 @@ LibraryManager.library = { }, // ========================================================================== + // nl_types.h + // ========================================================================== + + catopen: function(name, oflag) { + // nl_catd catopen (const char *name, int oflag) + return -1; + }, + + catgets: function(catd, set_id, msg_id, s) { + // char *catgets (nl_catd catd, int set_id, int msg_id, const char *s) + return s; + }, + + catclose: function(catd) { + // int catclose (nl_catd catd) + return 0; + }, + + // ========================================================================== // poll.h // ========================================================================== @@ -594,7 +601,7 @@ LibraryManager.library = { } mask &= events | {{{ cDefine('POLLERR') }}} | {{{ cDefine('POLLHUP') }}}; if (mask) nonzero++; - {{{ makeSetValue('pollfd', C_STRUCTS.pollfd.revents, 'mask', 'i16') }}} + {{{ makeSetValue('pollfd', C_STRUCTS.pollfd.revents, 'mask', 'i16') }}}; } return nonzero; }, @@ -1014,7 +1021,7 @@ LibraryManager.library = { return -1; } }, - ttyname__deps: ['ttyname_r'], + ttyname__deps: ['ttyname_r', 'malloc'], ttyname: function(fildes) { // char *ttyname(int fildes); // http://pubs.opengroup.org/onlinepubs/000095399/functions/ttyname.html @@ -1174,9 +1181,9 @@ LibraryManager.library = { } else { var length = Math.min(len, value.length); for (var i = 0; i < length; i++) { - {{{ makeSetValue('buf', 'i', 'value.charCodeAt(i)', 'i8') }}} + {{{ makeSetValue('buf', 'i', 'value.charCodeAt(i)', 'i8') }}}; } - if (len > length) {{{ makeSetValue('buf', 'i++', '0', 'i8') }}} + if (len > length) {{{ makeSetValue('buf', 'i++', '0', 'i8') }}}; return i; } }, @@ -1223,9 +1230,9 @@ LibraryManager.library = { // int getresuid(uid_t *ruid, uid_t *euid, uid_t *suid); // http://linux.die.net/man/2/getresuid // We have just one process/group/user, all with ID 0. - {{{ makeSetValue('ruid', '0', '0', 'i32') }}} - {{{ makeSetValue('euid', '0', '0', 'i32') }}} - {{{ makeSetValue('suid', '0', '0', 'i32') }}} + {{{ makeSetValue('ruid', '0', '0', 'i32') }}}; + {{{ makeSetValue('euid', '0', '0', 'i32') }}}; + {{{ makeSetValue('suid', '0', '0', 'i32') }}}; return 0; }, getresgid: 'getresuid', @@ -1237,7 +1244,7 @@ LibraryManager.library = { ___setErrNo(ERRNO_CODES.EINVAL); return -1; } else { - {{{ makeSetValue('grouplist', '0', '0', 'i32') }}} + {{{ makeSetValue('grouplist', '0', '0', 'i32') }}}; return 1; } }, @@ -1270,17 +1277,17 @@ LibraryManager.library = { } var length = Math.min(namelen, host.length); for (var i = 0; i < length; i++) { - {{{ makeSetValue('name', 'i', 'host.charCodeAt(i)', 'i8') }}} + {{{ makeSetValue('name', 'i', 'host.charCodeAt(i)', 'i8') }}}; } if (namelen > length) { - {{{ makeSetValue('name', 'i', '0', 'i8') }}} + {{{ makeSetValue('name', 'i', '0', 'i8') }}}; return 0; } else { ___setErrNo(ERRNO_CODES.ENAMETOOLONG); return -1; } }, - getlogin__deps: ['getlogin_r'], + getlogin__deps: ['getlogin_r', 'malloc'], getlogin: function() { // char *getlogin(void); // http://pubs.opengroup.org/onlinepubs/000095399/functions/getlogin.html @@ -1390,8 +1397,8 @@ LibraryManager.library = { for (var i = 0; i < nbytes; i += 2) { var first = {{{ makeGetValue('src', 'i', 'i8') }}}; var second = {{{ makeGetValue('src', 'i + 1', 'i8') }}}; - {{{ makeSetValue('dest', 'i', 'second', 'i8') }}} - {{{ makeSetValue('dest', 'i + 1', 'first', 'i8') }}} + {{{ makeSetValue('dest', 'i', 'second', 'i8') }}}; + {{{ makeSetValue('dest', 'i + 1', 'first', 'i8') }}}; } }, tcgetpgrp: function(fildes) { @@ -1565,14 +1572,6 @@ LibraryManager.library = { if (bytes != 0) self.alloc(bytes); return ret; // Previous break location. }, - open64: 'open', - lseek64: 'lseek', - ftruncate64: 'ftruncate', - __01open64_: 'open', - __01lseek64_: 'lseek', - __01truncate64_: 'truncate', - __01ftruncate64_: 'ftruncate', - // TODO: Check if any other aliases are needed. // ========================================================================== // stdio.h @@ -1643,6 +1642,7 @@ LibraryManager.library = { for (var i = 0; i < maxx; i++) { next = get(); {{{ makeSetValue('argPtr++', 0, 'next', 'i8') }}}; + if (next === 0) return i > 0 ? fields : fields-1; // we failed to read the full length of this field } formatIndex += nextC - formatIndex + 1; continue; @@ -1804,7 +1804,7 @@ LibraryManager.library = { break; case 'X': case 'x': - {{{ makeSetValue('argPtr', 0, 'parseInt(text, 16)', 'i32') }}} + {{{ makeSetValue('argPtr', 0, 'parseInt(text, 16)', 'i32') }}}; break; case 'F': case 'f': @@ -1815,15 +1815,15 @@ LibraryManager.library = { case 'E': // fallthrough intended if (long_) { - {{{ makeSetValue('argPtr', 0, 'parseFloat(text)', 'double') }}} + {{{ makeSetValue('argPtr', 0, 'parseFloat(text)', 'double') }}}; } else { - {{{ makeSetValue('argPtr', 0, 'parseFloat(text)', 'float') }}} + {{{ makeSetValue('argPtr', 0, 'parseFloat(text)', 'float') }}}; } break; case 's': var array = intArrayFromString(text); for (var j = 0; j < array.length; j++) { - {{{ makeSetValue('argPtr', 'j', 'array[j]', 'i8') }}} + {{{ makeSetValue('argPtr', 'j', 'array[j]', 'i8') }}}; } break; } @@ -1861,14 +1861,14 @@ LibraryManager.library = { // int x = 4; printf("%c\n", (char)x); var ret; if (type === 'double') { -#if TARGET_LE32 == 2 +#if TARGET_ASMJS_UNKNOWN_EMSCRIPTEN == 2 ret = {{{ makeGetValue('varargs', 'argIndex', 'double', undefined, undefined, true, 4) }}}; #else ret = {{{ makeGetValue('varargs', 'argIndex', 'double', undefined, undefined, true) }}}; #endif #if USE_TYPED_ARRAYS == 2 } else if (type == 'i64') { -#if TARGET_LE32 == 1 +#if TARGET_ASMJS_UNKNOWN_EMSCRIPTEN == 1 ret = [{{{ makeGetValue('varargs', 'argIndex', 'i32', undefined, undefined, true) }}}, {{{ makeGetValue('varargs', 'argIndex+8', 'i32', undefined, undefined, true) }}}]; argIndex += {{{ STACK_ALIGN }}}; // each 32-bit chunk is in a 64-bit block @@ -1885,7 +1885,7 @@ LibraryManager.library = { type = 'i32'; // varargs are always i32, i64, or double ret = {{{ makeGetValue('varargs', 'argIndex', 'i32', undefined, undefined, true) }}}; } -#if TARGET_LE32 == 2 +#if TARGET_ASMJS_UNKNOWN_EMSCRIPTEN == 2 argIndex += Runtime.getNativeFieldSize(type); #else argIndex += Math.max(Runtime.getNativeFieldSize(type), Runtime.getAlignSize(type, null, true)); @@ -1970,7 +1970,7 @@ LibraryManager.library = { } next = {{{ makeGetValue(0, 'textIndex+1', 'i8') }}}; } - if (precision === -1) { + if (precision < 0) { precision = 6; // Standard default. precisionSet = false; } @@ -2259,7 +2259,7 @@ LibraryManager.library = { case 'n': { // Write the length written so far to the next parameter. var ptr = getNextArg('i32*'); - {{{ makeSetValue('ptr', '0', 'ret.length', 'i32') }}} + {{{ makeSetValue('ptr', '0', 'ret.length', 'i32') }}}; break; } case '%': { @@ -2291,19 +2291,20 @@ LibraryManager.library = { clearerr: function(stream) { // void clearerr(FILE *stream); // http://pubs.opengroup.org/onlinepubs/000095399/functions/clearerr.html - stream = FS.getStream(stream); + stream = FS.getStreamFromPtr(stream); if (!stream) { return; } stream.eof = false; stream.error = false; }, - fclose__deps: ['close', 'fsync'], + fclose__deps: ['close', 'fsync', 'fileno'], fclose: function(stream) { // int fclose(FILE *stream); // http://pubs.opengroup.org/onlinepubs/000095399/functions/fclose.html - _fsync(stream); - return _close(stream); + var fd = _fileno(stream); + _fsync(fd); + return _close(fd); }, fdopen__deps: ['$FS', '__setErrNo', '$ERRNO_CODES'], fdopen: function(fildes, mode) { @@ -2324,21 +2325,21 @@ LibraryManager.library = { } else { stream.error = false; stream.eof = false; - return fildes; + return FS.getPtrForStream(stream); } }, feof__deps: ['$FS'], feof: function(stream) { // int feof(FILE *stream); // http://pubs.opengroup.org/onlinepubs/000095399/functions/feof.html - stream = FS.getStream(stream); + stream = FS.getStreamFromPtr(stream); return Number(stream && stream.eof); }, ferror__deps: ['$FS'], ferror: function(stream) { // int ferror(FILE *stream); // http://pubs.opengroup.org/onlinepubs/000095399/functions/ferror.html - stream = FS.getStream(stream); + stream = FS.getStreamFromPtr(stream); return Number(stream && stream.error); }, fflush__deps: ['$FS', '__setErrNo', '$ERRNO_CODES'], @@ -2352,7 +2353,7 @@ LibraryManager.library = { fgetc: function(stream) { // int fgetc(FILE *stream); // http://pubs.opengroup.org/onlinepubs/000095399/functions/fgetc.html - var streamObj = FS.getStream(stream); + var streamObj = FS.getStreamFromPtr(stream); if (!streamObj) return -1; if (streamObj.eof || streamObj.error) return -1; var ret = _fread(_fgetc.ret, 1, 1, stream); @@ -2377,7 +2378,7 @@ LibraryManager.library = { fgetpos: function(stream, pos) { // int fgetpos(FILE *restrict stream, fpos_t *restrict pos); // http://pubs.opengroup.org/onlinepubs/000095399/functions/fgetpos.html - stream = FS.getStream(stream); + stream = FS.getStreamFromPtr(stream); if (!stream) { ___setErrNo(ERRNO_CODES.EBADF); return -1; @@ -2386,16 +2387,16 @@ LibraryManager.library = { ___setErrNo(ERRNO_CODES.ESPIPE); return -1; } - {{{ makeSetValue('pos', '0', 'stream.position', 'i32') }}} + {{{ makeSetValue('pos', '0', 'stream.position', 'i32') }}}; var state = (stream.eof ? 1 : 0) + (stream.error ? 2 : 0); - {{{ makeSetValue('pos', Runtime.getNativeTypeSize('i32'), 'state', 'i32') }}} + {{{ makeSetValue('pos', Runtime.getNativeTypeSize('i32'), 'state', 'i32') }}}; return 0; }, fgets__deps: ['fgetc'], fgets: function(s, n, stream) { // char *fgets(char *restrict s, int n, FILE *restrict stream); // http://pubs.opengroup.org/onlinepubs/000095399/functions/fgets.html - var streamObj = FS.getStream(stream); + var streamObj = FS.getStreamFromPtr(stream); if (!streamObj) return 0; if (streamObj.error || streamObj.eof) return 0; var byte_; @@ -2405,9 +2406,9 @@ LibraryManager.library = { if (streamObj.error || (streamObj.eof && i == 0)) return 0; else if (streamObj.eof) break; } - {{{ makeSetValue('s', 'i', 'byte_', 'i8') }}} + {{{ makeSetValue('s', 'i', 'byte_', 'i8') }}}; } - {{{ makeSetValue('s', 'i', '0', 'i8') }}} + {{{ makeSetValue('s', 'i', '0', 'i8') }}}; return s; }, gets__deps: ['fgets'], @@ -2419,8 +2420,9 @@ LibraryManager.library = { fileno: function(stream) { // int fileno(FILE *stream); // http://pubs.opengroup.org/onlinepubs/000095399/functions/fileno.html - // We use file descriptor numbers and FILE* streams interchangeably. - return stream; + stream = FS.getStreamFromPtr(stream); + if (!stream) return -1; + return stream.fd; }, ftrylockfile: function() { // int ftrylockfile(FILE *file); @@ -2462,19 +2464,20 @@ LibraryManager.library = { ___setErrNo(ERRNO_CODES.EINVAL); return 0; } - var ret = _open(filename, flags, allocate([0x1FF, 0, 0, 0], 'i32', ALLOC_STACK)); // All creation permissions. - return (ret == -1) ? 0 : ret; + var fd = _open(filename, flags, allocate([0x1FF, 0, 0, 0], 'i32', ALLOC_STACK)); // All creation permissions. + return fd === -1 ? 0 : FS.getPtrForStream(FS.getStream(fd)); }, - fputc__deps: ['$FS', 'write'], + fputc__deps: ['$FS', 'write', 'fileno'], fputc__postset: '_fputc.ret = allocate([0], "i8", ALLOC_STATIC);', fputc: function(c, stream) { // int fputc(int c, FILE *stream); // http://pubs.opengroup.org/onlinepubs/000095399/functions/fputc.html var chr = unSign(c & 0xFF); - {{{ makeSetValue('_fputc.ret', '0', 'chr', 'i8') }}} - var ret = _write(stream, _fputc.ret, 1); + {{{ makeSetValue('_fputc.ret', '0', 'chr', 'i8') }}}; + var fd = _fileno(stream); + var ret = _write(fd, _fputc.ret, 1); if (ret == -1) { - var streamObj = FS.getStream(stream); + var streamObj = FS.getStreamFromPtr(stream); if (streamObj) streamObj.error = true; return -1; } else { @@ -2490,11 +2493,12 @@ LibraryManager.library = { return _fputc(c, {{{ makeGetValue(makeGlobalUse('_stdout'), '0', 'void*') }}}); }, putchar_unlocked: 'putchar', - fputs__deps: ['write', 'strlen'], + fputs__deps: ['write', 'strlen', 'fileno'], fputs: function(s, stream) { // int fputs(const char *restrict s, FILE *restrict stream); // http://pubs.opengroup.org/onlinepubs/000095399/functions/fputs.html - return _write(stream, s, _strlen(s)); + var fd = _fileno(stream); + return _write(fd, s, _strlen(s)); }, puts__deps: ['fputs', 'fputc', 'stdout'], puts: function(s) { @@ -2519,17 +2523,17 @@ LibraryManager.library = { return 0; } var bytesRead = 0; - var streamObj = FS.getStream(stream); + var streamObj = FS.getStreamFromPtr(stream); if (!streamObj) { ___setErrNo(ERRNO_CODES.EBADF); return 0; } while (streamObj.ungotten.length && bytesToRead > 0) { - {{{ makeSetValue('ptr++', '0', 'streamObj.ungotten.pop()', 'i8') }}} + {{{ makeSetValue('ptr++', '0', 'streamObj.ungotten.pop()', 'i8') }}}; bytesToRead--; bytesRead++; } - var err = _read(stream, ptr, bytesToRead); + var err = _read(streamObj.fd, ptr, bytesToRead); if (err == -1) { if (streamObj) streamObj.error = true; return 0; @@ -2543,7 +2547,7 @@ LibraryManager.library = { // FILE *freopen(const char *restrict filename, const char *restrict mode, FILE *restrict stream); // http://pubs.opengroup.org/onlinepubs/000095399/functions/freopen.html if (!filename) { - var streamObj = FS.getStream(stream); + var streamObj = FS.getStreamFromPtr(stream); if (!streamObj) { ___setErrNo(ERRNO_CODES.EBADF); return 0; @@ -2555,25 +2559,25 @@ LibraryManager.library = { _fclose(stream); return _fopen(filename, mode); }, - fseek__deps: ['$FS', 'lseek'], + fseek__deps: ['$FS', 'lseek', 'fileno'], fseek: function(stream, offset, whence) { // int fseek(FILE *stream, long offset, int whence); // http://pubs.opengroup.org/onlinepubs/000095399/functions/fseek.html - var ret = _lseek(stream, offset, whence); + var fd = _fileno(stream); + var ret = _lseek(fd, offset, whence); if (ret == -1) { return -1; } - stream = FS.getStream(stream); + stream = FS.getStreamFromPtr(stream); stream.eof = false; return 0; }, fseeko: 'fseek', - fseeko64: 'fseek', fsetpos__deps: ['$FS', 'lseek', '__setErrNo', '$ERRNO_CODES'], fsetpos: function(stream, pos) { // int fsetpos(FILE *stream, const fpos_t *pos); // http://pubs.opengroup.org/onlinepubs/000095399/functions/fsetpos.html - stream = FS.getStream(stream); + stream = FS.getStreamFromPtr(stream); if (!stream) { ___setErrNo(ERRNO_CODES.EBADF); return -1; @@ -2592,7 +2596,7 @@ LibraryManager.library = { ftell: function(stream) { // long ftell(FILE *stream); // http://pubs.opengroup.org/onlinepubs/000095399/functions/ftell.html - stream = FS.getStream(stream); + stream = FS.getStreamFromPtr(stream); if (!stream) { ___setErrNo(ERRNO_CODES.EBADF); return -1; @@ -2605,16 +2609,16 @@ LibraryManager.library = { } }, ftello: 'ftell', - ftello64: 'ftell', - fwrite__deps: ['$FS', 'write'], + fwrite__deps: ['$FS', 'write', 'fileno'], fwrite: function(ptr, size, nitems, stream) { // size_t fwrite(const void *restrict ptr, size_t size, size_t nitems, FILE *restrict stream); // http://pubs.opengroup.org/onlinepubs/000095399/functions/fwrite.html var bytesToWrite = nitems * size; if (bytesToWrite == 0) return 0; - var bytesWritten = _write(stream, ptr, bytesToWrite); + var fd = _fileno(stream); + var bytesWritten = _write(fd, ptr, bytesToWrite); if (bytesWritten == -1) { - var streamObj = FS.getStream(stream); + var streamObj = FS.getStreamFromPtr(stream); if (streamObj) streamObj.error = true; return 0; } else { @@ -2677,7 +2681,7 @@ LibraryManager.library = { // void rewind(FILE *stream); // http://pubs.opengroup.org/onlinepubs/000095399/functions/rewind.html _fseek(stream, 0, 0); // SEEK_SET. - var streamObj = FS.getStream(stream); + var streamObj = FS.getStreamFromPtr(stream); if (streamObj) streamObj.error = false; }, setvbuf: function(stream, buf, type, size) { @@ -2693,7 +2697,7 @@ LibraryManager.library = { if (buf) _setvbuf(stream, buf, 0, 8192); // _IOFBF, BUFSIZ. else _setvbuf(stream, buf, 2, 8192); // _IONBF, BUFSIZ. }, - tmpnam__deps: ['$FS'], + tmpnam__deps: ['$FS', 'malloc'], tmpnam: function(s, dir, prefix) { // char *tmpnam(char *s); // http://pubs.opengroup.org/onlinepubs/000095399/functions/tmpnam.html @@ -2734,7 +2738,7 @@ LibraryManager.library = { ungetc: function(c, stream) { // int ungetc(int c, FILE *stream); // http://pubs.opengroup.org/onlinepubs/000095399/functions/ungetc.html - stream = FS.getStream(stream); + stream = FS.getStreamFromPtr(stream); if (!stream) { return -1; } @@ -2759,7 +2763,7 @@ LibraryManager.library = { fscanf: function(stream, format, varargs) { // int fscanf(FILE *restrict stream, const char *restrict format, ... ); // http://pubs.opengroup.org/onlinepubs/000095399/functions/scanf.html - var streamObj = FS.getStream(stream); + var streamObj = FS.getStreamFromPtr(stream); if (!streamObj) { return -1; } @@ -2790,7 +2794,7 @@ LibraryManager.library = { function unget() { index--; }; return __scanString(format, get, unget, varargs); }, - snprintf__deps: ['_formatString'], + snprintf__deps: ['_formatString', 'malloc'], snprintf: function(s, n, format, varargs) { // int snprintf(char *restrict s, size_t n, const char *restrict format, ...); // http://pubs.opengroup.org/onlinepubs/000095399/functions/printf.html @@ -2857,7 +2861,7 @@ LibraryManager.library = { vsscanf: 'sscanf', #endif -#if TARGET_LE32 +#if TARGET_ASMJS_UNKNOWN_EMSCRIPTEN // convert va_arg into varargs vfprintf__deps: ['fprintf'], vfprintf: function(s, f, va_arg) { @@ -2897,28 +2901,12 @@ LibraryManager.library = { }, #endif - fopen64: 'fopen', - __01fopen64_: 'fopen', - __01freopen64_: 'freopen', - __01fseeko64_: 'fseek', - __01ftello64_: 'ftell', - __01tmpfile64_: 'tmpfile', - __isoc99_fscanf: 'fscanf', - // TODO: Check if any other aliases are needed. - _IO_getc: 'getc', - _IO_putc: 'putc', - _ZNSo3putEc: 'putchar', - _ZNSo5flushEv__deps: ['fflush', 'stdout'], - _ZNSo5flushEv: function() { - _fflush({{{ makeGetValue(makeGlobalUse('_stdout'), '0', 'void*') }}}); - }, - // ========================================================================== // sys/mman.h // ========================================================================== - mmap__deps: ['$FS'], - mmap: function(start, num, prot, flags, stream, offset) { + mmap__deps: ['$FS', 'malloc', 'memset'], + mmap: function(start, num, prot, flags, fd, offset) { /* FIXME: Since mmap is normally implemented at the kernel level, * this implementation simply uses malloc underneath the call to * mmap. @@ -2929,13 +2917,13 @@ LibraryManager.library = { if (!_mmap.mappings) _mmap.mappings = {}; - if (stream == -1) { + if (fd == -1) { ptr = _malloc(num); if (!ptr) return -1; _memset(ptr, 0, num); allocated = true; } else { - var info = FS.getStream(stream); + var info = FS.getStream(fd); if (!info) return -1; try { var res = FS.mmap(info, HEAPU8, start, num, offset, prot, flags); @@ -2950,7 +2938,6 @@ LibraryManager.library = { _mmap.mappings[ptr] = { malloc: ptr, num: num, allocated: allocated }; return ptr; }, - __01mmap64_: 'mmap', munmap: function(start, num) { if (!_mmap.mappings) _mmap.mappings = {}; @@ -3076,7 +3063,7 @@ LibraryManager.library = { return 0; }, - realloc__deps: ['memcpy'], + realloc__deps: ['malloc', 'memcpy', 'free'], realloc: function(ptr, size) { // Very simple, inefficient implementation - if you use a real malloc, best to use // a real realloc with it @@ -3147,7 +3134,7 @@ LibraryManager.library = { // Set end pointer. if (endptr) { - {{{ makeSetValue('endptr', 0, 'str', '*') }}} + {{{ makeSetValue('endptr', 0, 'str', '*') }}}; } // Unsign if needed. @@ -3233,7 +3220,7 @@ LibraryManager.library = { // Set end pointer. if (endptr) { - {{{ makeSetValue('endptr', 0, 'str', '*') }}} + {{{ makeSetValue('endptr', 0, 'str', '*') }}}; } try { @@ -3250,22 +3237,34 @@ LibraryManager.library = { strtoll: function(str, endptr, base) { return __parseInt64(str, endptr, base, '-9223372036854775808', '9223372036854775807'); // LLONG_MIN, LLONG_MAX. }, - strtoll_l: 'strtoll', // no locale support yet + strtoll_l__deps: ['strtoll'], + strtoll_l: function(str, endptr, base) { + return _strtoll(str, endptr, base); // no locale support yet + }, strtol__deps: ['_parseInt'], strtol: function(str, endptr, base) { return __parseInt(str, endptr, base, -2147483648, 2147483647, 32); // LONG_MIN, LONG_MAX. }, - strtol_l: 'strtol', // no locale support yet + strtol_l__deps: ['strtol'], + strtol_l: function(str, endptr, base) { + return _strtol(str, endptr, base); // no locale support yet + }, strtoul__deps: ['_parseInt'], strtoul: function(str, endptr, base) { return __parseInt(str, endptr, base, 0, 4294967295, 32, true); // ULONG_MAX. }, - strtoul_l: 'strtoul', // no locale support yet + strtoul_l__deps: ['strtoul'], + strtoul_l: function(str, endptr, base) { + return _strtoul(str, endptr, base); // no locale support yet + }, strtoull__deps: ['_parseInt64'], strtoull: function(str, endptr, base) { return __parseInt64(str, endptr, base, 0, '18446744073709551615', true); // ULONG_MAX. }, - strtoull_l: 'strtoull', // no locale support yet + strtoull_l__deps: ['strtoull'], + strtoull_l: function(str, endptr, base) { + return _strtoull(str, endptr, base); // no locale support yet + }, atoi__deps: ['strtol'], atoi: function(ptr) { @@ -3278,7 +3277,7 @@ LibraryManager.library = { return _strtoll(ptr, null, 10); }, - qsort__deps: ['memcpy'], + qsort__deps: ['malloc', 'memcpy', 'free'], qsort: function(base, num, size, cmp) { if (num == 0 || size == 0) return; // forward calls to the JavaScript sort method @@ -3327,7 +3326,7 @@ LibraryManager.library = { poolPtr = allocate(TOTAL_ENV_SIZE, 'i8', ALLOC_STATIC); envPtr = allocate(MAX_ENV_VALUES * {{{ Runtime.QUANTUM_SIZE }}}, 'i8*', ALLOC_STATIC); - {{{ makeSetValue('envPtr', '0', 'poolPtr', 'i8*') }}} + {{{ makeSetValue('envPtr', '0', 'poolPtr', 'i8*') }}}; {{{ makeSetValue(makeGlobalUse('_environ'), 0, 'envPtr', 'i8*') }}}; } else { envPtr = {{{ makeGetValue(makeGlobalUse('_environ'), '0', 'i8**') }}}; @@ -3451,18 +3450,30 @@ LibraryManager.library = { var limit = Math.min(nelem, 3); var doubleSize = {{{ Runtime.getNativeTypeSize('double') }}}; for (var i = 0; i < limit; i++) { - {{{ makeSetValue('loadavg', 'i * doubleSize', '0.1', 'double') }}} + {{{ makeSetValue('loadavg', 'i * doubleSize', '0.1', 'double') }}}; } return limit; }, - // Use browser's Math.random(). We can't set a seed, though. - srand: function(seed) {}, // XXX ignored - rand: function() { - return Math.floor(Math.random()*0x80000000); + __rand_seed: 'allocate([0x0273459b, 0, 0, 0], "i32", ALLOC_STATIC)', + srand__deps: ['__rand_seed'], + srand: function(seed) { + {{{ makeSetValue('___rand_seed', 0, 'seed', 'i32') }}} + }, + rand_r__sig: 'ii', + rand_r__asm: true, + rand_r: function(seedp) { + seedp = seedp|0; + var val = 0; + val = ((Math_imul({{{ makeGetValueAsm('seedp', 0, 'i32') }}}, 31010991)|0) + 0x676e6177 ) & {{{ cDefine('RAND_MAX') }}}; // assumes RAND_MAX is in bit mask form (power of 2 minus 1) + {{{ makeSetValueAsm('seedp', 0, 'val', 'i32') }}}; + return val|0; }, - rand_r: function(seed) { // XXX ignores the seed - return Math.floor(Math.random()*0x80000000); + rand__sig: 'i', + rand__asm: true, + rand__deps: ['rand_r', '__rand_seed'], + rand: function() { + return _rand_r(___rand_seed)|0; }, drand48: function() { @@ -3480,9 +3491,9 @@ LibraryManager.library = { } else { var size = Math.min(4095, absolute.path.length); // PATH_MAX - 1. for (var i = 0; i < size; i++) { - {{{ makeSetValue('resolved_name', 'i', 'absolute.path.charCodeAt(i)', 'i8') }}} + {{{ makeSetValue('resolved_name', 'i', 'absolute.path.charCodeAt(i)', 'i8') }}}; } - {{{ makeSetValue('resolved_name', 'size', '0', 'i8') }}} + {{{ makeSetValue('resolved_name', 'size', '0', 'i8') }}}; return resolved_name; } }, @@ -3506,11 +3517,18 @@ LibraryManager.library = { return ret; }, + emscripten_memcpy_big: function(dest, src, num) { + HEAPU8.set(HEAPU8.subarray(src, src+num), dest); + return dest; + }, + memcpy__asm: true, memcpy__sig: 'iiii', + memcpy__deps: ['emscripten_memcpy_big'], memcpy: function(dest, src, num) { dest = dest|0; src = src|0; num = num|0; var ret = 0; + if ((num|0) >= 4096) return _emscripten_memcpy_big(dest|0, src|0, num|0)|0; ret = dest|0; if ((dest&3) == (src&3)) { while (dest & 3) { @@ -3690,7 +3708,7 @@ LibraryManager.library = { var padding = 0, curr = 0, i = 0; while ((i|0) < (num|0)) { curr = padding ? 0 : {{{ makeGetValueAsm('psrc', 'i', 'i8') }}}; - {{{ makeSetValue('pdest', 'i', 'curr', 'i8') }}} + {{{ makeSetValue('pdest', 'i', 'curr', 'i8') }}}; padding = padding ? 1 : ({{{ makeGetValueAsm('psrc', 'i', 'i8') }}} == 0); i = (i+1)|0; } @@ -3743,83 +3761,13 @@ LibraryManager.library = { if ({{{ makeGetValue('pdest', 'len+i', 'i8') }}} == 0) break; i ++; if (i == num) { - {{{ makeSetValue('pdest', 'len+i', 0, 'i8') }}} + {{{ makeSetValue('pdest', 'len+i', 0, 'i8') }}}; break; } } return pdest; }, - strcmp__deps: ['strncmp'], - strcmp: function(px, py) { - return _strncmp(px, py, TOTAL_MEMORY); - }, - // We always assume ASCII locale. - strcoll: 'strcmp', - strcoll_l: 'strcmp', - - strcasecmp__asm: true, - strcasecmp__sig: 'iii', - strcasecmp__deps: ['strncasecmp'], - strcasecmp: function(px, py) { - px = px|0; py = py|0; - return _strncasecmp(px, py, -1)|0; - }, - - strncmp: function(px, py, n) { - var i = 0; - while (i < n) { - var x = {{{ makeGetValue('px', 'i', 'i8', 0, 1) }}}; - var y = {{{ makeGetValue('py', 'i', 'i8', 0, 1) }}}; - if (x == y && x == 0) return 0; - if (x == 0) return -1; - if (y == 0) return 1; - if (x == y) { - i ++; - continue; - } else { - return x > y ? 1 : -1; - } - } - return 0; - }, - - strncasecmp__asm: true, - strncasecmp__sig: 'iiii', - strncasecmp__deps: ['tolower'], - strncasecmp: function(px, py, n) { - px = px|0; py = py|0; n = n|0; - var i = 0, x = 0, y = 0; - while ((i>>>0) < (n>>>0)) { - x = _tolower({{{ makeGetValueAsm('px', 'i', 'i8', 0, 1) }}})|0; - y = _tolower({{{ makeGetValueAsm('py', 'i', 'i8', 0, 1) }}})|0; - if (((x|0) == (y|0)) & ((x|0) == 0)) return 0; - if ((x|0) == 0) return -1; - if ((y|0) == 0) return 1; - if ((x|0) == (y|0)) { - i = (i + 1)|0; - continue; - } else { - return ((x>>>0) > (y>>>0) ? 1 : -1)|0; - } - } - return 0; - }, - - memcmp__asm: true, - memcmp__sig: 'iiii', - memcmp: function(p1, p2, num) { - p1 = p1|0; p2 = p2|0; num = num|0; - var i = 0, v1 = 0, v2 = 0; - while ((i|0) < (num|0)) { - v1 = {{{ makeGetValueAsm('p1', 'i', 'i8', true) }}}; - v2 = {{{ makeGetValueAsm('p2', 'i', 'i8', true) }}}; - if ((v1|0) != (v2|0)) return ((v1|0) > (v2|0) ? 1 : -1)|0; - i = (i+1)|0; - } - return 0; - }, - memchr: function(ptr, chr, num) { chr = unSign(chr); for (var i = 0; i < num; i++) { @@ -3830,6 +3778,7 @@ LibraryManager.library = { }, strnlen: function(ptr, num) { + num = num >>> 0; for (var i = 0; i < num; i++) { if ({{{ makeGetValue('ptr', 0, 'i8') }}} == 0) return i; ptr++; @@ -3878,7 +3827,7 @@ LibraryManager.library = { }, rindex: 'strrchr', - strdup__deps: ['strlen'], + strdup__deps: ['strlen', 'malloc'], strdup: function(ptr) { var len = _strlen(ptr); var newStr = _malloc(len + 1); @@ -3887,7 +3836,7 @@ LibraryManager.library = { return newStr; }, - strndup__deps: ['strdup', 'strlen'], + strndup__deps: ['strdup', 'strlen', 'malloc'], strndup: function(ptr, size) { var len = _strlen(ptr); @@ -3995,7 +3944,7 @@ LibraryManager.library = { return ___setErrNo(ERRNO_CODES.EINVAL); } }, - strerror__deps: ['strerror_r'], + strerror__deps: ['strerror_r', 'malloc'], strerror: function(errnum) { if (!_strerror.buffer) _strerror.buffer = _malloc(256); _strerror_r(errnum, _strerror.buffer, 256); @@ -4020,7 +3969,10 @@ LibraryManager.library = { } }, _toupper: 'toupper', - toupper_l: 'toupper', + toupper_l__deps: ['toupper'], + toupper_l: function(str, endptr, base) { + return _toupper(str, endptr, base); // no locale support yet + }, tolower__asm: true, tolower__sig: 'ii', @@ -4031,66 +3983,106 @@ LibraryManager.library = { return (chr - {{{ charCode('A') }}} + {{{ charCode('a') }}})|0; }, _tolower: 'tolower', - tolower_l: 'tolower', + tolower_l__deps: ['tolower'], + tolower_l: function(chr) { + return _tolower(chr); // no locale support yet + }, // The following functions are defined as macros in glibc. islower: function(chr) { return chr >= {{{ charCode('a') }}} && chr <= {{{ charCode('z') }}}; }, - islower_l: 'islower', + islower_l__deps: ['islower'], + islower_l: function(chr) { + return _islower(chr); // no locale support yet + }, isupper: function(chr) { return chr >= {{{ charCode('A') }}} && chr <= {{{ charCode('Z') }}}; }, - isupper_l: 'isupper', + isupper_l__deps: ['isupper'], + isupper_l: function(chr) { + return _isupper(chr); // no locale support yet + }, isalpha: function(chr) { return (chr >= {{{ charCode('a') }}} && chr <= {{{ charCode('z') }}}) || (chr >= {{{ charCode('A') }}} && chr <= {{{ charCode('Z') }}}); }, - isalpha_l: 'isalpha', + isalpha_l__deps: ['isalpha'], + isalpha_l: function(chr) { + return _isalpha(chr); // no locale support yet + }, isdigit: function(chr) { return chr >= {{{ charCode('0') }}} && chr <= {{{ charCode('9') }}}; }, - isdigit_l: 'isdigit', + isdigit_l__deps: ['isdigit'], + isdigit_l: function(chr) { + return _isdigit(chr); // no locale support yet + }, isxdigit: function(chr) { return (chr >= {{{ charCode('0') }}} && chr <= {{{ charCode('9') }}}) || (chr >= {{{ charCode('a') }}} && chr <= {{{ charCode('f') }}}) || (chr >= {{{ charCode('A') }}} && chr <= {{{ charCode('F') }}}); }, - isxdigit_l: 'isxdigit', + isxdigit_l__deps: ['isxdigit'], + isxdigit_l: function(chr) { + return _isxdigit(chr); // no locale support yet + }, isalnum: function(chr) { return (chr >= {{{ charCode('0') }}} && chr <= {{{ charCode('9') }}}) || (chr >= {{{ charCode('a') }}} && chr <= {{{ charCode('z') }}}) || (chr >= {{{ charCode('A') }}} && chr <= {{{ charCode('Z') }}}); }, - isalnum_l: 'isalnum', + isalnum_l__deps: ['isalnum'], + isalnum_l: function(chr) { + return _isalnum(chr); // no locale support yet + }, ispunct: function(chr) { return (chr >= {{{ charCode('!') }}} && chr <= {{{ charCode('/') }}}) || (chr >= {{{ charCode(':') }}} && chr <= {{{ charCode('@') }}}) || (chr >= {{{ charCode('[') }}} && chr <= {{{ charCode('`') }}}) || (chr >= {{{ charCode('{') }}} && chr <= {{{ charCode('~') }}}); }, - ispunct_l: 'ispunct', + ispunct_l__deps: ['ispunct'], + ispunct_l: function(chr) { + return _ispunct(chr); // no locale support yet + }, isspace: function(chr) { return (chr == 32) || (chr >= 9 && chr <= 13); }, - isspace_l: 'isspace', + isspace_l__deps: ['isspace'], + isspace_l: function(chr) { + return _isspace(chr); // no locale support yet + }, isblank: function(chr) { return chr == {{{ charCode(' ') }}} || chr == {{{ charCode('\t') }}}; }, - isblank_l: 'isblank', + isblank_l__deps: ['isblank'], + isblank_l: function(chr) { + return _isblank(chr); // no locale support yet + }, iscntrl: function(chr) { return (0 <= chr && chr <= 0x1F) || chr === 0x7F; }, - iscntrl_l: 'iscntrl', + iscntrl_l__deps: ['iscntrl'], + iscntrl_l: function(chr) { + return _iscntrl(chr); // no locale support yet + }, isprint: function(chr) { return 0x1F < chr && chr < 0x7F; }, - isprint_l: 'isprint', + isprint_l__deps: ['isprint'], + isprint_l: function(chr) { + return _isprint(chr); // no locale support yet + }, isgraph: function(chr) { return 0x20 < chr && chr < 0x7F; }, - isgraph_l: 'isgraph', + isgraph_l__deps: ['isgraph'], + isgraph_l: function(chr) { + return _isgraph(chr); // no locale support yet + }, // Lookup tables for glibc ctype implementation. + __ctype_b_loc__deps: ['malloc'], __ctype_b_loc: function() { // http://refspecs.freestandards.org/LSB_3.0.0/LSB-Core-generic/LSB-Core-generic/baselib---ctype-b-loc.html var me = ___ctype_b_loc; @@ -4110,12 +4102,13 @@ LibraryManager.library = { var i16size = {{{ Runtime.getNativeTypeSize('i16') }}}; var arr = _malloc(values.length * i16size); for (var i = 0; i < values.length; i++) { - {{{ makeSetValue('arr', 'i * i16size', 'values[i]', 'i16') }}} + {{{ makeSetValue('arr', 'i * i16size', 'values[i]', 'i16') }}}; } me.ret = allocate([arr + 128 * i16size], 'i16*', ALLOC_NORMAL); } return me.ret; }, + __ctype_tolower_loc__deps: ['malloc'], __ctype_tolower_loc: function() { // http://refspecs.freestandards.org/LSB_3.1.1/LSB-Core-generic/LSB-Core-generic/libutil---ctype-tolower-loc.html var me = ___ctype_tolower_loc; @@ -4138,12 +4131,13 @@ LibraryManager.library = { var i32size = {{{ Runtime.getNativeTypeSize('i32') }}}; var arr = _malloc(values.length * i32size); for (var i = 0; i < values.length; i++) { - {{{ makeSetValue('arr', 'i * i32size', 'values[i]', 'i32') }}} + {{{ makeSetValue('arr', 'i * i32size', 'values[i]', 'i32') }}}; } me.ret = allocate([arr + 128 * i32size], 'i32*', ALLOC_NORMAL); } return me.ret; }, + __ctype_toupper_loc__deps: ['malloc'], __ctype_toupper_loc: function() { // http://refspecs.freestandards.org/LSB_3.1.1/LSB-Core-generic/LSB-Core-generic/libutil---ctype-toupper-loc.html var me = ___ctype_toupper_loc; @@ -4165,7 +4159,7 @@ LibraryManager.library = { var i32size = {{{ Runtime.getNativeTypeSize('i32') }}}; var arr = _malloc(values.length * i32size); for (var i = 0; i < values.length; i++) { - {{{ makeSetValue('arr', 'i * i32size', 'values[i]', 'i32') }}} + {{{ makeSetValue('arr', 'i * i32size', 'values[i]', 'i32') }}}; } me.ret = allocate([arr + 128 * i32size], 'i32*', ALLOC_NORMAL); } @@ -4186,7 +4180,7 @@ LibraryManager.library = { #if TARGET_X86 return makeSetValue(ptr, 0, 'varrp', 'void*'); #endif -#if TARGET_LE32 +#if TARGET_ASMJS_UNKNOWN_EMSCRIPTEN // 2-word structure: struct { void* start; void* currentOffset; } return makeSetValue(ptr, 0, 'varrp', 'void*') + ';' + makeSetValue(ptr, Runtime.QUANTUM_SIZE, 0, 'void*'); #endif @@ -4311,6 +4305,8 @@ LibraryManager.library = { abort('trap!'); }, + llvm_prefetch: function(){}, + __assert_fail: function(condition, filename, line, func) { ABORT = true; throw 'Assertion failed: ' + Pointer_stringify(condition) + ', at: ' + [filename ? Pointer_stringify(filename) : 'unknown filename', line, func ? Pointer_stringify(func) : 'unknown function'] + ' at ' + stackTrace(); @@ -4336,21 +4332,36 @@ LibraryManager.library = { _ZTVN10__cxxabiv120__si_class_type_infoE: [2], // yes inherited classes #endif + // We store an extra header in front of the exception data provided + // by the user. + // This header is: + // * type + // * destructor function pointer + // This is then followed by the actual exception data. + __cxa_exception_header_size: 8, + __cxa_last_thrown_exception: 0, + __cxa_caught_exceptions: [], + // Exceptions + __cxa_allocate_exception__deps: ['__cxa_exception_header_size', 'malloc'], __cxa_allocate_exception: function(size) { - return _malloc(size); + var ptr = _malloc(size + ___cxa_exception_header_size); + return ptr + ___cxa_exception_header_size; }, + __cxa_free_exception__deps: ['__cxa_exception_header_size', 'free'], __cxa_free_exception: function(ptr) { try { - return _free(ptr); + return _free(ptr - ___cxa_exception_header_size); } catch(e) { // XXX FIXME #if ASSERTIONS Module.printErr('exception during cxa_free_exception: ' + e); #endif } }, + // Here, we throw an exception after recording a couple of values that we need to remember + // We also remember that it was the last exception thrown as we need to know that later. __cxa_throw__sig: 'viii', - __cxa_throw__deps: ['llvm_eh_exception', '_ZSt18uncaught_exceptionv', '__cxa_find_matching_catch'], + __cxa_throw__deps: ['_ZSt18uncaught_exceptionv', '__cxa_find_matching_catch', '__cxa_exception_header_size', '__cxa_last_thrown_exception'], __cxa_throw: function(ptr, type, destructor) { if (!___cxa_throw.initialized) { try { @@ -4367,28 +4378,34 @@ LibraryManager.library = { #if EXCEPTION_DEBUG Module.printErr('Compiled code throwing an exception, ' + [ptr,type,destructor] + ', at ' + stackTrace()); #endif - {{{ makeSetValue('_llvm_eh_exception.buf', '0', 'ptr', 'void*') }}} - {{{ makeSetValue('_llvm_eh_exception.buf', QUANTUM_SIZE, 'type', 'void*') }}} - {{{ makeSetValue('_llvm_eh_exception.buf', 2 * QUANTUM_SIZE, 'destructor', 'void*') }}} + var header = ptr - ___cxa_exception_header_size; + {{{ makeSetValue('header', 0, 'type', 'void*') }}}; + {{{ makeSetValue('header', 4, 'destructor', 'void*') }}}; + ___cxa_last_thrown_exception = ptr; if (!("uncaught_exception" in __ZSt18uncaught_exceptionv)) { __ZSt18uncaught_exceptionv.uncaught_exception = 1; } else { __ZSt18uncaught_exceptionv.uncaught_exception++; } - {{{ makeThrow('ptr') }}}; + {{{ makeThrow('ptr') }}} }, - __cxa_rethrow__deps: ['llvm_eh_exception', '__cxa_end_catch'], + // This exception will be caught twice, but while begin_catch runs twice, + // we early-exit from end_catch when the exception has been rethrown, so + // pop that here from the caught exceptions. + __cxa_rethrow__deps: ['__cxa_end_catch', '__cxa_caught_exceptions'], __cxa_rethrow: function() { ___cxa_end_catch.rethrown = true; - {{{ makeThrow(makeGetValue('_llvm_eh_exception.buf', '0', 'void*')) }}}; + var ptr = ___cxa_caught_exceptions.pop(); + {{{ makeThrow('ptr') }}} }, - llvm_eh_exception__postset: '_llvm_eh_exception.buf = allocate(12, "void*", ALLOC_STATIC);', + llvm_eh_exception__deps: ['__cxa_last_thrown_exception'], llvm_eh_exception: function() { - return {{{ makeGetValue('_llvm_eh_exception.buf', '0', 'void*') }}}; + return ___cxa_last_thrown_exception; }, llvm_eh_selector__jsargs: true, + llvm_eh_selector__deps: ['__cxa_last_thrown_exception'], llvm_eh_selector: function(unused_exception_value, personality/*, varargs*/) { - var type = {{{ makeGetValue('_llvm_eh_exception.buf', QUANTUM_SIZE, 'void*') }}} + var type = ___cxa_last_thrown_exception; for (var i = 2; i < arguments.length; i++) { if (arguments[i] == type) return type; } @@ -4397,12 +4414,22 @@ LibraryManager.library = { llvm_eh_typeid_for: function(type) { return type; }, - __cxa_begin_catch__deps: ['_ZSt18uncaught_exceptionv'], + // Note that we push the last thrown exception here rather than the ptr. + // This is because if the exception is a pointer (as in test 3 of test_exceptions_typed), + // we don't actually get the value that we allocated, but something else. Easiest + // to remember that the last exception thrown is going to be the first to be caught, + // so just use that value instead as it is what we're really looking for. + __cxa_begin_catch__deps: ['_ZSt18uncaught_exceptionv', '__cxa_caught_exceptions', '__cxa_last_thrown_exception'], __cxa_begin_catch: function(ptr) { __ZSt18uncaught_exceptionv.uncaught_exception--; + ___cxa_caught_exceptions.push(___cxa_last_thrown_exception); return ptr; }, - __cxa_end_catch__deps: ['llvm_eh_exception', '__cxa_free_exception'], + // We're done with a catch. Now, we can run the destructor if there is one + // and free the exception. Note that if the dynCall on the destructor fails + // due to calling apply on undefined, that means that the destructor is + // an invalid index into the FUNCTION_TABLE, so something has gone wrong. + __cxa_end_catch__deps: ['__cxa_free_exception', '__cxa_last_thrown_exception', '__cxa_exception_header_size', '__cxa_caught_exceptions'], __cxa_end_catch: function() { if (___cxa_end_catch.rethrown) { ___cxa_end_catch.rethrown = false; @@ -4414,29 +4441,26 @@ LibraryManager.library = { #else __THREW__ = 0; #endif - // Clear type. - {{{ makeSetValue('_llvm_eh_exception.buf', QUANTUM_SIZE, '0', 'void*') }}} // Call destructor if one is registered then clear it. - var ptr = {{{ makeGetValue('_llvm_eh_exception.buf', '0', 'void*') }}}; - var destructor = {{{ makeGetValue('_llvm_eh_exception.buf', 2 * QUANTUM_SIZE, 'void*') }}}; - if (destructor) { - Runtime.dynCall('vi', destructor, [ptr]); - {{{ makeSetValue('_llvm_eh_exception.buf', 2 * QUANTUM_SIZE, '0', 'i32') }}} - } - // Free ptr if it isn't null. + var ptr = ___cxa_caught_exceptions.pop(); if (ptr) { + header = ptr - ___cxa_exception_header_size; + var destructor = {{{ makeGetValue('header', 4, 'void*') }}}; + if (destructor) { + Runtime.dynCall('vi', destructor, [ptr]); + {{{ makeSetValue('header', 4, '0', 'i32') }}}; + } ___cxa_free_exception(ptr); - {{{ makeSetValue('_llvm_eh_exception.buf', '0', '0', 'void*') }}} + ___cxa_last_thrown_exception = 0; } }, - __cxa_get_exception_ptr__deps: ['llvm_eh_exception'], __cxa_get_exception_ptr: function(ptr) { return ptr; }, _ZSt18uncaught_exceptionv: function() { // std::uncaught_exception() return !!__ZSt18uncaught_exceptionv.uncaught_exception; }, - __cxa_uncaught_exception__deps: ['_Zst18uncaught_exceptionv'], + __cxa_uncaught_exception__deps: ['_ZSt18uncaught_exceptionv'], __cxa_uncaught_exception: function() { return !!__ZSt18uncaught_exceptionv.uncaught_exception; }, @@ -4447,17 +4471,9 @@ LibraryManager.library = { throw exception; }, - _Unwind_Resume_or_Rethrow: function(ptr) { - {{{ makeThrow('ptr') }}}; - }, - _Unwind_RaiseException: function(ptr) { - {{{ makeThrow('ptr') }}}; - }, - _Unwind_DeleteException: function(ptr) {}, - terminate: '__cxa_call_unexpected', - __gxx_personality_v0__deps: ['llvm_eh_exception', '_ZSt18uncaught_exceptionv', '__cxa_find_matching_catch'], + __gxx_personality_v0__deps: ['_ZSt18uncaught_exceptionv', '__cxa_find_matching_catch'], __gxx_personality_v0: function() { }, @@ -4490,10 +4506,11 @@ LibraryManager.library = { // functionality boils down to picking a suitable 'catch' block. // We'll do that here, instead, to keep things simpler. - __cxa_find_matching_catch__deps: ['__cxa_does_inherit', '__cxa_is_number_type', '__resumeException'], + __cxa_find_matching_catch__deps: ['__cxa_does_inherit', '__cxa_is_number_type', '__resumeException', '__cxa_last_thrown_exception', '__cxa_exception_header_size'], __cxa_find_matching_catch: function(thrown, throwntype) { - if (thrown == -1) thrown = {{{ makeGetValue('_llvm_eh_exception.buf', '0', 'void*') }}}; - if (throwntype == -1) throwntype = {{{ makeGetValue('_llvm_eh_exception.buf', QUANTUM_SIZE, 'void*') }}}; + if (thrown == -1) thrown = ___cxa_last_thrown_exception; + header = thrown - ___cxa_exception_header_size; + if (throwntype == -1) throwntype = {{{ makeGetValue('header', 0, 'void*') }}}; var typeArray = Array.prototype.slice.call(arguments, 2); // If throwntype is a pointer, this means a pointer has been @@ -4519,13 +4536,13 @@ LibraryManager.library = { {{{ makeStructuralReturn(['thrown', 'throwntype']) }}}; }, - __resumeException__deps: [function() { Functions.libraryFunctions['__resumeException'] = 1 }], // will be called directly from compiled code + __resumeException__deps: [function() { Functions.libraryFunctions['__resumeException'] = 1 }, '__cxa_last_thrown_exception'], // will be called directly from compiled code __resumeException: function(ptr) { #if EXCEPTION_DEBUG Module.print("Resuming exception"); #endif - if ({{{ makeGetValue('_llvm_eh_exception.buf', 0, 'void*') }}} == 0) {{{ makeSetValue('_llvm_eh_exception.buf', 0, 'ptr', 'void*') }}}; - {{{ makeThrow('ptr') }}}; + if (!___cxa_last_thrown_exception) { ___cxa_last_thrown_exception = ptr; } + {{{ makeThrow('ptr') }}} }, // Recursively walks up the base types of 'possibilityType' @@ -4567,7 +4584,21 @@ LibraryManager.library = { } }, - _ZNSt9exceptionD2Ev: function(){}, // XXX a dependency of dlmalloc, but not actually needed if libcxx is not anyhow included + // Destructors for std::exception since we don't have them implemented in libcxx as we aren't using libcxxabi. + // These are also needed for the dlmalloc tests. + _ZNSt9exceptionD0Ev: function() {}, + _ZNSt9exceptionD1Ev: function() {}, + _ZNSt9exceptionD2Ev: function() {}, + + _ZNKSt9exception4whatEv__deps: ['malloc'], + _ZNKSt9exception4whatEv: function() { + if (!__ZNKSt9exception4whatEv.buffer) { + var name = "std::exception"; + __ZNKSt9exception4whatEv.buffer = _malloc(name.length + 1); + writeStringToMemory(name, __ZNKSt9exception4whatEv.buffer); + } + return __ZNKSt9exception4whatEv.buffer; + }, _ZNSt9type_infoD2Ev: function(){}, @@ -4592,6 +4623,8 @@ LibraryManager.library = { _ZTIv: [0], // void _ZTIPv: [0], // void* + _ZTISt9exception: 'allocate([allocate([1,0,0,0,0,0,0], "i8", ALLOC_STATIC)+8, 0], "i32", ALLOC_STATIC)', // typeinfo for std::exception + llvm_uadd_with_overflow_i8: function(x, y) { x = x & 0xff; y = y & 0xff; @@ -4837,11 +4870,11 @@ LibraryManager.library = { cbrtl: 'cbrt', modf: function(x, intpart) { - {{{ makeSetValue('intpart', 0, 'Math.floor(x)', 'double') }}} + {{{ makeSetValue('intpart', 0, 'Math.floor(x)', 'double') }}}; return x - {{{ makeGetValue('intpart', 0, 'double') }}}; }, modff: function(x, intpart) { - {{{ makeSetValue('intpart', 0, 'Math.floor(x)', 'float') }}} + {{{ makeSetValue('intpart', 0, 'Math.floor(x)', 'float') }}}; return x - {{{ makeGetValue('intpart', 0, 'float') }}}; }, frexp: function(x, exp_addr) { @@ -4857,7 +4890,7 @@ LibraryManager.library = { if (exp_ === raw_exp) exp_ += 1; sig = sign*x/Math.pow(2, exp_); } - {{{ makeSetValue('exp_addr', 0, 'exp_', 'i32') }}} + {{{ makeSetValue('exp_addr', 0, 'exp_', 'i32') }}}; return sig; }, frexpf: 'frexp', @@ -5365,7 +5398,7 @@ LibraryManager.library = { time: function(ptr) { var ret = Math.floor(Date.now()/1000); if (ptr) { - {{{ makeSetValue('ptr', 0, 'ret', 'i32') }}} + {{{ makeSetValue('ptr', 0, 'ret', 'i32') }}}; } return ret; }, @@ -5392,9 +5425,9 @@ LibraryManager.library = { {{{ makeGetValue('tmPtr', C_STRUCTS.tm.tm_min, 'i32') }}}, {{{ makeGetValue('tmPtr', C_STRUCTS.tm.tm_sec, 'i32') }}}, 0).getTime() / 1000; - {{{ makeSetValue('tmPtr', C_STRUCTS.tm.tm_wday, 'new Date(timestamp).getDay()', 'i32') }}} + {{{ makeSetValue('tmPtr', C_STRUCTS.tm.tm_wday, 'new Date(timestamp).getDay()', 'i32') }}}; var yday = Math.round((timestamp - (new Date(year, 0, 1)).getTime()) / (1000 * 60 * 60 * 24)); - {{{ makeSetValue('tmPtr', C_STRUCTS.tm.tm_yday, 'yday', 'i32') }}} + {{{ makeSetValue('tmPtr', C_STRUCTS.tm.tm_yday, 'yday', 'i32') }}}; return timestamp; }, timelocal: 'mktime', @@ -5407,15 +5440,15 @@ LibraryManager.library = { gmtime_r__deps: ['__tm_timezone'], gmtime_r: function(time, tmPtr) { var date = new Date({{{ makeGetValue('time', 0, 'i32') }}}*1000); - {{{ makeSetValue('tmPtr', C_STRUCTS.tm.tm_sec, 'date.getUTCSeconds()', 'i32') }}} - {{{ makeSetValue('tmPtr', C_STRUCTS.tm.tm_min, 'date.getUTCMinutes()', 'i32') }}} - {{{ makeSetValue('tmPtr', C_STRUCTS.tm.tm_hour, 'date.getUTCHours()', 'i32') }}} - {{{ makeSetValue('tmPtr', C_STRUCTS.tm.tm_mday, 'date.getUTCDate()', 'i32') }}} - {{{ makeSetValue('tmPtr', C_STRUCTS.tm.tm_mon, 'date.getUTCMonth()', 'i32') }}} - {{{ makeSetValue('tmPtr', C_STRUCTS.tm.tm_year, 'date.getUTCFullYear()-1900', 'i32') }}} - {{{ makeSetValue('tmPtr', C_STRUCTS.tm.tm_wday, 'date.getUTCDay()', 'i32') }}} - {{{ makeSetValue('tmPtr', C_STRUCTS.tm.tm_gmtoff, '0', 'i32') }}} - {{{ makeSetValue('tmPtr', C_STRUCTS.tm.tm_isdst, '0', 'i32') }}} + {{{ makeSetValue('tmPtr', C_STRUCTS.tm.tm_sec, 'date.getUTCSeconds()', 'i32') }}}; + {{{ makeSetValue('tmPtr', C_STRUCTS.tm.tm_min, 'date.getUTCMinutes()', 'i32') }}}; + {{{ makeSetValue('tmPtr', C_STRUCTS.tm.tm_hour, 'date.getUTCHours()', 'i32') }}}; + {{{ makeSetValue('tmPtr', C_STRUCTS.tm.tm_mday, 'date.getUTCDate()', 'i32') }}}; + {{{ makeSetValue('tmPtr', C_STRUCTS.tm.tm_mon, 'date.getUTCMonth()', 'i32') }}}; + {{{ makeSetValue('tmPtr', C_STRUCTS.tm.tm_year, 'date.getUTCFullYear()-1900', 'i32') }}}; + {{{ makeSetValue('tmPtr', C_STRUCTS.tm.tm_wday, 'date.getUTCDay()', 'i32') }}}; + {{{ makeSetValue('tmPtr', C_STRUCTS.tm.tm_gmtoff, '0', 'i32') }}}; + {{{ makeSetValue('tmPtr', C_STRUCTS.tm.tm_isdst, '0', 'i32') }}}; var start = new Date(date); // define date using UTC, start from Jan 01 00:00:00 UTC start.setUTCDate(1); start.setUTCMonth(0); @@ -5424,8 +5457,8 @@ LibraryManager.library = { start.setUTCSeconds(0); start.setUTCMilliseconds(0); var yday = Math.floor((date.getTime() - start.getTime()) / (1000 * 60 * 60 * 24)); - {{{ makeSetValue('tmPtr', C_STRUCTS.tm.tm_yday, 'yday', 'i32') }}} - {{{ makeSetValue('tmPtr', C_STRUCTS.tm.tm_zone, '___tm_timezone', 'i32') }}} + {{{ makeSetValue('tmPtr', C_STRUCTS.tm.tm_yday, 'yday', 'i32') }}}; + {{{ makeSetValue('tmPtr', C_STRUCTS.tm.tm_zone, '___tm_timezone', 'i32') }}}; return tmPtr; }, @@ -5448,23 +5481,23 @@ LibraryManager.library = { localtime_r: function(time, tmPtr) { _tzset(); var date = new Date({{{ makeGetValue('time', 0, 'i32') }}}*1000); - {{{ makeSetValue('tmPtr', C_STRUCTS.tm.tm_sec, 'date.getSeconds()', 'i32') }}} - {{{ makeSetValue('tmPtr', C_STRUCTS.tm.tm_min, 'date.getMinutes()', 'i32') }}} - {{{ makeSetValue('tmPtr', C_STRUCTS.tm.tm_hour, 'date.getHours()', 'i32') }}} - {{{ makeSetValue('tmPtr', C_STRUCTS.tm.tm_mday, 'date.getDate()', 'i32') }}} - {{{ makeSetValue('tmPtr', C_STRUCTS.tm.tm_mon, 'date.getMonth()', 'i32') }}} - {{{ makeSetValue('tmPtr', C_STRUCTS.tm.tm_year, 'date.getFullYear()-1900', 'i32') }}} - {{{ makeSetValue('tmPtr', C_STRUCTS.tm.tm_wday, 'date.getDay()', 'i32') }}} + {{{ makeSetValue('tmPtr', C_STRUCTS.tm.tm_sec, 'date.getSeconds()', 'i32') }}}; + {{{ makeSetValue('tmPtr', C_STRUCTS.tm.tm_min, 'date.getMinutes()', 'i32') }}}; + {{{ makeSetValue('tmPtr', C_STRUCTS.tm.tm_hour, 'date.getHours()', 'i32') }}}; + {{{ makeSetValue('tmPtr', C_STRUCTS.tm.tm_mday, 'date.getDate()', 'i32') }}}; + {{{ makeSetValue('tmPtr', C_STRUCTS.tm.tm_mon, 'date.getMonth()', 'i32') }}}; + {{{ makeSetValue('tmPtr', C_STRUCTS.tm.tm_year, 'date.getFullYear()-1900', 'i32') }}}; + {{{ makeSetValue('tmPtr', C_STRUCTS.tm.tm_wday, 'date.getDay()', 'i32') }}}; var start = new Date(date.getFullYear(), 0, 1); var yday = Math.floor((date.getTime() - start.getTime()) / (1000 * 60 * 60 * 24)); - {{{ makeSetValue('tmPtr', C_STRUCTS.tm.tm_yday, 'yday', 'i32') }}} - {{{ makeSetValue('tmPtr', C_STRUCTS.tm.tm_gmtoff, 'start.getTimezoneOffset() * 60', 'i32') }}} + {{{ makeSetValue('tmPtr', C_STRUCTS.tm.tm_yday, 'yday', 'i32') }}}; + {{{ makeSetValue('tmPtr', C_STRUCTS.tm.tm_gmtoff, 'start.getTimezoneOffset() * 60', 'i32') }}}; var dst = Number(start.getTimezoneOffset() != date.getTimezoneOffset()); - {{{ makeSetValue('tmPtr', C_STRUCTS.tm.tm_isdst, 'dst', 'i32') }}} + {{{ makeSetValue('tmPtr', C_STRUCTS.tm.tm_isdst, 'dst', 'i32') }}}; - {{{ makeSetValue('tmPtr', C_STRUCTS.tm.tm_zone, '___tm_timezone', 'i32') }}} + {{{ makeSetValue('tmPtr', C_STRUCTS.tm.tm_zone, '___tm_timezone', 'i32') }}}; return tmPtr; }, @@ -5482,9 +5515,9 @@ LibraryManager.library = { var timePart = formatted.match(/\d{2}:\d{2}:\d{2}/)[0]; formatted = datePart + timePart + ' ' + date.getFullYear() + '\n'; formatted.split('').forEach(function(chr, index) { - {{{ makeSetValue('buf', 'index', 'chr.charCodeAt(0)', 'i8') }}} + {{{ makeSetValue('buf', 'index', 'chr.charCodeAt(0)', 'i8') }}}; }); - {{{ makeSetValue('buf', '25', '0', 'i8') }}} + {{{ makeSetValue('buf', '25', '0', 'i8') }}}; return buf; }, @@ -5514,18 +5547,18 @@ LibraryManager.library = { if (_tzset.called) return; _tzset.called = true; - {{{ makeSetValue(makeGlobalUse('_timezone'), '0', '-(new Date()).getTimezoneOffset() * 60', 'i32') }}} + {{{ makeSetValue(makeGlobalUse('_timezone'), '0', '-(new Date()).getTimezoneOffset() * 60', 'i32') }}}; var winter = new Date(2000, 0, 1); var summer = new Date(2000, 6, 1); - {{{ makeSetValue(makeGlobalUse('_daylight'), '0', 'Number(winter.getTimezoneOffset() != summer.getTimezoneOffset())', 'i32') }}} + {{{ makeSetValue(makeGlobalUse('_daylight'), '0', 'Number(winter.getTimezoneOffset() != summer.getTimezoneOffset())', 'i32') }}}; var winterName = 'GMT'; // XXX do not rely on browser timezone info, it is very unpredictable | winter.toString().match(/\(([A-Z]+)\)/)[1]; var summerName = 'GMT'; // XXX do not rely on browser timezone info, it is very unpredictable | summer.toString().match(/\(([A-Z]+)\)/)[1]; var winterNamePtr = allocate(intArrayFromString(winterName), 'i8', ALLOC_NORMAL); var summerNamePtr = allocate(intArrayFromString(summerName), 'i8', ALLOC_NORMAL); - {{{ makeSetValue(makeGlobalUse('_tzname'), '0', 'winterNamePtr', 'i32') }}} - {{{ makeSetValue(makeGlobalUse('_tzname'), Runtime.QUANTUM_SIZE, 'summerNamePtr', 'i32') }}} + {{{ makeSetValue(makeGlobalUse('_tzname'), '0', 'winterNamePtr', 'i32') }}}; + {{{ makeSetValue(makeGlobalUse('_tzname'), Runtime.QUANTUM_SIZE, 'summerNamePtr', 'i32') }}}; }, stime__deps: ['$ERRNO_CODES', '__setErrNo'], @@ -5870,7 +5903,10 @@ LibraryManager.library = { writeArrayToMemory(bytes, s); return bytes.length-1; }, - strftime_l: 'strftime', // no locale support yet + strftime_l__deps: ['strftime'], + strftime_l: function(s, maxsize, format, tm) { + return _strftime(s, maxsize, format, tm); // no locale support yet + }, strptime__deps: ['_isLeapYear', '_arraySum', '_addDays', '_MONTH_DAYS_REGULAR', '_MONTH_DAYS_LEAP'], strptime: function(buf, format, tm) { @@ -6095,15 +6131,15 @@ LibraryManager.library = { */ var fullDate = new Date(date.year, date.month, date.day, date.hour, date.min, date.sec, 0); - {{{ makeSetValue('tm', C_STRUCTS.tm.tm_sec, 'fullDate.getSeconds()', 'i32') }}} - {{{ makeSetValue('tm', C_STRUCTS.tm.tm_min, 'fullDate.getMinutes()', 'i32') }}} - {{{ makeSetValue('tm', C_STRUCTS.tm.tm_hour, 'fullDate.getHours()', 'i32') }}} - {{{ makeSetValue('tm', C_STRUCTS.tm.tm_mday, 'fullDate.getDate()', 'i32') }}} - {{{ makeSetValue('tm', C_STRUCTS.tm.tm_mon, 'fullDate.getMonth()', 'i32') }}} - {{{ makeSetValue('tm', C_STRUCTS.tm.tm_year, 'fullDate.getFullYear()-1900', 'i32') }}} - {{{ makeSetValue('tm', C_STRUCTS.tm.tm_wday, 'fullDate.getDay()', 'i32') }}} - {{{ makeSetValue('tm', C_STRUCTS.tm.tm_yday, '__arraySum(__isLeapYear(fullDate.getFullYear()) ? __MONTH_DAYS_LEAP : __MONTH_DAYS_REGULAR, fullDate.getMonth()-1)+fullDate.getDate()-1', 'i32') }}} - {{{ makeSetValue('tm', C_STRUCTS.tm.tm_isdst, '0', 'i32') }}} + {{{ makeSetValue('tm', C_STRUCTS.tm.tm_sec, 'fullDate.getSeconds()', 'i32') }}}; + {{{ makeSetValue('tm', C_STRUCTS.tm.tm_min, 'fullDate.getMinutes()', 'i32') }}}; + {{{ makeSetValue('tm', C_STRUCTS.tm.tm_hour, 'fullDate.getHours()', 'i32') }}}; + {{{ makeSetValue('tm', C_STRUCTS.tm.tm_mday, 'fullDate.getDate()', 'i32') }}}; + {{{ makeSetValue('tm', C_STRUCTS.tm.tm_mon, 'fullDate.getMonth()', 'i32') }}}; + {{{ makeSetValue('tm', C_STRUCTS.tm.tm_year, 'fullDate.getFullYear()-1900', 'i32') }}}; + {{{ makeSetValue('tm', C_STRUCTS.tm.tm_wday, 'fullDate.getDay()', 'i32') }}}; + {{{ makeSetValue('tm', C_STRUCTS.tm.tm_yday, '__arraySum(__isLeapYear(fullDate.getFullYear()) ? __MONTH_DAYS_LEAP : __MONTH_DAYS_REGULAR, fullDate.getMonth()-1)+fullDate.getDate()-1', 'i32') }}}; + {{{ makeSetValue('tm', C_STRUCTS.tm.tm_isdst, '0', 'i32') }}}; // we need to convert the matched sequence into an integer array to take care of UTF-8 characters > 0x7F // TODO: not sure that intArrayFromString handles all unicode characters correctly @@ -6112,7 +6148,10 @@ LibraryManager.library = { return 0; }, - strptime_l: 'strptime', // no locale support yet + strptime_l__deps: ['strptime'], + strptime_l: function(buf, format, tm) { + return _strptime(buf, format, tm); // no locale support yet + }, getdate: function(string) { // struct tm *getdate(const char *string); @@ -6134,8 +6173,8 @@ LibraryManager.library = { var seconds = {{{ makeGetValue('rqtp', C_STRUCTS.timespec.tv_sec, 'i32') }}}; var nanoseconds = {{{ makeGetValue('rqtp', C_STRUCTS.timespec.tv_nsec, 'i32') }}}; if (rmtp !== 0) { - {{{ makeSetValue('rmtp', C_STRUCTS.timespec.tv_sec, '0', 'i32') }}} - {{{ makeSetValue('rmtp', C_STRUCTS.timespec.tv_nsec, '0', 'i32') }}} + {{{ makeSetValue('rmtp', C_STRUCTS.timespec.tv_sec, '0', 'i32') }}}; + {{{ makeSetValue('rmtp', C_STRUCTS.timespec.tv_nsec, '0', 'i32') }}}; } return _usleep((seconds * 1e6) + (nanoseconds / 1000)); }, @@ -6166,7 +6205,7 @@ LibraryManager.library = { } else { nsec = _emscripten_get_now_res(); } - {{{ makeSetValue('res', C_STRUCTS.timespec.tv_sec, '1', 'i32') }}} + {{{ makeSetValue('res', C_STRUCTS.timespec.tv_sec, '1', 'i32') }}}; {{{ makeSetValue('res', C_STRUCTS.timespec.tv_nsec, 'nsec', 'i32') }}} // resolution is milliseconds return 0; }, @@ -6250,9 +6289,6 @@ LibraryManager.library = { label = label|0; table = table|0; var i = 0; -#if ASSERTIONS - if ((label|0) == 0) abort(121); -#endif setjmpId = (setjmpId+1)|0; {{{ makeSetValueAsm('env', '0', 'setjmpId', 'i32') }}}; while ((i|0) < {{{ 2*MAX_SETJMPS }}}) { @@ -6310,6 +6346,10 @@ LibraryManager.library = { throw { longjmp: true, id: {{{ makeGetValue('env', '0', 'i32') }}}, value: value || 1 }; #endif }, + emscripten_longjmp__deps: ['longjmp'], + emscripten_longjmp: function(env, value) { + _longjmp(env, value); + }, // ========================================================================== // signal.h @@ -6362,6 +6402,13 @@ LibraryManager.library = { siginterrupt: function() { throw 'siginterrupt not implemented' }, + raise__deps: ['$ERRNO_CODES', '__setErrNo'], + raise: function(sig) { + // TODO: + ___setErrNo(ERRNO_CODES.ENOSYS); + return -1; + }, + // ========================================================================== // sys/wait.h // ========================================================================== @@ -6408,7 +6455,19 @@ LibraryManager.library = { // var indexes = Runtime.calculateStructAlignment({ fields: ['i32', 'i32'] }); var me = _localeconv; if (!me.ret) { - me.ret = allocate([allocate(intArrayFromString('.'), 'i8', ALLOC_NORMAL)], 'i8*', ALLOC_NORMAL); // just decimal point, for now + // These are defaults from the "C" locale + me.ret = allocate([ + allocate(intArrayFromString('.'), 'i8', ALLOC_NORMAL),0,0,0, // decimal_point + allocate(intArrayFromString(''), 'i8', ALLOC_NORMAL),0,0,0, // thousands_sep + allocate(intArrayFromString(''), 'i8', ALLOC_NORMAL),0,0,0, // grouping + allocate(intArrayFromString(''), 'i8', ALLOC_NORMAL),0,0,0, // int_curr_symbol + allocate(intArrayFromString(''), 'i8', ALLOC_NORMAL),0,0,0, // currency_symbol + allocate(intArrayFromString(''), 'i8', ALLOC_NORMAL),0,0,0, // mon_decimal_point + allocate(intArrayFromString(''), 'i8', ALLOC_NORMAL),0,0,0, // mon_thousands_sep + allocate(intArrayFromString(''), 'i8', ALLOC_NORMAL),0,0,0, // mon_grouping + allocate(intArrayFromString(''), 'i8', ALLOC_NORMAL),0,0,0, // positive_sign + allocate(intArrayFromString(''), 'i8', ALLOC_NORMAL),0,0,0 // negative_sign + ], 'i8*', ALLOC_NORMAL); // Allocate strings in lconv, still don't allocate chars } return me.ret; }, @@ -6419,6 +6478,7 @@ LibraryManager.library = { // langinfo.h // ========================================================================== + nl_langinfo__deps: ['malloc'], nl_langinfo: function(item) { // char *nl_langinfo(nl_item item); // http://pubs.opengroup.org/onlinepubs/000095399/functions/nl_langinfo.html @@ -6847,7 +6907,7 @@ LibraryManager.library = { __setErrNo__postset: '___errno_state = Runtime.staticAlloc(4); {{{ makeSetValue("___errno_state", 0, 0, "i32") }}};', __setErrNo: function(value) { // For convenient setting and returning of errno. - {{{ makeSetValue('___errno_state', '0', 'value', 'i32') }}} + {{{ makeSetValue('___errno_state', '0', 'value', 'i32') }}}; return value; }, __errno_location__deps: ['__setErrNo'], @@ -6871,15 +6931,14 @@ LibraryManager.library = { // int setrlimit(int resource, const struct rlimit *rlp) return 0; }, - __01getrlimit64_: 'getrlimit', // TODO: Implement for real. We just do time used, and no useful data getrusage: function(resource, rlp) { // int getrusage(int resource, struct rusage *rlp); - {{{ makeSetValue('rlp', C_STRUCTS.rusage.ru_utime.tv_sec, '1', 'i32') }}} - {{{ makeSetValue('rlp', C_STRUCTS.rusage.ru_utime.tv_usec, '2', 'i32') }}} - {{{ makeSetValue('rlp', C_STRUCTS.rusage.ru_stime.tv_sec, '3', 'i32') }}} - {{{ makeSetValue('rlp', C_STRUCTS.rusage.ru_stime.tv_usec, '4', 'i32') }}} + {{{ makeSetValue('rlp', C_STRUCTS.rusage.ru_utime.tv_sec, '1', 'i32') }}}; + {{{ makeSetValue('rlp', C_STRUCTS.rusage.ru_utime.tv_usec, '2', 'i32') }}}; + {{{ makeSetValue('rlp', C_STRUCTS.rusage.ru_stime.tv_sec, '3', 'i32') }}}; + {{{ makeSetValue('rlp', C_STRUCTS.rusage.ru_stime.tv_usec, '4', 'i32') }}}; return 0; }, @@ -6943,8 +7002,8 @@ LibraryManager.library = { void **restrict stackaddr, size_t *restrict stacksize); */ /*FIXME: assumes that there is only one thread, and that attr is the current thread*/ - {{{ makeSetValue('stackaddr', '0', 'STACK_BASE', 'i8*') }}} - {{{ makeSetValue('stacksize', '0', 'TOTAL_STACK', 'i32') }}} + {{{ makeSetValue('stackaddr', '0', 'STACK_BASE', 'i8*') }}}; + {{{ makeSetValue('stacksize', '0', 'TOTAL_STACK', 'i32') }}}; return 0; }, @@ -6962,7 +7021,7 @@ LibraryManager.library = { if (key == 0) { return ERRNO_CODES.EINVAL; } - {{{ makeSetValue('key', '0', 'PTHREAD_SPECIFIC_NEXT_KEY', 'i32*') }}} + {{{ makeSetValue('key', '0', 'PTHREAD_SPECIFIC_NEXT_KEY', 'i32*') }}}; // values start at 0 PTHREAD_SPECIFIC[PTHREAD_SPECIFIC_NEXT_KEY] = 0; PTHREAD_SPECIFIC_NEXT_KEY++; @@ -7020,7 +7079,7 @@ LibraryManager.library = { posix_memalign__deps: ['memalign'], posix_memalign: function(memptr, alignment, size) { var ptr = _memalign(alignment, size); - {{{ makeSetValue('memptr', '0', 'ptr', 'i8*') }}} + {{{ makeSetValue('memptr', '0', 'ptr', 'i8*') }}}; return 0; }, @@ -7046,7 +7105,7 @@ LibraryManager.library = { } return addr; }, - inet_ntoa__deps: ['_inet_ntop4_raw'], + inet_ntoa__deps: ['_inet_ntop4_raw', 'malloc'], inet_ntoa: function(in_addr) { if (!_inet_ntoa.buffer) { _inet_ntoa.buffer = _malloc(1024); @@ -7062,7 +7121,7 @@ LibraryManager.library = { if (addr === null) { return 0; } - {{{ makeSetValue('inp', '0', 'addr', 'i32') }}} + {{{ makeSetValue('inp', '0', 'addr', 'i32') }}}; return 1; }, @@ -7221,7 +7280,7 @@ LibraryManager.library = { if (ret === null) { return 0; } - {{{ makeSetValue('dst', '0', 'ret', 'i32') }}} + {{{ makeSetValue('dst', '0', 'ret', 'i32') }}}; return 1; }, _inet_pton6_raw__deps: ['htons'], @@ -7325,25 +7384,21 @@ LibraryManager.library = { // netinet/in.h // ========================================================================== - _in6addr_any: + in6addr_any: 'allocate([0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], "i8", ALLOC_STATIC)', - _in6addr_loopback: + in6addr_loopback: 'allocate([0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1], "i8", ALLOC_STATIC)', - _in6addr_linklocal_allnodes: - 'allocate([255,2,0,0,0,0,0,0,0,0,0,0,0,0,0,1], "i8", ALLOC_STATIC)', - _in6addr_linklocal_allrouters: - 'allocate([255,2,0,0,0,0,0,0,0,0,0,0,0,0,0,2], "i8", ALLOC_STATIC)', - _in6addr_interfacelocal_allnodes: - 'allocate([255,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1], "i8", ALLOC_STATIC)', - _in6addr_interfacelocal_allrouters: - 'allocate([255,1,0,0,0,0,0,0,0,0,0,0,0,0,0,2], "i8", ALLOC_STATIC)', - _in6addr_sitelocal_allrouters: - 'allocate([255,5,0,0,0,0,0,0,0,0,0,0,0,0,0,2], "i8", ALLOC_STATIC)', // ========================================================================== // netdb.h // ========================================================================== + __h_errno_state: 'allocate(1, "i32", ALLOC_STATIC)', + __h_errno_location__deps: ['__h_errno_state'], + __h_errno_location: function() { + return ___h_errno_state; + }, + // We can't actually resolve hostnames in the browser, so instead // we're generating fake IP addresses with lookup_name that we can // resolve later on with lookup_addr. @@ -7399,6 +7454,7 @@ LibraryManager.library = { gethostbyaddr: function (addr, addrlen, type) { if (type !== {{{ cDefine('AF_INET') }}}) { ___setErrNo(ERRNO_CODES.EAFNOSUPPORT); + // TODO: set h_errno return null; } addr = {{{ makeGetValue('addr', '0', 'i32') }}}; // addr is in_addr @@ -7411,7 +7467,7 @@ LibraryManager.library = { return _gethostbyname(hostp); }, - gethostbyname__deps: ['$DNS', '_inet_pton4_raw'], + gethostbyname__deps: ['$DNS', '_inet_pton4_raw', 'malloc'], gethostbyname: function(name) { name = Pointer_stringify(name); @@ -7419,18 +7475,18 @@ LibraryManager.library = { var ret = _malloc({{{ C_STRUCTS.hostent.__size__ }}}); // XXX possibly leaked, as are others here var nameBuf = _malloc(name.length+1); writeStringToMemory(name, nameBuf); - {{{ makeSetValue('ret', C_STRUCTS.hostent.h_name, 'nameBuf', 'i8*') }}} + {{{ makeSetValue('ret', C_STRUCTS.hostent.h_name, 'nameBuf', 'i8*') }}}; var aliasesBuf = _malloc(4); - {{{ makeSetValue('aliasesBuf', '0', '0', 'i8*') }}} - {{{ makeSetValue('ret', C_STRUCTS.hostent.h_aliases, 'aliasesBuf', 'i8**') }}} + {{{ makeSetValue('aliasesBuf', '0', '0', 'i8*') }}}; + {{{ makeSetValue('ret', C_STRUCTS.hostent.h_aliases, 'aliasesBuf', 'i8**') }}}; var afinet = {{{ cDefine('AF_INET') }}}; - {{{ makeSetValue('ret', C_STRUCTS.hostent.h_addrtype, 'afinet', 'i32') }}} - {{{ makeSetValue('ret', C_STRUCTS.hostent.h_length, '4', 'i32') }}} + {{{ makeSetValue('ret', C_STRUCTS.hostent.h_addrtype, 'afinet', 'i32') }}}; + {{{ makeSetValue('ret', C_STRUCTS.hostent.h_length, '4', 'i32') }}}; var addrListBuf = _malloc(12); - {{{ makeSetValue('addrListBuf', '0', 'addrListBuf+8', 'i32*') }}} - {{{ makeSetValue('addrListBuf', '4', '0', 'i32*') }}} - {{{ makeSetValue('addrListBuf', '8', '__inet_pton4_raw(DNS.lookup_name(name))', 'i32') }}} - {{{ makeSetValue('ret', C_STRUCTS.hostent.h_addr_list, 'addrListBuf', 'i8**') }}} + {{{ makeSetValue('addrListBuf', '0', 'addrListBuf+8', 'i32*') }}}; + {{{ makeSetValue('addrListBuf', '4', '0', 'i32*') }}}; + {{{ makeSetValue('addrListBuf', '8', '__inet_pton4_raw(DNS.lookup_name(name))', 'i32') }}}; + {{{ makeSetValue('ret', C_STRUCTS.hostent.h_addr_list, 'addrListBuf', 'i8**') }}}; return ret; }, @@ -7444,7 +7500,7 @@ LibraryManager.library = { return 0; }, - getaddrinfo__deps: ['$Sockets', '$DNS', '_inet_pton4_raw', '_inet_ntop4_raw', '_inet_pton6_raw', '_inet_ntop6_raw', '_write_sockaddr', 'htonl'], + getaddrinfo__deps: ['$Sockets', '$DNS', '_inet_pton4_raw', '_inet_ntop4_raw', '_inet_pton6_raw', '_inet_ntop6_raw', '_write_sockaddr', 'htonl', 'malloc'], getaddrinfo: function(node, service, hint, out) { // Note getaddrinfo currently only returns a single addrinfo with ai_next defaulting to NULL. When NULL // hints are specified or ai_family set to AF_UNSPEC or ai_socktype or ai_protocol set to 0 then we @@ -7486,6 +7542,7 @@ LibraryManager.library = { } else { {{{ makeSetValue('ai', C_STRUCTS.addrinfo.ai_addrlen, C_STRUCTS.sockaddr_in.__size__, 'i32') }}}; } + {{{ makeSetValue('ai', C_STRUCTS.addrinfo.ai_next, '0', 'i32') }}}; return ai; } @@ -7606,7 +7663,7 @@ LibraryManager.library = { node = DNS.lookup_name(node); addr = __inet_pton4_raw(node); if (family === {{{ cDefine('AF_UNSPEC') }}}) { - family = {{{ cDefine('AF_INET') }}} + family = {{{ cDefine('AF_INET') }}}; } else if (family === {{{ cDefine('AF_INET6') }}}) { addr = [0, 0, _htonl(0xffff), addr]; } @@ -7660,7 +7717,7 @@ LibraryManager.library = { // are actually negative numbers and you can't have expressions as keys in JavaScript literals. $GAI_ERRNO_MESSAGES: {}, - gai_strerror__deps: ['$GAI_ERRNO_MESSAGES'], + gai_strerror__deps: ['$GAI_ERRNO_MESSAGES', 'malloc'], gai_strerror: function(val) { var buflen = 256; @@ -7702,7 +7759,7 @@ LibraryManager.library = { list: [], map: {} }, - setprotoent__deps: ['$Protocols'], + setprotoent__deps: ['$Protocols', 'malloc'], setprotoent: function(stayopen) { // void setprotoent(int stayopen); @@ -8367,7 +8424,7 @@ LibraryManager.library = { } }, - getsockname__deps: ['$FS', '$SOCKFS', '$DNS', '$ERRNO_CODES', '__setErrNo', '_write_sockaddr', '_inet_pton_raw'], + getsockname__deps: ['$FS', '$SOCKFS', '$DNS', '$ERRNO_CODES', '__setErrNo', '_write_sockaddr'], getsockname: function (fd, addr, addrlen) { var sock = SOCKFS.getSocket(fd); if (!sock) { @@ -8385,7 +8442,7 @@ LibraryManager.library = { } }, - getpeername__deps: ['$FS', '$SOCKFS', '$DNS', '$ERRNO_CODES', '__setErrNo', '_write_sockaddr', '_inet_pton_raw'], + getpeername__deps: ['$FS', '$SOCKFS', '$DNS', '$ERRNO_CODES', '__setErrNo', '_write_sockaddr'], getpeername: function (fd, addr, addrlen) { var sock = SOCKFS.getSocket(fd); if (!sock) { @@ -8535,7 +8592,7 @@ LibraryManager.library = { } }, - recvmsg__deps: ['$FS', '$SOCKFS', '$DNS', '$ERRNO_CODES', '__setErrNo', '_inet_pton_raw', '_write_sockaddr'], + recvmsg__deps: ['$FS', '$SOCKFS', '$DNS', '$ERRNO_CODES', '__setErrNo', '_write_sockaddr'], recvmsg: function(fd, message, flags) { var sock = SOCKFS.getSocket(fd); if (!sock) { @@ -8616,6 +8673,8 @@ LibraryManager.library = { return 0; }, + mkport: function() { throw 'TODO' }, + // ========================================================================== // select.h // ========================================================================== @@ -8867,10 +8926,19 @@ LibraryManager.library = { emscripten_get_callstack_js: function(flags) { var err = new Error(); if (!err.stack) { - Runtime.warnOnce('emscripten_get_callstack_js is not supported on this browser!'); - return ''; + // IE10+ special cases: It does have callstack info, but it is only populated if an Error object is thrown, + // so try that as a special-case. + try { + throw new Error(0); + } catch(e) { + err = e; + } + if (!err.stack) { + Runtime.warnOnce('emscripten_get_callstack_js is not supported on this browser!'); + return ''; + } } - var callstack = new Error().stack.toString(); + var callstack = err.stack.toString(); // Find the symbols in the callstack that corresponds to the functions that report callstack information, and remove everyhing up to these from the output. var iThisFunc = callstack.lastIndexOf('_emscripten_log'); @@ -8896,7 +8964,8 @@ LibraryManager.library = { // Process all lines: lines = callstack.split('\n'); callstack = ''; - var firefoxRe = new RegExp('\\s*(.*?)@(.*):(.*)'); // Extract components of form ' Object._main@http://server.com:4324' + var newFirefoxRe = new RegExp('\\s*(.*?)@(.*?):([0-9]+):([0-9]+)'); // New FF30 with column info: extract components of form ' Object._main@http://server.com:4324:12' + var firefoxRe = new RegExp('\\s*(.*?)@(.*):(.*)(:(.*))?'); // Old FF without column info: extract components of form ' Object._main@http://server.com:4324' var chromeRe = new RegExp('\\s*at (.*?) \\\((.*):(.*):(.*)\\\)'); // Extract components of form ' at Object._main (http://server.com/file.html:4324:12)' for(l in lines) { @@ -8914,12 +8983,13 @@ LibraryManager.library = { lineno = parts[3]; column = parts[4]; } else { - parts = firefoxRe.exec(line); - if (parts && parts.length == 4) { + parts = newFirefoxRe.exec(line); + if (!parts) parts = firefoxRe.exec(line); + if (parts && parts.length >= 4) { jsSymbolName = parts[1]; file = parts[2]; lineno = parts[3]; - column = 0; // Firefox doesn't carry column information. See https://bugzilla.mozilla.org/show_bug.cgi?id=762556 + column = parts[4]|0; // Old Firefox doesn't carry column information, but in new FF30, it is present. See https://bugzilla.mozilla.org/show_bug.cgi?id=762556 } else { // Was not able to extract this line for demangling/sourcemapping purposes. Output it as-is. callstack += line + '\n'; @@ -8975,7 +9045,7 @@ LibraryManager.library = { } // Truncate output to avoid writing past bounds. if (callstack.length > maxbytes-1) { - callstack.slice(0, maxbytes-1); + callstack = callstack.slice(0, maxbytes-1); } // Output callstack string as C string to HEAP. writeStringToMemory(callstack, str, false); @@ -9021,6 +9091,53 @@ LibraryManager.library = { _emscripten_log_js(flags, str); }, + emscripten_get_compiler_setting: function(name) { + name = Pointer_stringify(name); + + var ret = Runtime.getCompilerSetting(name); + if (typeof ret === 'number') return ret; + + if (!_emscripten_get_compiler_setting.cache) _emscripten_get_compiler_setting.cache = {}; + var cache = _emscripten_get_compiler_setting.cache; + var fullname = name + '__str'; + var fullret = cache[fullname]; + if (fullret) return fullret; + return cache[fullname] = allocate(intArrayFromString(ret + ''), 'i8', ALLOC_NORMAL); + }, + +#if ASM_JS +#if ALLOW_MEMORY_GROWTH + emscripten_replace_memory__asm: true, // this is used inside the asm module + emscripten_replace_memory__sig: 'viiiiiiii', // bogus + emscripten_replace_memory: function(_HEAP8, _HEAP16, _HEAP32, _HEAPU8, _HEAPU16, _HEAPU32, _HEAPF32, _HEAPF64) { + _HEAP8 = _HEAP8; // fake asm coercions + _HEAP16 = _HEAP16; + _HEAP32 = _HEAP32; + _HEAPU8 = _HEAPU8; + _HEAPU16 = _HEAPU16; + _HEAPU32 = _HEAPU32; + _HEAPF32 = _HEAPF32; + _HEAPF64 = _HEAPF64; + HEAP8 = _HEAP8; // replace the memory views + HEAP16 = _HEAP16; + HEAP32 = _HEAP32; + HEAPU8 = _HEAPU8; + HEAPU16 = _HEAPU16; + HEAPU32 = _HEAPU32; + HEAPF32 = _HEAPF32; + HEAPF64 = _HEAPF64; + }, + // this function is inside the asm block, but prevents validation as asm.js + // the codebase still benefits from being in the general asm.js shape, + // but should not declare itself as validating (which is prevented in ASM_JS == 2). + {{{ (assert(ASM_JS === 2), DEFAULT_LIBRARY_FUNCS_TO_INCLUDE.push('emscripten_replace_memory'), '') }}} +#endif +#endif + + emscripten_debugger: function() { + debugger; + }, + //============================ // emscripten vector ops //============================ @@ -9180,6 +9297,30 @@ LibraryManager.library = { // misc shims for musl __lockfile: function() { return 1 }, __unlockfile: function(){}, + + // misc definitions to avoid unnecessary unresolved symbols from fastcomp + emscripten_prep_setjmp: true, + emscripten_check_longjmp: true, + emscripten_get_longjmp_result: true, + emscripten_setjmp: true, + emscripten_preinvoke: true, + emscripten_postinvoke: true, + emscripten_resume: true, + emscripten_landingpad: true, + getHigh32: true, + setHigh32: true, + FtoILow: true, + FtoIHigh: true, + DtoILow: true, + DtoIHigh: true, + BDtoILow: true, + BDtoIHigh: true, + SItoF: true, + UItoF: true, + SItoD: true, + UItoD: true, + BItoD: true, + llvm_dbg_value: true, }; function autoAddDeps(object, name) { @@ -9192,7 +9333,7 @@ function autoAddDeps(object, name) { } // Add aborting stubs for various libc stuff needed by libc++ -['pthread_cond_signal', 'pthread_equal', 'pthread_join', 'pthread_detach', 'catgets', 'catopen', 'catclose'].forEach(function(aborter) { +['pthread_cond_signal', 'pthread_equal', 'pthread_join', 'pthread_detach'].forEach(function(aborter) { LibraryManager.library[aborter] = function aborting_stub() { throw 'TODO: ' + aborter }; }); diff --git a/src/library_browser.js b/src/library_browser.js index d5e35339..0808b9f0 100644 --- a/src/library_browser.js +++ b/src/library_browser.js @@ -1,7 +1,6 @@ //"use strict"; // Utilities for browser environments - mergeInto(LibraryManager.library, { $Browser__deps: ['$PATH'], $Browser__postset: 'Module["requestFullScreen"] = function Module_requestFullScreen(lockPointer, resizeCanvas) { Browser.requestFullScreen(lockPointer, resizeCanvas) };\n' + // exports @@ -13,6 +12,7 @@ mergeInto(LibraryManager.library, { $Browser: { mainLoop: { scheduler: null, + method: '', shouldPause: false, paused: false, queue: [], @@ -196,24 +196,33 @@ mergeInto(LibraryManager.library, { // Canvas event setup var canvas = Module['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['webkitRequestPointerLock'] || + canvas['msRequestPointerLock'] || + function(){}; canvas.exitPointerLock = document['exitPointerLock'] || document['mozExitPointerLock'] || document['webkitExitPointerLock'] || + document['msExitPointerLock'] || function(){}; // no-op if function does not exist canvas.exitPointerLock = canvas.exitPointerLock.bind(document); function pointerLockChange() { Browser.pointerLock = document['pointerLockElement'] === canvas || document['mozPointerLockElement'] === canvas || - document['webkitPointerLockElement'] === canvas; + document['webkitPointerLockElement'] === canvas || + document['msPointerLockElement'] === canvas; } 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", function(ev) { @@ -233,6 +242,10 @@ mergeInto(LibraryManager.library, { } #endif var ctx; + var errorInfo = '?'; + function onContextCreationError(event) { + errorInfo = event.statusMessage || errorInfo; + } try { if (useWebGL) { var contextAttributes = { @@ -250,10 +263,6 @@ mergeInto(LibraryManager.library, { contextAttributes.preserveDrawingBuffer = true; #endif - var errorInfo = '?'; - function onContextCreationError(event) { - errorInfo = event.statusMessage || errorInfo; - } canvas.addEventListener('webglcontextcreationerror', onContextCreationError, false); try { ['experimental-webgl', 'webgl'].some(function(webglId) { @@ -341,20 +350,32 @@ mergeInto(LibraryManager.library, { var canvas = Module['canvas']; function fullScreenChange() { Browser.isFullScreen = false; + var canvasContainer = canvas.parentNode; if ((document['webkitFullScreenElement'] || document['webkitFullscreenElement'] || document['mozFullScreenElement'] || document['mozFullscreenElement'] || - document['fullScreenElement'] || document['fullscreenElement']) === canvas) { + document['fullScreenElement'] || document['fullscreenElement'] || + document['msFullScreenElement'] || document['msFullscreenElement'] || + document['webkitCurrentFullScreenElement']) === canvasContainer) { canvas.cancelFullScreen = document['cancelFullScreen'] || document['mozCancelFullScreen'] || - document['webkitCancelFullScreen']; + document['webkitCancelFullScreen'] || + document['msExitFullscreen'] || + document['exitFullscreen'] || + function() {}; canvas.cancelFullScreen = canvas.cancelFullScreen.bind(document); if (Browser.lockPointer) canvas.requestPointerLock(); Browser.isFullScreen = true; if (Browser.resizeCanvas) Browser.setFullScreenCanvasSize(); - } else if (Browser.resizeCanvas){ - Browser.setWindowedCanvasSize(); + } 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(); } if (Module['onFullScreen']) Module['onFullScreen'](Browser.isFullScreen); + Browser.updateCanvasDimensions(canvas); } if (!Browser.fullScreenHandlersInstalled) { @@ -362,12 +383,20 @@ mergeInto(LibraryManager.library, { document.addEventListener('fullscreenchange', fullScreenChange, false); document.addEventListener('mozfullscreenchange', fullScreenChange, false); document.addEventListener('webkitfullscreenchange', fullScreenChange, false); + document.addEventListener('MSFullscreenChange', fullScreenChange, false); } - canvas.requestFullScreen = canvas['requestFullScreen'] || - canvas['mozRequestFullScreen'] || - (canvas['webkitRequestFullScreen'] ? function() { canvas['webkitRequestFullScreen'](Element['ALLOW_KEYBOARD_INPUT']) } : null); - canvas.requestFullScreen(); + // 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'] ? function() { canvasContainer['webkitRequestFullScreen'](Element['ALLOW_KEYBOARD_INPUT']) } : null); + canvasContainer.requestFullScreen(); }, requestAnimationFrame: function requestAnimationFrame(func) { @@ -445,6 +474,10 @@ mergeInto(LibraryManager.library, { 0; }, + getMouseWheelDelta: function(event) { + return Math.max(-1, Math.min(1, event.type === 'DOMMouseScroll' ? event.detail : -event.wheelDelta)); + }, + mouseX: 0, mouseY: 0, mouseMovementX: 0, @@ -560,19 +593,13 @@ mergeInto(LibraryManager.library, { setCanvasSize: function(width, height, noUpdates) { var canvas = Module['canvas']; - canvas.width = width; - canvas.height = height; + Browser.updateCanvasDimensions(canvas, width, height); if (!noUpdates) Browser.updateResizeListeners(); }, windowedWidth: 0, windowedHeight: 0, setFullScreenCanvasSize: function() { - var canvas = Module['canvas']; - this.windowedWidth = canvas.width; - this.windowedHeight = canvas.height; - canvas.width = screen.width; - canvas.height = screen.height; // check if SDL is available if (typeof SDL != "undefined") { var flags = {{{ makeGetValue('SDL.screen+Runtime.QUANTUM_SIZE*0', '0', 'i32', 0, 1) }}}; @@ -583,9 +610,6 @@ mergeInto(LibraryManager.library, { }, setWindowedCanvasSize: function() { - var canvas = Module['canvas']; - canvas.width = this.windowedWidth; - canvas.height = this.windowedHeight; // check if SDL is available if (typeof SDL != "undefined") { var flags = {{{ makeGetValue('SDL.screen+Runtime.QUANTUM_SIZE*0', '0', 'i32', 0, 1) }}}; @@ -593,8 +617,55 @@ mergeInto(LibraryManager.library, { {{{ makeSetValue('SDL.screen+Runtime.QUANTUM_SIZE*0', '0', 'flags', 'i32') }}} } Browser.updateResizeListeners(); - } + }, + updateCanvasDimensions : function(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['webkitFullScreenElement'] || document['webkitFullscreenElement'] || + document['mozFullScreenElement'] || document['mozFullscreenElement'] || + document['fullScreenElement'] || document['fullscreenElement'] || + document['msFullScreenElement'] || document['msFullscreenElement'] || + 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"); + } + } + } + } }, emscripten_async_wget: function(url, file, onload, onerror) { @@ -652,8 +723,59 @@ mergeInto(LibraryManager.library, { // PROGRESS http.onprogress = function http_onprogress(e) { - var percentComplete = (e.position / e.totalSize)*100; - if (onprogress) Runtime.dynCall('vii', onprogress, [arg, percentComplete]); + if (e.lengthComputable || (e.lengthComputable === undefined && e.totalSize != 0)) { + var percentComplete = (e.position / e.totalSize)*100; + if (onprogress) Runtime.dynCall('vii', onprogress, [arg, percentComplete]); + } + }; + + // Useful because the browser can limit the number of redirection + try { + if (http.channel instanceof Ci.nsIHttpChannel) + http.channel.redirectionLimit = 0; + } catch (ex) { /* whatever */ } + + if (_request == "POST") { + //Send the proper header information along with the request + http.setRequestHeader("Content-type", "application/x-www-form-urlencoded"); + http.setRequestHeader("Content-length", _param.length); + http.setRequestHeader("Connection", "close"); + http.send(_param); + } else { + http.send(null); + } + }, + + emscripten_async_wget2_data: function(url, request, param, arg, free, onload, onerror, onprogress) { + var _url = Pointer_stringify(url); + var _request = Pointer_stringify(request); + var _param = Pointer_stringify(param); + + var http = new XMLHttpRequest(); + http.open(_request, _url, true); + http.responseType = 'arraybuffer'; + + // LOAD + http.onload = function http_onload(e) { + if (http.status == 200 || _url.substr(0,4).toLowerCase() != "http") { + var byteArray = new Uint8Array(http.response); + var buffer = _malloc(byteArray.length); + HEAPU8.set(byteArray, buffer); + if (onload) Runtime.dynCall('viii', onload, [arg, buffer, byteArray.length]); + if (free) _free(buffer); + } else { + if (onerror) Runtime.dynCall('viii', onerror, [arg, http.status, http.statusText]); + } + }; + + // ERROR + http.onerror = function http_onerror(e) { + if (onerror) Runtime.dynCall('viii', onerror, [arg, http.status, http.statusText]); + }; + + // PROGRESS + http.onprogress = function http_onprogress(e) { + if (onprogress) Runtime.dynCall('viii', onprogress, [arg, e.loaded, e.lengthComputable || e.lengthComputable === undefined ? e.total : 0]); }; // Useful because the browser can limit the number of redirection @@ -674,6 +796,8 @@ mergeInto(LibraryManager.library, { }, emscripten_async_prepare: function(file, onload, onerror) { + Module['noExitRuntime'] = true; + var _file = Pointer_stringify(file); var data = FS.analyzePath(_file); if (!data.exists) return -1; @@ -693,6 +817,8 @@ mergeInto(LibraryManager.library, { }, emscripten_async_prepare_data: function(data, size, suffix, arg, onload, onerror) { + Module['noExitRuntime'] = true; + var _suffix = Pointer_stringify(suffix); if (!Browser.asyncPrepareDataCounter) Browser.asyncPrepareDataCounter = 0; var name = 'prepare_data_' + (Browser.asyncPrepareDataCounter++) + '.' + _suffix; @@ -743,7 +869,7 @@ mergeInto(LibraryManager.library, { document.body.appendChild(script); }, - emscripten_set_main_loop: function(func, fps, simulateInfiniteLoop) { + emscripten_set_main_loop: function(func, fps, simulateInfiniteLoop, arg) { Module['noExitRuntime'] = true; Browser.mainLoop.runner = function Browser_mainLoop_runner() { @@ -775,12 +901,30 @@ mergeInto(LibraryManager.library, { return; } + // 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. +#if FULL_ES2 + GL.newRenderingFrameStarted(); +#endif +#if LEGACY_GL_EMULATION + GL.newRenderingFrameStarted(); +#endif + + if (Browser.mainLoop.method === 'timeout' && Module.ctx) { + Module.printErr('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 + } + if (Module['preMainLoop']) { Module['preMainLoop'](); } try { - Runtime.dynCall('v', func); + if (typeof arg !== 'undefined') { + Runtime.dynCall('vi', func, [arg]); + } else { + Runtime.dynCall('v', func); + } } catch (e) { if (e instanceof ExitStatus) { return; @@ -805,11 +949,13 @@ mergeInto(LibraryManager.library, { if (fps && fps > 0) { Browser.mainLoop.scheduler = function Browser_mainLoop_scheduler() { setTimeout(Browser.mainLoop.runner, 1000/fps); // doing this each time means that on exception, we stop - } + }; + Browser.mainLoop.method = 'timeout'; } else { Browser.mainLoop.scheduler = function Browser_mainLoop_scheduler() { Browser.requestAnimationFrame(Browser.mainLoop.runner); - } + }; + Browser.mainLoop.method = 'rAF'; } Browser.mainLoop.scheduler(); @@ -818,6 +964,11 @@ mergeInto(LibraryManager.library, { } }, + emscripten_set_main_loop_arg__deps: ['emscripten_set_main_loop'], + emscripten_set_main_loop_arg: function(func, arg, fps, simulateInfiniteLoop) { + _emscripten_set_main_loop(func, fps, simulateInfiniteLoop, arg); + }, + emscripten_cancel_main_loop: function() { Browser.mainLoop.scheduler = null; Browser.mainLoop.shouldPause = true; @@ -909,8 +1060,11 @@ mergeInto(LibraryManager.library, { var callbackId = msg.data['callbackId']; var callbackInfo = info.callbacks[callbackId]; if (!callbackInfo) return; // no callback or callback removed meanwhile - info.awaited--; - info.callbacks[callbackId] = null; // TODO: reuse callbackIds, compress this + // Don't trash our callback state if we expect additional calls. + if (msg.data['finalResponse']) { + info.awaited--; + info.callbacks[callbackId] = null; // TODO: reuse callbackIds, compress this + } var data = msg.data['data']; if (data) { if (!data.byteLength) data = new Uint8Array(data); @@ -937,6 +1091,8 @@ mergeInto(LibraryManager.library, { }, emscripten_call_worker: function(id, funcName, data, size, callback, arg) { + Module['noExitRuntime'] = true; // should we only do this if there is a callback? + funcName = Pointer_stringify(funcName); var info = Browser.workers[id]; var callbackId = -1; @@ -955,12 +1111,23 @@ mergeInto(LibraryManager.library, { }); }, + emscripten_worker_respond_provisionally: function(data, size) { + if (!inWorkerCall) throw 'not in worker call!'; + if (workerResponded) throw 'already responded with final response!'; + postMessage({ + 'callbackId': workerCallbackId, + 'finalResponse': false, + 'data': data ? new Uint8Array({{{ makeHEAPView('U8', 'data', 'data + size') }}}) : 0 // XXX copy to a new typed array as a workaround for chrome bug 169705 + }); + }, + emscripten_worker_respond: function(data, size) { if (!inWorkerCall) throw 'not in worker call!'; - if (workerResponded) throw 'already responded!'; + if (workerResponded) throw 'already responded with final response!'; workerResponded = true; postMessage({ 'callbackId': workerCallbackId, + 'finalResponse': true, 'data': data ? new Uint8Array({{{ makeHEAPView('U8', 'data', 'data + size') }}}) : 0 // XXX copy to a new typed array as a workaround for chrome bug 169705 }); }, diff --git a/src/library_egl.js b/src/library_egl.js index 11cf8951..46ec939e 100644 --- a/src/library_egl.js +++ b/src/library_egl.js @@ -492,7 +492,11 @@ var LibraryEGL = { EGL.setErrorCode(0x3000 /* EGL_SUCCESS */); return 1; }, - + + + // EGLAPI EGLBoolean EGLAPIENTRY eglWaitGL(void); + eglWaitGL: 'eglWaitClient', + // EGLAPI EGLBoolean EGLAPIENTRY eglSwapInterval(EGLDisplay dpy, EGLint interval); eglSwapInterval: function(display, interval) { if (display != 62000 /* Magic ID for Emscripten 'default display' */) { @@ -550,12 +554,26 @@ var LibraryEGL = { // EGLAPI EGLBoolean EGLAPIENTRY eglSwapBuffers(EGLDisplay dpy, EGLSurface surface); eglSwapBuffers: function() { - EGL.setErrorCode(0x3000 /* EGL_SUCCESS */); + if (!EGL.defaultDisplayInitialized) { + EGL.setErrorCode(0x3001 /* EGL_NOT_INITIALIZED */); + } else if (!Module.ctx) { + EGL.setErrorCode(0x3002 /* EGL_BAD_ACCESS */); + } else if (Module.ctx.isContextLost()) { + EGL.setErrorCode(0x300E /* EGL_CONTEXT_LOST */); + } else { + // According to documentation this does an implicit flush. + // Due to discussion at https://github.com/kripken/emscripten/pull/1871 + // the flush was removed since this _may_ result in slowing code down. + //_glFlush(); + EGL.setErrorCode(0x3000 /* EGL_SUCCESS */); + return 1; // EGL_TRUE + } + return 0; // EGL_FALSE }, eglGetProcAddress__deps: ['emscripten_GetProcAddress'], eglGetProcAddress: function(name_) { - return _emscripten_GetProcAddress(Pointer_stringify(name_)); + return _emscripten_GetProcAddress(name_); }, }; diff --git a/src/library_fs.js b/src/library_fs.js index e6b060f6..3d0036ee 100644 --- a/src/library_fs.js +++ b/src/library_fs.js @@ -16,7 +16,7 @@ mergeInto(LibraryManager.library, { root: null, mounts: [], devices: [null], - streams: [null], + streams: [], nextInode: 1, nameTable: null, currentPath: '/', @@ -40,7 +40,17 @@ mergeInto(LibraryManager.library, { // lookupPath: function(path, opts) { path = PATH.resolve(FS.cwd(), path); - opts = opts || { recurse_count: 0 }; + opts = opts || {}; + + var defaults = { + follow_mount: true, + recurse_count: 0 + }; + for (var key in defaults) { + if (opts[key] === undefined) { + opts[key] = defaults[key]; + } + } if (opts.recurse_count > 8) { // max recursive lookup of 8 throw new FS.ErrnoError(ERRNO_CODES.ELOOP); @@ -67,10 +77,11 @@ mergeInto(LibraryManager.library, { // jump to the mount's root node if this is a mountpoint if (FS.isMountpoint(current)) { - current = current.mount.root; + if (!islast || (islast && opts.follow_mount)) { + current = current.mounted.root; + } } - // follow symlinks // by default, lookupPath will not follow a symlink if it is the final path component. // setting opts.follow = true will override this behavior. if (!islast || opts.follow) { @@ -163,28 +174,26 @@ mergeInto(LibraryManager.library, { createNode: function(parent, name, mode, rdev) { if (!FS.FSNode) { FS.FSNode = function(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.parent = null; - this.mount = null; - if (!parent) { - parent = this; // root node sets parent to itself - } - this.parent = parent; - this.mount = parent.mount; - FS.hashAddNode(this); }; + FS.FSNode.prototype = {}; + // compatibility var readMode = {{{ cDefine('S_IRUGO') }}} | {{{ cDefine('S_IXUGO') }}}; var writeMode = {{{ cDefine('S_IWUGO') }}}; - FS.FSNode.prototype = {}; - // NOTE we must use Object.defineProperties instead of individual calls to // Object.defineProperty in order to make closure compiler happy Object.defineProperties(FS.FSNode.prototype, { @@ -204,7 +213,12 @@ mergeInto(LibraryManager.library, { }, }); } - return new FS.FSNode(parent, name, mode, rdev); + + var node = new FS.FSNode(parent, name, mode, rdev); + + FS.hashAddNode(node); + + return node; }, destroyNode: function(node) { FS.hashRemoveNode(node); @@ -213,7 +227,7 @@ mergeInto(LibraryManager.library, { return node === node.parent; }, isMountpoint: function(node) { - return node.mounted; + return !!node.mounted; }, isFile: function(mode) { return (mode & {{{ cDefine('S_IFMT') }}}) === {{{ cDefine('S_IFREG') }}}; @@ -344,7 +358,7 @@ mergeInto(LibraryManager.library, { // MAX_OPEN_FDS: 4096, nextfd: function(fd_start, fd_end) { - fd_start = fd_start || 1; + fd_start = fd_start || 0; fd_end = fd_end || FS.MAX_OPEN_FDS; for (var fd = fd_start; fd <= fd_end; fd++) { if (!FS.streams[fd]) { @@ -400,6 +414,22 @@ mergeInto(LibraryManager.library, { }, // + // file pointers + // + // instead of maintaining a separate mapping from FILE* to file descriptors, + // we employ a simple trick: the pointer to a stream is its fd plus 1. This + // means that all valid streams have a valid non-zero pointer while allowing + // the fs for stdin to be the standard value of zero. + // + // + getStreamFromPtr: function(ptr) { + return FS.streams[ptr - 1]; + }, + getPtrForStream: function(stream) { + return stream ? stream.fd + 1 : 0; + }, + + // // devices // // each character device consists of a device id + stream operations. @@ -441,61 +471,131 @@ mergeInto(LibraryManager.library, { // // core // + getMounts: function(mount) { + var mounts = []; + var check = [mount]; + + while (check.length) { + var m = check.pop(); + + mounts.push(m); + + check.push.apply(check, m.mounts); + } + + return mounts; + }, syncfs: function(populate, callback) { if (typeof(populate) === 'function') { callback = populate; populate = false; } + var mounts = FS.getMounts(FS.root.mount); var completed = 0; - var total = FS.mounts.length; + function done(err) { if (err) { - return callback(err); + if (!done.errored) { + done.errored = true; + return callback(err); + } + return; } - if (++completed >= total) { + if (++completed >= mounts.length) { callback(null); } }; // sync all mounts - for (var i = 0; i < FS.mounts.length; i++) { - var mount = FS.mounts[i]; + mounts.forEach(function (mount) { if (!mount.type.syncfs) { - done(null); - continue; + return done(null); } mount.type.syncfs(mount, populate, done); - } + }); }, mount: function(type, opts, mountpoint) { - var lookup; - if (mountpoint) { - lookup = FS.lookupPath(mountpoint, { follow: false }); + var root = mountpoint === '/'; + var pseudo = !mountpoint; + var node; + + if (root && FS.root) { + throw new FS.ErrnoError(ERRNO_CODES.EBUSY); + } else if (!root && !pseudo) { + var lookup = FS.lookupPath(mountpoint, { follow_mount: false }); + mountpoint = lookup.path; // use the absolute path + node = lookup.node; + + if (FS.isMountpoint(node)) { + throw new FS.ErrnoError(ERRNO_CODES.EBUSY); + } + + if (!FS.isDir(node.mode)) { + throw new FS.ErrnoError(ERRNO_CODES.ENOTDIR); + } } + var mount = { type: type, opts: opts, mountpoint: mountpoint, - root: null + mounts: [] }; + // create a root node for the fs - var root = type.mount(mount); - root.mount = mount; - mount.root = root; - // assign the mount info to the mountpoint's node - if (lookup) { - lookup.node.mount = mount; - lookup.node.mounted = true; - // compatibility update FS.root if we mount to / - if (mountpoint === '/') { - FS.root = mount.root; + var mountRoot = type.mount(mount); + mountRoot.mount = mount; + mount.root = mountRoot; + + if (root) { + FS.root = mountRoot; + } else if (node) { + // set as a mountpoint + node.mounted = mount; + + // add the new mount to the current mount's children + if (node.mount) { + node.mount.mounts.push(mount); } } - // add to our cached list of mounts - FS.mounts.push(mount); - return root; + + return mountRoot; + }, + unmount: function (mountpoint) { + var lookup = FS.lookupPath(mountpoint, { follow_mount: false }); + + if (!FS.isMountpoint(lookup.node)) { + throw new FS.ErrnoError(ERRNO_CODES.EINVAL); + } + + // destroy the nodes for this mount, and all its child mounts + var node = lookup.node; + var mount = node.mounted; + var mounts = FS.getMounts(mount); + + Object.keys(FS.nameTable).forEach(function (hash) { + var current = FS.nameTable[hash]; + + while (current) { + var next = current.name_next; + + if (mounts.indexOf(current.mount) !== -1) { + FS.destroyNode(current); + } + + current = next; + } + }); + + // no longer a mountpoint + node.mounted = null; + + // remove this mount from the child mounts + var idx = node.mount.mounts.indexOf(mount); + assert(idx !== -1); + node.mount.mounts.splice(idx, 1); }, lookup: function(parent, name) { return parent.node_ops.lookup(parent, name); @@ -516,13 +616,13 @@ mergeInto(LibraryManager.library, { }, // helpers to create specific types of nodes create: function(path, mode) { - mode = mode !== undefined ? mode : 0666; + mode = mode !== undefined ? mode : 438 /* 0666 */; mode &= {{{ cDefine('S_IALLUGO') }}}; mode |= {{{ cDefine('S_IFREG') }}}; return FS.mknod(path, mode, 0); }, mkdir: function(path, mode) { - mode = mode !== undefined ? mode : 0777; + mode = mode !== undefined ? mode : 511 /* 0777 */; mode &= {{{ cDefine('S_IRWXUGO') }}} | {{{ cDefine('S_ISVTX') }}}; mode |= {{{ cDefine('S_IFDIR') }}}; return FS.mknod(path, mode, 0); @@ -530,7 +630,7 @@ mergeInto(LibraryManager.library, { mkdev: function(path, mode, dev) { if (typeof(dev) === 'undefined') { dev = mode; - mode = 0666; + mode = 438 /* 0666 */; } mode |= {{{ cDefine('S_IFCHR') }}}; return FS.mknod(path, mode, dev); @@ -677,7 +777,7 @@ mergeInto(LibraryManager.library, { FS.destroyNode(node); }, readlink: function(path) { - var lookup = FS.lookupPath(path, { follow: false }); + var lookup = FS.lookupPath(path); var link = lookup.node; if (!link.node_ops.readlink) { throw new FS.ErrnoError(ERRNO_CODES.EINVAL); @@ -795,7 +895,7 @@ mergeInto(LibraryManager.library, { }, open: function(path, flags, mode, fd_start, fd_end) { flags = typeof flags === 'string' ? FS.modeStringToFlags(flags) : flags; - mode = typeof mode === 'undefined' ? 0666 : mode; + mode = typeof mode === 'undefined' ? 438 /* 0666 */ : mode; if ((flags & {{{ cDefine('O_CREAT') }}})) { mode = (mode & {{{ cDefine('S_IALLUGO') }}}) | {{{ cDefine('S_IFREG') }}}; } else { @@ -975,6 +1075,9 @@ mergeInto(LibraryManager.library, { opts = opts || {}; opts.flags = opts.flags || 'r'; opts.encoding = opts.encoding || 'binary'; + if (opts.encoding !== 'utf8' && opts.encoding !== 'binary') { + throw new Error('Invalid encoding type "' + opts.encoding + '"'); + } var ret; var stream = FS.open(path, opts.flags); var stat = FS.stat(path); @@ -989,8 +1092,6 @@ mergeInto(LibraryManager.library, { } } else if (opts.encoding === 'binary') { ret = buf; - } else { - throw new Error('Invalid encoding type "' + opts.encoding + '"'); } FS.close(stream); return ret; @@ -999,15 +1100,16 @@ mergeInto(LibraryManager.library, { opts = opts || {}; opts.flags = opts.flags || 'w'; opts.encoding = opts.encoding || 'utf8'; + if (opts.encoding !== 'utf8' && opts.encoding !== 'binary') { + throw new Error('Invalid encoding type "' + opts.encoding + '"'); + } var stream = FS.open(path, opts.flags, opts.mode); if (opts.encoding === 'utf8') { var utf8 = new Runtime.UTF8Processor(); var buf = new Uint8Array(utf8.processJSString(data)); - FS.write(stream, buf, 0, buf.length, 0); + FS.write(stream, buf, 0, buf.length, 0, opts.canOwn); } else if (opts.encoding === 'binary') { - FS.write(stream, data, 0, data.length, 0); - } else { - throw new Error('Invalid encoding type "' + opts.encoding + '"'); + FS.write(stream, data, 0, data.length, 0, opts.canOwn); } FS.close(stream); }, @@ -1080,16 +1182,16 @@ mergeInto(LibraryManager.library, { // open default streams for the stdin, stdout and stderr devices var stdin = FS.open('/dev/stdin', 'r'); - {{{ makeSetValue(makeGlobalUse('_stdin'), 0, 'stdin.fd', 'void*') }}}; - assert(stdin.fd === 1, 'invalid handle for stdin (' + stdin.fd + ')'); + {{{ makeSetValue(makeGlobalUse('_stdin'), 0, 'FS.getPtrForStream(stdin)', 'void*') }}}; + assert(stdin.fd === 0, 'invalid handle for stdin (' + stdin.fd + ')'); var stdout = FS.open('/dev/stdout', 'w'); - {{{ makeSetValue(makeGlobalUse('_stdout'), 0, 'stdout.fd', 'void*') }}}; - assert(stdout.fd === 2, 'invalid handle for stdout (' + stdout.fd + ')'); + {{{ makeSetValue(makeGlobalUse('_stdout'), 0, 'FS.getPtrForStream(stdout)', 'void*') }}}; + assert(stdout.fd === 1, 'invalid handle for stdout (' + stdout.fd + ')'); var stderr = FS.open('/dev/stderr', 'w'); - {{{ makeSetValue(makeGlobalUse('_stderr'), 0, 'stderr.fd', 'void*') }}}; - assert(stderr.fd === 3, 'invalid handle for stderr (' + stderr.fd + ')'); + {{{ makeSetValue(makeGlobalUse('_stderr'), 0, 'FS.getPtrForStream(stderr)', 'void*') }}}; + assert(stderr.fd === 2, 'invalid handle for stderr (' + stderr.fd + ')'); }, ensureErrnoError: function() { if (FS.ErrnoError) return; @@ -1119,7 +1221,6 @@ mergeInto(LibraryManager.library, { FS.nameTable = new Array(4096); - FS.root = FS.createNode(null, '/', {{{ cDefine('S_IFDIR') }}} | 0777, 0); FS.mount(MEMFS, {}, '/'); FS.createDefaultDirectories(); @@ -1335,82 +1436,81 @@ mergeInto(LibraryManager.library, { // XHR, which is not possible in browsers except in a web worker! Use preloading, // either --preload-file in emcc or FS.createPreloadedFile createLazyFile: function(parent, name, url, canRead, canWrite) { - if (typeof XMLHttpRequest !== 'undefined') { - if (!ENVIRONMENT_IS_WORKER) throw 'Cannot do synchronous binary XHRs outside webworkers in modern browsers. Use --embed-file or --preload-file in emcc'; - // Lazy chunked Uint8Array (implements get and length from Uint8Array). Actual getting is abstracted away for eventual reuse. - function LazyUint8Array() { - this.lengthKnown = false; - this.chunks = []; // Loaded chunks. Index is the chunk number + // Lazy chunked Uint8Array (implements get and length from Uint8Array). Actual getting is abstracted away for eventual reuse. + function LazyUint8Array() { + this.lengthKnown = false; + this.chunks = []; // Loaded chunks. Index is the chunk number + } + LazyUint8Array.prototype.get = function LazyUint8Array_get(idx) { + if (idx > this.length-1 || idx < 0) { + return undefined; } - LazyUint8Array.prototype.get = function LazyUint8Array_get(idx) { - if (idx > this.length-1 || idx < 0) { - return undefined; - } - var chunkOffset = idx % this.chunkSize; - var chunkNum = Math.floor(idx / this.chunkSize); - return this.getter(chunkNum)[chunkOffset]; - } - LazyUint8Array.prototype.setDataGetter = function LazyUint8Array_setDataGetter(getter) { - this.getter = getter; - } - LazyUint8Array.prototype.cacheLength = function LazyUint8Array_cacheLength() { - // Find length - var xhr = new XMLHttpRequest(); - xhr.open('HEAD', url, false); - xhr.send(null); - if (!(xhr.status >= 200 && xhr.status < 300 || xhr.status === 304)) throw new Error("Couldn't load " + url + ". Status: " + xhr.status); - var datalength = Number(xhr.getResponseHeader("Content-length")); - var header; - var hasByteServing = (header = xhr.getResponseHeader("Accept-Ranges")) && header === "bytes"; + var chunkOffset = idx % this.chunkSize; + var chunkNum = Math.floor(idx / this.chunkSize); + return this.getter(chunkNum)[chunkOffset]; + } + LazyUint8Array.prototype.setDataGetter = function LazyUint8Array_setDataGetter(getter) { + this.getter = getter; + } + LazyUint8Array.prototype.cacheLength = function LazyUint8Array_cacheLength() { + // Find length + var xhr = new XMLHttpRequest(); + xhr.open('HEAD', url, false); + xhr.send(null); + if (!(xhr.status >= 200 && xhr.status < 300 || xhr.status === 304)) throw new Error("Couldn't load " + url + ". Status: " + xhr.status); + var datalength = Number(xhr.getResponseHeader("Content-length")); + var header; + var hasByteServing = (header = xhr.getResponseHeader("Accept-Ranges")) && header === "bytes"; #if SMALL_XHR_CHUNKS - var chunkSize = 1024; // Chunk size in bytes + var chunkSize = 1024; // Chunk size in bytes #else - var chunkSize = 1024*1024; // Chunk size in bytes + var chunkSize = 1024*1024; // Chunk size in bytes #endif - if (!hasByteServing) chunkSize = datalength; - - // Function to get a range from the remote URL. - var doXHR = (function(from, to) { - if (from > to) throw new Error("invalid range (" + from + ", " + to + ") or no bytes requested!"); - if (to > datalength-1) throw new Error("only " + datalength + " bytes available! programmer error!"); - - // TODO: Use mozResponseArrayBuffer, responseStream, etc. if available. - var xhr = new XMLHttpRequest(); - xhr.open('GET', url, false); - if (datalength !== chunkSize) xhr.setRequestHeader("Range", "bytes=" + from + "-" + to); - - // Some hints to the browser that we want binary data. - if (typeof Uint8Array != 'undefined') xhr.responseType = 'arraybuffer'; - if (xhr.overrideMimeType) { - xhr.overrideMimeType('text/plain; charset=x-user-defined'); - } - - xhr.send(null); - if (!(xhr.status >= 200 && xhr.status < 300 || xhr.status === 304)) throw new Error("Couldn't load " + url + ". Status: " + xhr.status); - if (xhr.response !== undefined) { - return new Uint8Array(xhr.response || []); - } else { - return intArrayFromString(xhr.responseText || '', true); - } - }); - var lazyArray = this; - lazyArray.setDataGetter(function(chunkNum) { - var start = chunkNum * chunkSize; - var end = (chunkNum+1) * chunkSize - 1; // including this byte - end = Math.min(end, datalength-1); // if datalength-1 is selected, this is the last block - if (typeof(lazyArray.chunks[chunkNum]) === "undefined") { - lazyArray.chunks[chunkNum] = doXHR(start, end); - } - if (typeof(lazyArray.chunks[chunkNum]) === "undefined") throw new Error("doXHR failed!"); - return lazyArray.chunks[chunkNum]; - }); + if (!hasByteServing) chunkSize = datalength; - this._length = datalength; - this._chunkSize = chunkSize; - this.lengthKnown = true; - } + // Function to get a range from the remote URL. + var doXHR = (function(from, to) { + if (from > to) throw new Error("invalid range (" + from + ", " + to + ") or no bytes requested!"); + if (to > datalength-1) throw new Error("only " + datalength + " bytes available! programmer error!"); + + // TODO: Use mozResponseArrayBuffer, responseStream, etc. if available. + var xhr = new XMLHttpRequest(); + xhr.open('GET', url, false); + if (datalength !== chunkSize) xhr.setRequestHeader("Range", "bytes=" + from + "-" + to); + + // Some hints to the browser that we want binary data. + if (typeof Uint8Array != 'undefined') xhr.responseType = 'arraybuffer'; + if (xhr.overrideMimeType) { + xhr.overrideMimeType('text/plain; charset=x-user-defined'); + } + xhr.send(null); + if (!(xhr.status >= 200 && xhr.status < 300 || xhr.status === 304)) throw new Error("Couldn't load " + url + ". Status: " + xhr.status); + if (xhr.response !== undefined) { + return new Uint8Array(xhr.response || []); + } else { + return intArrayFromString(xhr.responseText || '', true); + } + }); + var lazyArray = this; + lazyArray.setDataGetter(function(chunkNum) { + var start = chunkNum * chunkSize; + var end = (chunkNum+1) * chunkSize - 1; // including this byte + end = Math.min(end, datalength-1); // if datalength-1 is selected, this is the last block + if (typeof(lazyArray.chunks[chunkNum]) === "undefined") { + lazyArray.chunks[chunkNum] = doXHR(start, end); + } + if (typeof(lazyArray.chunks[chunkNum]) === "undefined") throw new Error("doXHR failed!"); + return lazyArray.chunks[chunkNum]; + }); + + this._length = datalength; + this._chunkSize = chunkSize; + this.lengthKnown = true; + } + if (typeof XMLHttpRequest !== 'undefined') { + if (!ENVIRONMENT_IS_WORKER) throw 'Cannot do synchronous binary XHRs outside webworkers in modern browsers. Use --embed-file or --preload-file in emcc'; var lazyArray = new LazyUint8Array(); Object.defineProperty(lazyArray, "length", { get: function() { diff --git a/src/library_gc.js b/src/library_gc.js index b3dae0e9..d86f2d15 100644 --- a/src/library_gc.js +++ b/src/library_gc.js @@ -1,4 +1,6 @@ +// WARNING: this is deprecated. You should just build Boehm from source, it is much faster than the toy version written in emscripten + if (GC_SUPPORT) { EXPORTED_FUNCTIONS['_calloc'] = 1; EXPORTED_FUNCTIONS['_realloc'] = 1; diff --git a/src/library_gl.js b/src/library_gl.js index f6978c04..851b01b1 100644 --- a/src/library_gl.js +++ b/src/library_gl.js @@ -19,6 +19,7 @@ var LibraryGL = { textures: [], uniforms: [], shaders: [], + vaos: [], #if FULL_ES2 clientBuffers: [], @@ -57,6 +58,7 @@ var LibraryGL = { unpackAlignment: 4, // default alignment is 4 bytes init: function() { + GL.createLog2ceilLookup(GL.MAX_TEMP_BUFFER_SIZE); Browser.moduleContextCreatedCallbacks.push(GL.initExtensions); }, @@ -81,36 +83,58 @@ var LibraryGL = { miniTempBuffer: null, miniTempBufferViews: [0], // index i has the view of size i+1 - // Large temporary buffers + // When user GL code wants to render from client-side memory, we need to upload the vertex data to a temp VBO + // for rendering. Maintain a set of temp VBOs that are created-on-demand to appropriate sizes, and never destroyed. + // Also, for best performance the VBOs are double-buffered, i.e. every second frame we switch the set of VBOs we + // upload to, so that rendering from the previous frame is not disturbed by uploading from new data to it, which + // could cause a GPU-CPU pipeline stall. + // Note that index buffers are not double-buffered (at the moment) in this manner. MAX_TEMP_BUFFER_SIZE: {{{ GL_MAX_TEMP_BUFFER_SIZE }}}, - tempBufferIndexLookup: null, - tempVertexBuffers: null, - tempIndexBuffers: null, + tempVertexBuffers1: [], + tempVertexBufferCounters1: [], + tempVertexBuffers2: [], + tempVertexBufferCounters2: [], + // Maximum number of temp VBOs of one size to maintain, after that we start reusing old ones, which is safe but can give + // a performance impact. If CPU-GPU stalls are a problem, increasing this might help. + numTempVertexBuffersPerSize: 64, // (const) + tempIndexBuffers: [], tempQuadIndexBuffer: null, - generateTempBuffers: function(quads) { - GL.tempBufferIndexLookup = new Uint8Array(GL.MAX_TEMP_BUFFER_SIZE+1); - GL.tempVertexBuffers = []; - GL.tempIndexBuffers = []; - var last = -1, curr = -1; - var size = 1; - for (var i = 0; i <= GL.MAX_TEMP_BUFFER_SIZE; i++) { - if (i > size) { - size <<= 1; + // Precompute a lookup table for the function ceil(log2(x)), i.e. how many bits are needed to represent x, or, + // if x was rounded up to next pow2, which index is the single '1' bit at? + // Then log2ceilLookup[x] returns ceil(log2(x)). + log2ceilLookup: null, + createLog2ceilLookup: function(maxValue) { + GL.log2ceilLookup = new Uint8Array(maxValue+1); + var log2 = 0; + var pow2 = 1; + GL.log2ceilLookup[0] = 0; + for(var i = 1; i <= maxValue; ++i) { + if (i > pow2) { + pow2 <<= 1; + ++log2; } - if (size != last) { - curr++; - GL.tempVertexBuffers[curr] = GLctx.createBuffer(); - GLctx.bindBuffer(GLctx.ARRAY_BUFFER, GL.tempVertexBuffers[curr]); - GLctx.bufferData(GLctx.ARRAY_BUFFER, size, GLctx.DYNAMIC_DRAW); - GLctx.bindBuffer(GLctx.ARRAY_BUFFER, null); - GL.tempIndexBuffers[curr] = GLctx.createBuffer(); - GLctx.bindBuffer(GLctx.ELEMENT_ARRAY_BUFFER, GL.tempIndexBuffers[curr]); - GLctx.bufferData(GLctx.ELEMENT_ARRAY_BUFFER, size, GLctx.DYNAMIC_DRAW); - GLctx.bindBuffer(GLctx.ELEMENT_ARRAY_BUFFER, null); - last = size; + GL.log2ceilLookup[i] = log2; + } + }, + + generateTempBuffers: function(quads) { + var largestIndex = GL.log2ceilLookup[GL.MAX_TEMP_BUFFER_SIZE]; + GL.tempVertexBufferCounters1.length = GL.tempVertexBufferCounters2.length = largestIndex+1; + GL.tempVertexBuffers1.length = GL.tempVertexBuffers2.length = largestIndex+1; + GL.tempIndexBuffers.length = largestIndex+1; + for(var i = 0; i <= largestIndex; ++i) { + GL.tempIndexBuffers[i] = null; // Created on-demand + GL.tempVertexBufferCounters1[i] = GL.tempVertexBufferCounters2[i] = 0; + var ringbufferLength = GL.numTempVertexBuffersPerSize; + GL.tempVertexBuffers1[i] = []; + GL.tempVertexBuffers2[i] = []; + var ringbuffer1 = GL.tempVertexBuffers1[i]; + var ringbuffer2 = GL.tempVertexBuffers2[i]; + ringbuffer1.length = ringbuffer2.length = ringbufferLength; + for(var j = 0; j < ringbufferLength; ++j) { + ringbuffer1[j] = ringbuffer2[j] = null; // Created on-demand } - GL.tempBufferIndexLookup[i] = curr; } if (quads) { @@ -140,6 +164,54 @@ var LibraryGL = { } }, + getTempVertexBuffer: function getTempVertexBuffer(sizeBytes) { + var idx = GL.log2ceilLookup[sizeBytes]; + var ringbuffer = GL.tempVertexBuffers1[idx]; + var nextFreeBufferIndex = GL.tempVertexBufferCounters1[idx]; + GL.tempVertexBufferCounters1[idx] = (GL.tempVertexBufferCounters1[idx]+1) & (GL.numTempVertexBuffersPerSize-1); + var vbo = ringbuffer[nextFreeBufferIndex]; + if (vbo) { + return vbo; + } + var prevVBO = GLctx.getParameter(GLctx.ARRAY_BUFFER_BINDING); + ringbuffer[nextFreeBufferIndex] = GLctx.createBuffer(); + GLctx.bindBuffer(GLctx.ARRAY_BUFFER, ringbuffer[nextFreeBufferIndex]); + GLctx.bufferData(GLctx.ARRAY_BUFFER, 1 << idx, GLctx.DYNAMIC_DRAW); + GLctx.bindBuffer(GLctx.ARRAY_BUFFER, prevVBO); + return ringbuffer[nextFreeBufferIndex]; + }, + + getTempIndexBuffer: function getTempIndexBuffer(sizeBytes) { + var idx = GL.log2ceilLookup[sizeBytes]; + var ibo = GL.tempIndexBuffers[idx]; + if (ibo) { + return ibo; + } + var prevIBO = GLctx.getParameter(GLctx.ELEMENT_ARRAY_BUFFER_BINDING); + GL.tempIndexBuffers[idx] = GLctx.createBuffer(); + GLctx.bindBuffer(GLctx.ELEMENT_ARRAY_BUFFER, GL.tempIndexBuffers[idx]); + GLctx.bufferData(GLctx.ELEMENT_ARRAY_BUFFER, 1 << idx, GLctx.DYNAMIC_DRAW); + GLctx.bindBuffer(GLctx.ELEMENT_ARRAY_BUFFER, prevIBO); + return GL.tempIndexBuffers[idx]; + }, + + // Called at start of each new WebGL rendering frame. This swaps the doublebuffered temp VB memory pointers, + // so that every second frame utilizes different set of temp buffers. The aim is to keep the set of buffers + // being rendered, and the set of buffers being updated disjoint. + newRenderingFrameStarted: function newRenderingFrameStarted() { + var vb = GL.tempVertexBuffers1; + GL.tempVertexBuffers1 = GL.tempVertexBuffers2; + GL.tempVertexBuffers2 = vb; + vb = GL.tempVertexBufferCounters1; + GL.tempVertexBufferCounters1 = GL.tempVertexBufferCounters2; + GL.tempVertexBufferCounters2 = vb; + var largestIndex = GL.log2ceilLookup[GL.MAX_TEMP_BUFFER_SIZE]; + for(var i = 0; i <= largestIndex; ++i) { + GL.tempVertexBufferCounters1[i] = 0; + } + }, + +#if LEGACY_GL_EMULATION // Find a token in a shader source string findToken: function(source, token) { function isIdentChar(ch) { @@ -168,6 +240,7 @@ var LibraryGL = { } while (true); return false; }, +#endif getSource: function(shader, count, string, length) { var source = ''; @@ -185,6 +258,7 @@ var LibraryGL = { } source += frag; } +#if LEGACY_GL_EMULATION // Let's see if we need to enable the standard derivatives extension type = GLctx.getShaderParameter(GL.shaders[shader], 0x8B4F /* GL_SHADER_TYPE */); if (type == 0x8B30 /* GL_FRAGMENT_SHADER */) { @@ -200,6 +274,7 @@ var LibraryGL = { #endif } } +#endif return source; }, @@ -446,9 +521,6 @@ var LibraryGL = { preDrawHandleClientVertexAttribBindings: function preDrawHandleClientVertexAttribBindings(count) { GL.resetBufferBinding = false; - var used = GL.usedTempBuffers; - used.length = 0; - // TODO: initial pass to detect ranges we need to upload, might not need an upload per attrib for (var i = 0; i < GL.maxVertexAttribs; ++i) { var cb = GL.clientBuffers[i]; @@ -457,15 +529,7 @@ var LibraryGL = { GL.resetBufferBinding = true; var size = GL.calcBufLength(cb.size, cb.type, cb.stride, count); - var index = GL.tempBufferIndexLookup[size]; - var buf; - do { -#if ASSERTIONS - assert(index < GL.tempVertexBuffers.length); -#endif - buf = GL.tempVertexBuffers[index++]; - } while (used.indexOf(buf) >= 0); - used.push(buf); + var buf = GL.getTempVertexBuffer(size); GLctx.bindBuffer(GLctx.ARRAY_BUFFER, buf); GLctx.bufferSubData(GLctx.ARRAY_BUFFER, 0, @@ -567,9 +631,12 @@ var LibraryGL = { GLctx.getExtension('WEBKIT_EXT_texture_filter_anisotropic'); GL.floatExt = GLctx.getExtension('OES_texture_float'); - + // Extension available from Firefox 26 and Google Chrome 30 GL.instancedArraysExt = GLctx.getExtension('ANGLE_instanced_arrays'); + + // Extension available from Firefox 25 and WebKit + GL.vaoExt = Module.ctx.getExtension('OES_vertex_array_object'); // These are the 'safe' feature-enabling extensions that don't add any performance impact related to e.g. debugging, and // should be enabled by default so that client GLES2/GL code will not need to go through extra hoops to get its stuff working. @@ -581,7 +648,8 @@ var LibraryGL = { "OES_element_index_uint", "EXT_texture_filter_anisotropic", "ANGLE_instanced_arrays", "OES_texture_float_linear", "OES_texture_half_float_linear", "WEBGL_compressed_texture_atc", "WEBGL_compressed_texture_pvrtc", "EXT_color_buffer_half_float", "WEBGL_color_buffer_float", - "EXT_frag_depth", "EXT_sRGB", "WEBGL_draw_buffers", "WEBGL_shared_resources" ]; + "EXT_frag_depth", "EXT_sRGB", "WEBGL_draw_buffers", "WEBGL_shared_resources", + "EXT_shader_texture_lod" ]; function shouldEnableAutomatically(extension) { for(var i in automaticallyEnabledExtensions) { @@ -760,7 +828,7 @@ var LibraryGL = { } else { data = null; } - CLctx['compressedTexSubImage2D'](target, level, xoffset, yoffset, width, height, data); + GLctx['compressedTexSubImage2D'](target, level, xoffset, yoffset, width, height, format, data); }, glTexImage2D__sig: 'viiiiiiiii', @@ -820,12 +888,12 @@ var LibraryGL = { glGetTexParameterfv__sig: 'viii', glGetTexParameterfv: function(target, pname, params) { - {{{ makeSetValue('params', '0', 'Module.getTexParameter(target, pname)', 'float') }}}; + {{{ makeSetValue('params', '0', 'GLctx.getTexParameter(target, pname)', 'float') }}}; }, glGetTexParameteriv__sig: 'viii', glGetTexParameteriv: function(target, pname, params) { - {{{ makeSetValue('params', '0', 'Module.getTexParameter(target, pname)', 'i32') }}}; + {{{ makeSetValue('params', '0', 'GLctx.getTexParameter(target, pname)', 'i32') }}}; }, glTexParameterfv__sig: 'viii', @@ -898,7 +966,11 @@ var LibraryGL = { usage = 0x88E8; // GL_DYNAMIC_DRAW break; } - GLctx.bufferData(target, HEAPU8.subarray(data, data+size), usage); + if (!data) { + GLctx.bufferData(target, size, usage); + } else { + GLctx.bufferData(target, HEAPU8.subarray(data, data+size), usage); + } }, glBufferSubData__sig: 'viiii', @@ -1481,9 +1553,7 @@ var LibraryGL = { #endif var log = GLctx.getShaderInfoLog(GL.shaders[shader]); // Work around a bug in Chromium which causes getShaderInfoLog to return null - if (!log) { - log = ""; - } + if (!log) log = '(unknown error)'; log = log.substr(0, maxLength - 1); writeStringToMemory(log, infoLog); if (length) { @@ -1497,7 +1567,10 @@ var LibraryGL = { GL.validateGLObjectID(GL.shaders, shader, 'glGetShaderiv', 'shader'); #endif if (pname == 0x8B84) { // GL_INFO_LOG_LENGTH - {{{ makeSetValue('p', '0', 'GLctx.getShaderInfoLog(GL.shaders[shader]).length + 1', 'i32') }}}; + var log = GLctx.getShaderInfoLog(GL.shaders[shader]); + // Work around a bug in Chromium which causes getShaderInfoLog to return null + if (!log) log = '(unknown error)'; + {{{ makeSetValue('p', '0', 'log.length + 1', 'i32') }}}; } else { {{{ makeSetValue('p', '0', 'GLctx.getShaderParameter(GL.shaders[shader], pname)', 'i32') }}}; } @@ -1729,6 +1802,81 @@ var LibraryGL = { }, #if LEGACY_GL_EMULATION + glGenVertexArrays__deps: ['emulGlGenVertexArrays'], +#endif + glGenVertexArrays__sig: 'vii', + glGenVertexArrays: function (n, arrays) { +#if LEGACY_GL_EMULATION + _emulGlGenVertexArrays(n, arrays); +#else +#if GL_ASSERTIONS + assert(GL.vaoExt, 'Must have OES_vertex_array_object to use vao'); +#endif + + for(var i = 0; i < n; i++) { + var id = GL.getNewId(GL.vaos); + var vao = GL.vaoExt.createVertexArrayOES(); + vao.name = id; + GL.vaos[id] = vao; + {{{ makeSetValue('arrays', 'i*4', 'id', 'i32') }}}; + } +#endif + }, + +#if LEGACY_GL_EMULATION + glDeleteVertexArrays__deps: ['emulGlDeleteVertexArrays'], +#endif + glDeleteVertexArrays__sig: 'vii', + glDeleteVertexArrays: function(n, vaos) { +#if LEGACY_GL_EMULATION + _emulGlDeleteVertexArrays(n, vaos); +#else +#if GL_ASSERTIONS + assert(GL.vaoExt, 'Must have OES_vertex_array_object to use vao'); +#endif + for(var i = 0; i < n; i++) { + var id = {{{ makeGetValue('vaos', 'i*4', 'i32') }}}; + GL.vaoExt.deleteVertexArrayOES(GL.vaos[id]); + GL.vaos[id] = null; + } +#endif + }, + +#if LEGACY_GL_EMULATION + glBindVertexArray__deps: ['emulGlBindVertexArray'], +#endif + glBindVertexArray__sig: 'vi', + glBindVertexArray: function(vao) { +#if LEGACY_GL_EMULATION + _emulGlBindVertexArray(vao); +#else +#if GL_ASSERTIONS + assert(GL.vaoExt, 'Must have OES_vertex_array_object to use vao'); +#endif + + GL.vaoExt.bindVertexArrayOES(GL.vaos[vao]); +#endif + }, + +#if LEGACY_GL_EMULATION + glIsVertexArray__deps: ['emulGlIsVertexArray'], +#endif + glIsVertexArray__sig: 'ii', + glIsVertexArray: function(array) { +#if LEGACY_GL_EMULATION + return _emulGlIsVertexArray(array); +#else +#if GL_ASSERTIONS + assert(GL.vaoExt, 'Must have OES_vertex_array_object to use vao'); +#endif + + var vao = GL.vaos[array]; + if (!vao) return 0; + return GL.vaoExt.isVertexArrayOES(vao); +#endif + }, + +#if LEGACY_GL_EMULATION // GL emulation: provides misc. functionality not present in OpenGL ES 2.0 or WebGL @@ -1786,7 +1934,7 @@ var LibraryGL = { }; var glEnable = _glEnable; - _glEnable = function _glEnable(cap) { + _glEnable = _emscripten_glEnable = function _glEnable(cap) { // Clean up the renderer on any change to the rendering state. The optimization of // skipping renderer setup is aimed at the case of multiple glDraw* right after each other if (GLImmediate.lastRenderer) GLImmediate.lastRenderer.cleanup(); @@ -1811,7 +1959,7 @@ var LibraryGL = { }; var glDisable = _glDisable; - _glDisable = function _glDisable(cap) { + _glDisable = _emscripten_glDisable = function _glDisable(cap) { if (GLImmediate.lastRenderer) GLImmediate.lastRenderer.cleanup(); if (cap == 0x0B60 /* GL_FOG */) { if (GLEmulation.fogEnabled != false) { @@ -1832,7 +1980,7 @@ var LibraryGL = { } glDisable(cap); }; - _glIsEnabled = function _glIsEnabled(cap) { + _glIsEnabled = _emscripten_glIsEnabled = function _glIsEnabled(cap) { if (cap == 0x0B60 /* GL_FOG */) { return GLEmulation.fogEnabled ? 1 : 0; } else if (!(cap in validCapabilities)) { @@ -1842,7 +1990,7 @@ var LibraryGL = { }; var glGetBooleanv = _glGetBooleanv; - _glGetBooleanv = function _glGetBooleanv(pname, p) { + _glGetBooleanv = _emscripten_glGetBooleanv = function _glGetBooleanv(pname, p) { var attrib = GLEmulation.getAttributeFromCapability(pname); if (attrib !== null) { var result = GLImmediate.enabledClientAttributes[attrib]; @@ -1853,7 +2001,7 @@ var LibraryGL = { }; var glGetIntegerv = _glGetIntegerv; - _glGetIntegerv = function _glGetIntegerv(pname, params) { + _glGetIntegerv = _emscripten_glGetIntegerv = function _glGetIntegerv(pname, params) { switch (pname) { case 0x84E2: pname = GLctx.MAX_TEXTURE_IMAGE_UNITS /* fake it */; break; // GL_MAX_TEXTURE_UNITS case 0x8B4A: { // GL_MAX_VERTEX_UNIFORM_COMPONENTS_ARB @@ -1922,7 +2070,7 @@ var LibraryGL = { }; var glGetString = _glGetString; - _glGetString = function _glGetString(name_) { + _glGetString = _emscripten_glGetString = function _glGetString(name_) { if (GL.stringCache[name_]) return GL.stringCache[name_]; switch(name_) { case 0x1F03 /* GL_EXTENSIONS */: // Add various extensions that we can support @@ -1946,7 +2094,7 @@ var LibraryGL = { GL.shaderOriginalSources = {}; #endif var glCreateShader = _glCreateShader; - _glCreateShader = function _glCreateShader(shaderType) { + _glCreateShader = _emscripten_glCreateShader = function _glCreateShader(shaderType) { var id = glCreateShader(shaderType); GL.shaderInfos[id] = { type: shaderType, @@ -1963,7 +2111,7 @@ var LibraryGL = { } var glShaderSource = _glShaderSource; - _glShaderSource = function _glShaderSource(shader, count, string, length) { + _glShaderSource = _emscripten_glShaderSource = function _glShaderSource(shader, count, string, length) { var source = GL.getSource(shader, count, string, length); #if GL_DEBUG console.log("glShaderSource: Input: \n" + source); @@ -2077,7 +2225,7 @@ var LibraryGL = { }; var glCompileShader = _glCompileShader; - _glCompileShader = function _glCompileShader(shader) { + _glCompileShader = _emscripten_glCompileShader = function _glCompileShader(shader) { GLctx.compileShader(GL.shaders[shader]); #if GL_DEBUG if (!GLctx.getShaderParameter(GL.shaders[shader], GLctx.COMPILE_STATUS)) { @@ -2092,14 +2240,14 @@ var LibraryGL = { GL.programShaders = {}; var glAttachShader = _glAttachShader; - _glAttachShader = function _glAttachShader(program, shader) { + _glAttachShader = _emscripten_glAttachShader = function _glAttachShader(program, shader) { if (!GL.programShaders[program]) GL.programShaders[program] = []; GL.programShaders[program].push(shader); glAttachShader(program, shader); }; var glDetachShader = _glDetachShader; - _glDetachShader = function _glDetachShader(program, shader) { + _glDetachShader = _emscripten_glDetachShader = function _glDetachShader(program, shader) { var programShader = GL.programShaders[program]; if (!programShader) { Module.printErr('WARNING: _glDetachShader received invalid program: ' + program); @@ -2111,7 +2259,7 @@ var LibraryGL = { }; var glUseProgram = _glUseProgram; - _glUseProgram = function _glUseProgram(program) { + _glUseProgram = _emscripten_glUseProgram = function _glUseProgram(program) { #if GL_DEBUG if (GL.debug) { Module.printErr('[using program with shaders]'); @@ -2132,7 +2280,7 @@ var LibraryGL = { } var glDeleteProgram = _glDeleteProgram; - _glDeleteProgram = function _glDeleteProgram(program) { + _glDeleteProgram = _emscripten_glDeleteProgram = function _glDeleteProgram(program) { glDeleteProgram(program); if (program == GL.currProgram) { GLImmediate.currentRenderer = null; // This changes the FFP emulation shader program, need to recompute that. @@ -2143,12 +2291,12 @@ var LibraryGL = { // If attribute 0 was not bound, bind it to 0 for WebGL performance reasons. Track if 0 is free for that. var zeroUsedPrograms = {}; var glBindAttribLocation = _glBindAttribLocation; - _glBindAttribLocation = function _glBindAttribLocation(program, index, name) { + _glBindAttribLocation = _emscripten_glBindAttribLocation = function _glBindAttribLocation(program, index, name) { if (index == 0) zeroUsedPrograms[program] = true; glBindAttribLocation(program, index, name); }; var glLinkProgram = _glLinkProgram; - _glLinkProgram = function _glLinkProgram(program) { + _glLinkProgram = _emscripten_glLinkProgram = function _glLinkProgram(program) { if (!(program in zeroUsedPrograms)) { GLctx.bindAttribLocation(GL.programs[program], 0, 'a_position'); } @@ -2156,7 +2304,7 @@ var LibraryGL = { }; var glBindBuffer = _glBindBuffer; - _glBindBuffer = function _glBindBuffer(target, buffer) { + _glBindBuffer = _emscripten_glBindBuffer = function _glBindBuffer(target, buffer) { glBindBuffer(target, buffer); if (target == GLctx.ARRAY_BUFFER) { if (GLEmulation.currentVao) { @@ -2171,7 +2319,7 @@ var LibraryGL = { }; var glGetFloatv = _glGetFloatv; - _glGetFloatv = function _glGetFloatv(pname, params) { + _glGetFloatv = _emscripten_glGetFloatv = function _glGetFloatv(pname, params) { if (pname == 0x0BA6) { // GL_MODELVIEW_MATRIX HEAPF32.set(GLImmediate.matrix[0/*m*/], params >> 2); } else if (pname == 0x0BA7) { // GL_PROJECTION_MATRIX @@ -2194,7 +2342,7 @@ var LibraryGL = { }; var glHint = _glHint; - _glHint = function _glHint(target, mode) { + _glHint = _emscripten_glHint = function _glHint(target, mode) { if (target == 0x84EF) { // GL_TEXTURE_COMPRESSION_HINT return; } @@ -2202,21 +2350,21 @@ var LibraryGL = { }; var glEnableVertexAttribArray = _glEnableVertexAttribArray; - _glEnableVertexAttribArray = function _glEnableVertexAttribArray(index) { + _glEnableVertexAttribArray = _emscripten_glEnableVertexAttribArray = function _glEnableVertexAttribArray(index) { glEnableVertexAttribArray(index); GLEmulation.enabledVertexAttribArrays[index] = 1; if (GLEmulation.currentVao) GLEmulation.currentVao.enabledVertexAttribArrays[index] = 1; }; var glDisableVertexAttribArray = _glDisableVertexAttribArray; - _glDisableVertexAttribArray = function _glDisableVertexAttribArray(index) { + _glDisableVertexAttribArray = _emscripten_glDisableVertexAttribArray = function _glDisableVertexAttribArray(index) { glDisableVertexAttribArray(index); delete GLEmulation.enabledVertexAttribArrays[index]; if (GLEmulation.currentVao) delete GLEmulation.currentVao.enabledVertexAttribArrays[index]; }; var glVertexAttribPointer = _glVertexAttribPointer; - _glVertexAttribPointer = function _glVertexAttribPointer(index, size, type, normalized, stride, pointer) { + _glVertexAttribPointer = _emscripten_glVertexAttribPointer = function _glVertexAttribPointer(index, size, type, normalized, stride, pointer) { glVertexAttribPointer(index, size, type, normalized, stride, pointer); if (GLEmulation.currentVao) { // TODO: avoid object creation here? likely not hot though GLEmulation.currentVao.vertexAttribPointers[index] = [index, size, type, normalized, stride, pointer]; @@ -2248,6 +2396,7 @@ var LibraryGL = { glGetShaderPrecisionFormat__sig: 'v', glGetShaderPrecisionFormat: function() { throw 'glGetShaderPrecisionFormat: TODO' }, + glDeleteObject__deps: ['glDeleteProgram', 'glDeleteShader'], glDeleteObject__sig: 'vi', glDeleteObject: function(id) { if (GL.programs[id]) { @@ -2258,8 +2407,10 @@ var LibraryGL = { Module.printErr('WARNING: deleteObject received invalid id: ' + id); } }, + glDeleteObjectARB: 'glDeleteObject', glGetObjectParameteriv__sig: 'viii', + glGetObjectParameteriv__deps: ['glGetProgramiv', 'glGetShaderiv'], glGetObjectParameteriv: function(id, type, result) { if (GL.programs[id]) { if (type == 0x8B84) { // GL_OBJECT_INFO_LOG_LENGTH_ARB @@ -2280,7 +2431,9 @@ var LibraryGL = { Module.printErr('WARNING: getObjectParameteriv received invalid id: ' + id); } }, + glGetObjectParameterivARB: 'glGetObjectParameteriv', + glGetInfoLog__deps: ['glGetProgramInfoLog', 'glGetShaderInfoLog'], glGetInfoLog__sig: 'viiii', glGetInfoLog: function(id, maxLength, length, infoLog) { if (GL.programs[id]) { @@ -2291,6 +2444,7 @@ var LibraryGL = { Module.printErr('WARNING: getObjectParameteriv received invalid id: ' + id); } }, + glGetInfoLogARB: 'glGetInfoLog', glBindProgram__sig: 'vii', glBindProgram: function(type, id) { @@ -2298,6 +2452,7 @@ var LibraryGL = { assert(id == 0); #endif }, + glBindProgramARB: 'glBindProgram', glGetPointerv: function(name, p) { var attribute; @@ -2321,7 +2476,7 @@ var LibraryGL = { // GL Immediate mode // See comment in GLEmulation.init() -#if FULL_ES2 == 0 +#if !FULL_ES2 $GLImmediate__postset: 'GLImmediate.setupFuncs(); Browser.moduleContextCreatedCallbacks.push(function() { GLImmediate.init() });', #endif $GLImmediate__deps: ['$Browser', '$GL', '$GLEmulation'], @@ -2585,7 +2740,7 @@ var LibraryGL = { return "float"; } - return Abort_NoSupport("Unsupported combiner op: 0x" + op.toString(16)); + return abort_noSupport("Unsupported combiner op: 0x" + op.toString(16)); } function getCurTexUnit() { @@ -2742,14 +2897,6 @@ var LibraryGL = { this.key0 = -1; // The key of this texture unit must be recomputed when rendering the next time. GLImmediate.currentRenderer = null; // The currently used renderer must be re-evaluated at next render. } - this.traverseState = function(keyView) { - if (this.key0 == -1) { - this.recomputeKey(); - } - keyView.next(this.key0); - keyView.next(this.key1); - keyView.next(this.key2); - }; } function CTexUnit() { @@ -2758,26 +2905,55 @@ var LibraryGL = { this.enabled_tex2D = false; this.enabled_tex3D = false; this.enabled_texCube = false; + this.texTypesEnabled = 0; // A bitfield combination of the four flags above, used for fast access to operations. this.traverseState = function CTexUnit_traverseState(keyView) { - var texUnitType = this.getTexType(); - keyView.next(texUnitType); - if (!texUnitType) return; - this.env.traverseState(keyView); + if (this.texTypesEnabled) { + if (this.env.key0 == -1) { + this.env.recomputeKey(); + } + keyView.next(this.texTypesEnabled | (this.env.key0 << 4)); + keyView.next(this.env.key1); + keyView.next(this.env.key2); + } else { + // For correctness, must traverse a zero value, theoretically a subsequent integer key could collide with this value otherwise. + keyView.next(0); + } }; }; // Class impls: CTexUnit.prototype.enabled = function CTexUnit_enabled() { - return this.getTexType() != 0; + return this.texTypesEnabled; } CTexUnit.prototype.genPassLines = function CTexUnit_genPassLines(passOutputVar, passInputVar, texUnitID) { if (!this.enabled()) { return ["vec4 " + passOutputVar + " = " + passInputVar + ";"]; } - - return this.env.genPassLines(passOutputVar, passInputVar, texUnitID); + var lines = this.env.genPassLines(passOutputVar, passInputVar, texUnitID).join('\n'); + + var texLoadLines = ''; + var texLoadRegex = /(texture.*?\(.*?\))/g; + var loadCounter = 0; + var load; + + // As an optimization, merge duplicate identical texture loads to one var. + while(load = texLoadRegex.exec(lines)) { + var texLoadExpr = load[1]; + var secondOccurrence = lines.slice(load.index+1).indexOf(texLoadExpr); + if (secondOccurrence != -1) { // And also has a second occurrence of same load expression.. + // Create new var to store the common load. + var prefix = TEXENVJIT_NAMESPACE_PREFIX + 'env' + texUnitID + "_"; + var texLoadVar = prefix + 'texload' + loadCounter++; + var texLoadLine = 'vec4 ' + texLoadVar + ' = ' + texLoadExpr + ';\n'; + texLoadLines += texLoadLine + '\n'; // Store the generated texture load statements in a temp string to not confuse regex search in progress. + lines = lines.split(texLoadExpr).join(texLoadVar); + // Reset regex search, since we modified the string. + texLoadRegex = /(texture.*\(.*\))/g; + } + } + return [texLoadLines + lines]; } CTexUnit.prototype.getTexType = function CTexUnit_getTexType() { @@ -2898,13 +3074,18 @@ var LibraryGL = { var alphaLines = this.genCombinerLines(false, alphaVar, passInputVar, texUnitID, this.alphaCombiner, this.alphaSrc, this.alphaOp); + + // Generate scale, but avoid generating an identity op that multiplies by one. + var scaledColor = (this.colorScale == 1) ? colorVar : (colorVar + " * " + valToFloatLiteral(this.colorScale)); + var scaledAlpha = (this.alphaScale == 1) ? alphaVar : (alphaVar + " * " + valToFloatLiteral(this.alphaScale)); + var line = [ "vec4 " + passOutputVar, " = ", "vec4(", - colorVar + " * " + valToFloatLiteral(this.colorScale), + scaledColor, ", ", - alphaVar + " * " + valToFloatLiteral(this.alphaScale), + scaledAlpha, ")", ";", ].join(""); @@ -2912,7 +3093,7 @@ var LibraryGL = { } } - return Abort_NoSupport("Unsupported TexEnv mode: 0x" + this.mode.toString(16)); + return abort_noSupport("Unsupported TexEnv mode: 0x" + this.mode.toString(16)); } CTexEnv.prototype.genCombinerLines = function CTexEnv_getCombinerLines(isColor, outputVar, @@ -3084,12 +3265,7 @@ var LibraryGL = { traverseState: function(keyView) { for (var i = 0; i < s_texUnits.length; i++) { - var texUnit = s_texUnits[i]; - var enabled = texUnit.enabled(); - keyView.next(enabled); - if (enabled) { - texUnit.traverseState(keyView); - } + s_texUnits[i].traverseState(keyView); } }, @@ -3113,24 +3289,28 @@ var LibraryGL = { if (!cur.enabled_tex1D) { GLImmediate.currentRenderer = null; // Renderer state changed, and must be recreated or looked up again. cur.enabled_tex1D = true; + cur.texTypesEnabled |= 1; } break; case GL_TEXTURE_2D: if (!cur.enabled_tex2D) { GLImmediate.currentRenderer = null; cur.enabled_tex2D = true; + cur.texTypesEnabled |= 2; } break; case GL_TEXTURE_3D: if (!cur.enabled_tex3D) { GLImmediate.currentRenderer = null; cur.enabled_tex3D = true; + cur.texTypesEnabled |= 4; } break; case GL_TEXTURE_CUBE_MAP: if (!cur.enabled_texCube) { GLImmediate.currentRenderer = null; cur.enabled_texCube = true; + cur.texTypesEnabled |= 8; } break; } @@ -3143,24 +3323,28 @@ var LibraryGL = { if (cur.enabled_tex1D) { GLImmediate.currentRenderer = null; // Renderer state changed, and must be recreated or looked up again. cur.enabled_tex1D = false; + cur.texTypesEnabled &= ~1; } break; case GL_TEXTURE_2D: if (cur.enabled_tex2D) { GLImmediate.currentRenderer = null; cur.enabled_tex2D = false; + cur.texTypesEnabled &= ~2; } break; case GL_TEXTURE_3D: if (cur.enabled_tex3D) { GLImmediate.currentRenderer = null; cur.enabled_tex3D = false; + cur.texTypesEnabled &= ~4; } break; case GL_TEXTURE_CUBE_MAP: if (cur.enabled_texCube) { GLImmediate.currentRenderer = null; cur.enabled_texCube = false; + cur.texTypesEnabled &= ~8; } break; } @@ -3329,6 +3513,107 @@ var LibraryGL = { Module.printErr('WARNING: Unhandled `pname` in call to `glTexEnvfv`.'); } }, + + hook_getTexEnviv: function(target, pname, param) { + if (target != GL_TEXTURE_ENV) + return; + + var env = getCurTexUnit().env; + switch (pname) { + case GL_TEXTURE_ENV_MODE: + {{{ makeSetValue('param', '0', 'env.mode', 'i32') }}}; + return; + + case GL_TEXTURE_ENV_COLOR: + {{{ makeSetValue('param', '0', 'Math.max(Math.min(env.envColor[0]*255, 255, -255))', 'i32') }}}; + {{{ makeSetValue('param', '1', 'Math.max(Math.min(env.envColor[1]*255, 255, -255))', 'i32') }}}; + {{{ makeSetValue('param', '2', 'Math.max(Math.min(env.envColor[2]*255, 255, -255))', 'i32') }}}; + {{{ makeSetValue('param', '3', 'Math.max(Math.min(env.envColor[3]*255, 255, -255))', 'i32') }}}; + return; + + case GL_COMBINE_RGB: + {{{ makeSetValue('param', '0', 'env.colorCombiner', 'i32') }}}; + return; + + case GL_COMBINE_ALPHA: + {{{ makeSetValue('param', '0', 'env.alphaCombiner', 'i32') }}}; + return; + + case GL_SRC0_RGB: + {{{ makeSetValue('param', '0', 'env.colorSrc[0]', 'i32') }}}; + return; + + case GL_SRC1_RGB: + {{{ makeSetValue('param', '0', 'env.colorSrc[1]', 'i32') }}}; + return; + + case GL_SRC2_RGB: + {{{ makeSetValue('param', '0', 'env.colorSrc[2]', 'i32') }}}; + return; + + case GL_SRC0_ALPHA: + {{{ makeSetValue('param', '0', 'env.alphaSrc[0]', 'i32') }}}; + return; + + case GL_SRC1_ALPHA: + {{{ makeSetValue('param', '0', 'env.alphaSrc[1]', 'i32') }}}; + return; + + case GL_SRC2_ALPHA: + {{{ makeSetValue('param', '0', 'env.alphaSrc[2]', 'i32') }}}; + return; + + case GL_OPERAND0_RGB: + {{{ makeSetValue('param', '0', 'env.colorOp[0]', 'i32') }}}; + return; + + case GL_OPERAND1_RGB: + {{{ makeSetValue('param', '0', 'env.colorOp[1]', 'i32') }}}; + return; + + case GL_OPERAND2_RGB: + {{{ makeSetValue('param', '0', 'env.colorOp[2]', 'i32') }}}; + return; + + case GL_OPERAND0_ALPHA: + {{{ makeSetValue('param', '0', 'env.alphaOp[0]', 'i32') }}}; + return; + + case GL_OPERAND1_ALPHA: + {{{ makeSetValue('param', '0', 'env.alphaOp[1]', 'i32') }}}; + return; + + case GL_OPERAND2_ALPHA: + {{{ makeSetValue('param', '0', 'env.alphaOp[2]', 'i32') }}}; + return; + + case GL_RGB_SCALE: + {{{ makeSetValue('param', '0', 'env.colorScale', 'i32') }}}; + return; + + case GL_ALPHA_SCALE: + {{{ makeSetValue('param', '0', 'env.alphaScale', 'i32') }}}; + return; + + default: + Module.printErr('WARNING: Unhandled `pname` in call to `glGetTexEnvi`.'); + } + }, + + hook_getTexEnvfv: function(target, pname, param) { + if (target != GL_TEXTURE_ENV) + return; + + var env = getCurTexUnit().env; + switch (pname) { + case GL_TEXTURE_ENV_COLOR: + {{{ makeSetValue('param', '0', 'env.envColor[0]', 'float') }}}; + {{{ makeSetValue('param', '4', 'env.envColor[1]', 'float') }}}; + {{{ makeSetValue('param', '8', 'env.envColor[2]', 'float') }}}; + {{{ makeSetValue('param', '12', 'env.envColor[3]', 'float') }}}; + return; + } + } }; }, @@ -3413,6 +3698,11 @@ var LibraryGL = { GLImmediate.enabledClientAttributes[name] = true; GLImmediate.setClientAttribute(name, size, type, 0, GLImmediate.rendererComponentPointer); GLImmediate.rendererComponentPointer += size * GL.byteSizeByType[type - GL.byteSizeByTypeRoot]; +#if GL_FFP_ONLY + // We can enable the correct attribute stream index immediately here, since the same attribute in each shader + // will be bound to this same index. + GL.enableVertexAttribArray(name); +#endif } else { GLImmediate.rendererComponents[name]++; } @@ -3434,7 +3724,6 @@ var LibraryGL = { // we maintain a cache of renderers, optimized to not generate garbage var attributes = GLImmediate.liveClientAttributes; var cacheMap = GLImmediate.rendererCache; - var temp; var keyView = cacheMap.getStaticKeyView().reset(); // By attrib state: @@ -3442,7 +3731,6 @@ var LibraryGL = { for (var i = 0; i < attributes.length; i++) { enabledAttributesKey |= 1 << attributes[i].name; } - keyView.next(enabledAttributesKey); // By fog state: var fogParam = 0; @@ -3459,13 +3747,17 @@ var LibraryGL = { break; } } - keyView.next(fogParam); + keyView.next((enabledAttributesKey << 2) | fogParam); +#if !GL_FFP_ONLY // By cur program: keyView.next(GL.currProgram); if (!GL.currProgram) { +#endif GLImmediate.TexEnvJIT.traverseState(keyView); +#if !GL_FFP_ONLY } +#endif // If we don't already have it, create it. var renderer = keyView.get(); @@ -3643,7 +3935,8 @@ var LibraryGL = { GLctx.bindAttribLocation(this.program, GLImmediate.VERTEX, 'a_position'); GLctx.bindAttribLocation(this.program, GLImmediate.COLOR, 'a_color'); GLctx.bindAttribLocation(this.program, GLImmediate.NORMAL, 'a_normal'); - for (var i = 0; i < GLImmediate.MAX_TEXTURES; i++) { + var maxVertexAttribs = GLctx.getParameter(GLctx.MAX_VERTEX_ATTRIBS); + for (var i = 0; i < GLImmediate.MAX_TEXTURES && GLImmediate.TEXTURE0 + i < maxVertexAttribs; i++) { GLctx.bindAttribLocation(this.program, GLImmediate.TEXTURE0 + i, 'a_texCoord'+i); GLctx.bindAttribLocation(this.program, GLImmediate.TEXTURE0 + i, aTexCoordPrefix+i); } @@ -3670,7 +3963,7 @@ var LibraryGL = { this.texCoordLocations[i] = GLctx.getAttribLocation(this.program, aTexCoordPrefix + i); } } - + this.colorLocation = GLctx.getAttribLocation(this.program, 'a_color'); if (!useCurrProgram) { // Temporarily switch to the program so we can set our sampler uniforms early. var prevBoundProg = GLctx.getParameter(GLctx.CURRENT_PROGRAM); @@ -3682,6 +3975,9 @@ var LibraryGL = { GLctx.uniform1i(texSamplerLoc, texUnitID); } } + // The default color attribute value is not the same as the default for all other attribute streams (0,0,0,1) but (1,1,1,1), + // so explicitly set it right at start. + GLctx.vertexAttrib4fv(this.colorLocation, [1,1,1,1]); GLctx.useProgram(prevBoundProg); } @@ -3689,7 +3985,6 @@ var LibraryGL = { for (var i = 0; i < GLImmediate.MAX_TEXTURES; i++) { this.textureMatrixLocations[i] = GLctx.getUniformLocation(this.program, 'u_textureMatrix' + i); } - this.colorLocation = GLctx.getAttribLocation(this.program, 'a_color'); this.normalLocation = GLctx.getAttribLocation(this.program, 'a_normal'); this.modelViewLocation = GLctx.getUniformLocation(this.program, 'u_modelView'); @@ -3720,7 +4015,7 @@ var LibraryGL = { #if ASSERTIONS assert(end <= GL.MAX_TEMP_BUFFER_SIZE, 'too much vertex data'); #endif - arrayBuffer = GL.tempVertexBuffers[GL.tempBufferIndexLookup[end]]; + arrayBuffer = GL.getTempVertexBuffer(end); // TODO: consider using the last buffer we bound, if it was larger. downside is larger buffer, but we might avoid rebinding and preparing } else { arrayBuffer = GL.currArrayBuffer; @@ -3782,11 +4077,9 @@ var LibraryGL = { #if GL_FFP_ONLY if (!GL.currArrayBuffer) { GLctx.vertexAttribPointer(GLImmediate.VERTEX, posAttr.size, posAttr.type, false, GLImmediate.stride, posAttr.offset); - GL.enableVertexAttribArray(GLImmediate.VERTEX); if (this.hasNormal) { var normalAttr = clientAttributes[GLImmediate.NORMAL]; GLctx.vertexAttribPointer(GLImmediate.NORMAL, normalAttr.size, normalAttr.type, true, GLImmediate.stride, normalAttr.offset); - GL.enableVertexAttribArray(GLImmediate.NORMAL); } } #else @@ -3809,11 +4102,9 @@ var LibraryGL = { var texAttr = clientAttributes[attribLoc]; if (texAttr.size) { GLctx.vertexAttribPointer(attribLoc, texAttr.size, texAttr.type, false, GLImmediate.stride, texAttr.offset); - GL.enableVertexAttribArray(attribLoc); } else { // These two might be dangerous, but let's try them. GLctx.vertexAttrib4f(attribLoc, 0, 0, 0, 1); - GL.disableVertexAttribArray(attribLoc); } } #else @@ -3848,21 +4139,18 @@ var LibraryGL = { #if GL_FFP_ONLY if (!GL.currArrayBuffer) { GLctx.vertexAttribPointer(GLImmediate.COLOR, colorAttr.size, colorAttr.type, true, GLImmediate.stride, colorAttr.offset); - GL.enableVertexAttribArray(GLImmediate.COLOR); } #else GLctx.vertexAttribPointer(this.colorLocation, colorAttr.size, colorAttr.type, true, GLImmediate.stride, colorAttr.offset); GLctx.enableVertexAttribArray(this.colorLocation); #endif - } else if (this.hasColor) { -#if GL_FFP_ONLY - GL.disableVertexAttribArray(GLImmediate.COLOR); - GLctx.vertexAttrib4fv(GLImmediate.COLOR, GLImmediate.clientColor); -#else + } +#if !GL_FFP_ONLY + else if (this.hasColor) { GLctx.disableVertexAttribArray(this.colorLocation); GLctx.vertexAttrib4fv(this.colorLocation, GLImmediate.clientColor); -#endif } +#endif if (this.hasFog) { if (this.fogColorLocation) GLctx.uniform4fv(this.fogColorLocation, GLEmulation.fogColor); if (this.fogEndLocation) GLctx.uniform1f(this.fogEndLocation, GLEmulation.fogEnd); @@ -3889,6 +4177,7 @@ var LibraryGL = { } if (!GL.currProgram) { GLctx.useProgram(null); + GLImmediate.fixedFunctionProgram = 0; } if (!GL.currArrayBuffer) { GLctx.bindBuffer(GLctx.ARRAY_BUFFER, null); @@ -3911,7 +4200,7 @@ var LibraryGL = { // Replace some functions with immediate-mode aware versions. If there are no client // attributes enabled, and we use webgl-friendly modes (no GL_QUADS), then no need // for emulation - _glDrawArrays = function _glDrawArrays(mode, first, count) { + _glDrawArrays = _emscripten_glDrawArrays = function _glDrawArrays(mode, first, count) { if (GLImmediate.totalEnabledClientAttributes == 0 && mode <= 6) { GLctx.drawArrays(mode, first, count); return; @@ -3927,7 +4216,7 @@ var LibraryGL = { GLImmediate.mode = -1; }; - _glDrawElements = function _glDrawElements(mode, count, type, indices, start, end) { // start, end are given if we come from glDrawRangeElements + _glDrawElements = _emscripten_glDrawElements = function _glDrawElements(mode, count, type, indices, start, end) { // start, end are given if we come from glDrawRangeElements if (GLImmediate.totalEnabledClientAttributes == 0 && mode <= 6 && GL.currElementArrayBuffer) { GLctx.drawElements(mode, count, type, indices); return; @@ -3967,43 +4256,51 @@ var LibraryGL = { } var glActiveTexture = _glActiveTexture; - _glActiveTexture = function _glActiveTexture(texture) { + _glActiveTexture = _emscripten_glActiveTexture = function _glActiveTexture(texture) { GLImmediate.TexEnvJIT.hook_activeTexture(texture); glActiveTexture(texture); }; var glEnable = _glEnable; - _glEnable = function _glEnable(cap) { + _glEnable = _emscripten_glEnable = function _glEnable(cap) { GLImmediate.TexEnvJIT.hook_enable(cap); glEnable(cap); }; var glDisable = _glDisable; - _glDisable = function _glDisable(cap) { + _glDisable = _emscripten_glDisable = function _glDisable(cap) { GLImmediate.TexEnvJIT.hook_disable(cap); glDisable(cap); }; var glTexEnvf = (typeof(_glTexEnvf) != 'undefined') ? _glTexEnvf : function(){}; - _glTexEnvf = function _glTexEnvf(target, pname, param) { + _glTexEnvf = _emscripten_glTexEnvf = function _glTexEnvf(target, pname, param) { GLImmediate.TexEnvJIT.hook_texEnvf(target, pname, param); // Don't call old func, since we are the implementor. //glTexEnvf(target, pname, param); }; var glTexEnvi = (typeof(_glTexEnvi) != 'undefined') ? _glTexEnvi : function(){}; - _glTexEnvi = function _glTexEnvi(target, pname, param) { + _glTexEnvi = _emscripten_glTexEnvi = function _glTexEnvi(target, pname, param) { GLImmediate.TexEnvJIT.hook_texEnvi(target, pname, param); // Don't call old func, since we are the implementor. //glTexEnvi(target, pname, param); }; var glTexEnvfv = (typeof(_glTexEnvfv) != 'undefined') ? _glTexEnvfv : function(){}; - _glTexEnvfv = function _glTexEnvfv(target, pname, param) { + _glTexEnvfv = _emscripten_glTexEnvfv = function _glTexEnvfv(target, pname, param) { GLImmediate.TexEnvJIT.hook_texEnvfv(target, pname, param); // Don't call old func, since we are the implementor. //glTexEnvfv(target, pname, param); }; + _glGetTexEnviv = function _glGetTexEnviv(target, pname, param) { + GLImmediate.TexEnvJIT.hook_getTexEnviv(target, pname, param); + }; + + _glGetTexEnvfv = function _glGetTexEnvfv(target, pname, param) { + GLImmediate.TexEnvJIT.hook_getTexEnvfv(target, pname, param); + }; + var glGetIntegerv = _glGetIntegerv; - _glGetIntegerv = function _glGetIntegerv(pname, params) { + _glGetIntegerv = _emscripten_glGetIntegerv = function _glGetIntegerv(pname, params) { switch (pname) { case 0x8B8D: { // GL_CURRENT_PROGRAM // Just query directly so we're working with WebGL objects. @@ -4028,11 +4325,12 @@ var LibraryGL = { if (!Module.useWebGL) return; // a 2D canvas may be currently used TODO: make sure we are actually called in that case - GLImmediate.TexEnvJIT.init(GLctx); - // User can override the maximum number of texture units that we emulate. Using fewer texture units increases runtime performance // slightly, so it is advantageous to choose as small value as needed. GLImmediate.MAX_TEXTURES = Module['GL_MAX_TEXTURE_IMAGE_UNITS'] || GLctx.getParameter(GLctx.MAX_TEXTURE_IMAGE_UNITS); + + GLImmediate.TexEnvJIT.init(GLctx, GLImmediate.MAX_TEXTURES); + GLImmediate.NUM_ATTRIBUTES = 3 /*pos+normal+color attributes*/ + GLImmediate.MAX_TEXTURES; GLImmediate.clientAttributes = []; GLEmulation.enabledClientAttribIndices = []; @@ -4221,7 +4519,7 @@ var LibraryGL = { #if ASSERTIONS assert(numProvidedIndexes << 1 <= GL.MAX_TEMP_BUFFER_SIZE, 'too many immediate mode indexes (a)'); #endif - var indexBuffer = GL.tempIndexBuffers[GL.tempBufferIndexLookup[numProvidedIndexes << 1]]; + var indexBuffer = GL.getTempIndexBuffer(numProvidedIndexes << 1); GLctx.bindBuffer(GLctx.ELEMENT_ARRAY_BUFFER, indexBuffer); GLctx.bufferSubData(GLctx.ELEMENT_ARRAY_BUFFER, 0, {{{ makeHEAPView('U16', 'ptr', 'ptr + (numProvidedIndexes << 1)') }}}); ptr = 0; @@ -4257,7 +4555,7 @@ var LibraryGL = { GLctx.bindBuffer(GLctx.ELEMENT_ARRAY_BUFFER, GL.buffers[GL.currElementArrayBuffer] || null); } -#if GL_UNSAFE_OPTS == 0 +#if !GL_UNSAFE_OPTS #if !GL_FFP_ONLY renderer.cleanup(); #endif @@ -4370,6 +4668,9 @@ var LibraryGL = { GLImmediate.clientColor[1] = g; GLImmediate.clientColor[2] = b; GLImmediate.clientColor[3] = a; +#if GL_FFP_ONLY + GLctx.vertexAttrib4fv(GLImmediate.COLOR, GLImmediate.clientColor); +#endif } }, glColor4d: 'glColor4f', @@ -4498,6 +4799,7 @@ var LibraryGL = { // Additional non-GLES rendering calls + glDrawRangeElements__deps: ['glDrawElements'], glDrawRangeElements__sig: 'viiiiii', glDrawRangeElements: function(mode, start, end, count, type, indices) { _glDrawElements(mode, count, type, indices, start, end); @@ -4517,6 +4819,10 @@ var LibraryGL = { GLImmediate.enabledClientAttributes[attrib] = true; GLImmediate.totalEnabledClientAttributes++; GLImmediate.currentRenderer = null; // Will need to change current renderer, since the set of active vertex pointers changed. +#if GL_FFP_ONLY + // In GL_FFP_ONLY mode, attributes are bound to the same index in each FFP emulation shader, so we can immediately apply the change here. + GL.enableVertexAttribArray(attrib); +#endif if (GLEmulation.currentVao) GLEmulation.currentVao.enabledClientStates[cap] = 1; GLImmediate.modifiedClientAttributes = true; } @@ -4533,6 +4839,10 @@ var LibraryGL = { GLImmediate.enabledClientAttributes[attrib] = false; GLImmediate.totalEnabledClientAttributes--; GLImmediate.currentRenderer = null; // Will need to change current renderer, since the set of active vertex pointers changed. +#if GL_FFP_ONLY + // In GL_FFP_ONLY mode, attributes are bound to the same index in each FFP emulation shader, so we can immediately apply the change here. + GL.disableVertexAttribArray(attrib); +#endif if (GLEmulation.currentVao) delete GLEmulation.currentVao.enabledClientStates[cap]; GLImmediate.modifiedClientAttributes = true; } @@ -4544,7 +4854,6 @@ var LibraryGL = { #if GL_FFP_ONLY if (GL.currArrayBuffer) { GLctx.vertexAttribPointer(GLImmediate.VERTEX, size, type, false, stride, pointer); - GL.enableVertexAttribArray(GLImmediate.VERTEX); } #endif }, @@ -4554,7 +4863,6 @@ var LibraryGL = { if (GL.currArrayBuffer) { var loc = GLImmediate.TEXTURE0 + GLImmediate.clientActiveTexture; GLctx.vertexAttribPointer(loc, size, type, false, stride, pointer); - GL.enableVertexAttribArray(loc); } #endif }, @@ -4563,7 +4871,6 @@ var LibraryGL = { #if GL_FFP_ONLY if (GL.currArrayBuffer) { GLctx.vertexAttribPointer(GLImmediate.NORMAL, size, type, true, stride, pointer); - GL.enableVertexAttribArray(GLImmediate.NORMAL); } #endif }, @@ -4572,7 +4879,6 @@ var LibraryGL = { #if GL_FFP_ONLY if (GL.currArrayBuffer) { GLctx.vertexAttribPointer(GLImmediate.COLOR, size, type, true, stride, pointer); - GL.enableVertexAttribArray(GLImmediate.COLOR); } #endif }, @@ -4583,9 +4889,9 @@ var LibraryGL = { }, // Vertex array object (VAO) support. TODO: when the WebGL extension is popular, use that and remove this code and GL.vaos - glGenVertexArrays__deps: ['$GLEmulation'], - glGenVertexArrays__sig: 'vii', - glGenVertexArrays: function(n, vaos) { + emulGlGenVertexArrays__deps: ['$GLEmulation'], + emulGlGenVertexArrays__sig: 'vii', + emulGlGenVertexArrays: function(n, vaos) { for (var i = 0; i < n; i++) { var id = GL.getNewId(GLEmulation.vaos); GLEmulation.vaos[id] = { @@ -4599,16 +4905,23 @@ var LibraryGL = { {{{ makeSetValue('vaos', 'i*4', 'id', 'i32') }}}; } }, - glDeleteVertexArrays__sig: 'vii', - glDeleteVertexArrays: function(n, vaos) { + emulGlDeleteVertexArrays__sig: 'vii', + emulGlDeleteVertexArrays: function(n, vaos) { for (var i = 0; i < n; i++) { var id = {{{ makeGetValue('vaos', 'i*4', 'i32') }}}; GLEmulation.vaos[id] = null; if (GLEmulation.currentVao && GLEmulation.currentVao.id == id) GLEmulation.currentVao = null; } }, - glBindVertexArray__sig: 'vi', - glBindVertexArray: function(vao) { + emulGlIsVertexArray__sig: 'vi', + emulGlIsVertexArray: function(array) { + var vao = GLEmulation.vaos[array]; + if (!vao) return 0; + return 1; + }, + emulGlBindVertexArray__deps: ['glBindBuffer', 'glEnableVertexAttribArray', 'glVertexAttribPointer', 'glEnableClientState'], + emulGlBindVertexArray__sig: 'vi', + emulGlBindVertexArray: function(vao) { // undo vao-related things, wipe the slate clean, both for vao of 0 or an actual vao GLEmulation.currentVao = null; // make sure the commands we run here are not recorded if (GLImmediate.lastRenderer) GLImmediate.lastRenderer.cleanup(); @@ -4786,6 +5099,9 @@ var LibraryGL = { glTexEnvf: function() { Runtime.warnOnce('glTexEnvf: TODO') }, glTexEnvfv: function() { Runtime.warnOnce('glTexEnvfv: TODO') }, + glGetTexEnviv: function(target, pname, param) { throw 'GL emulation not initialized!'; }, + glGetTexEnvfv: function(target, pname, param) { throw 'GL emulation not initialized!'; }, + glTexImage1D: function() { throw 'glTexImage1D: TODO' }, glTexCoord3f: function() { throw 'glTexCoord3f: TODO' }, glGetTexLevelParameteriv: function() { throw 'glGetTexLevelParameteriv: TODO' }, @@ -4804,49 +5120,23 @@ var LibraryGL = { glCheckFramebufferStatusOES : 'glCheckFramebufferStatus', glDeleteFramebuffersOES : 'glDeleteFramebuffers', glDeleteRenderbuffersOES : 'glDeleteRenderbuffers', - glGenVertexArraysOES: 'glGenVertexArrays', - glDeleteVertexArraysOES: 'glDeleteVertexArrays', - glBindVertexArrayOES: 'glBindVertexArray', glFramebufferTexture2DOES: 'glFramebufferTexture2D', #else // LEGACY_GL_EMULATION - // Warn if code tries to use various emulation stuff, when emulation is disabled - // (do not warn if INCLUDE_FULL_LIBRARY is one, because then likely the gl code will - // not be called anyhow, leave only the runtime aborts) - glVertexPointer__deps: [function() { -#if INCLUDE_FULL_LIBRARY == 0 - warn('Legacy GL function (glVertexPointer) called. You need to compile with -s LEGACY_GL_EMULATION=1 to enable legacy GL emulation.'); -#endif - }], - glVertexPointer: function(){ throw 'Legacy GL function (glVertexPointer) called. You need to compile with -s LEGACY_GL_EMULATION=1 to enable legacy GL emulation.'; }, - glGenVertexArrays__deps: [function() { -#if INCLUDE_FULL_LIBRARY == 0 - warn('Legacy GL function (glGenVertexArrays) called. You need to compile with -s LEGACY_GL_EMULATION=1 to enable legacy GL emulation.'); -#endif - }], - glGenVertexArrays: function(){ throw 'Legacy GL function (glGenVertexArrays) called. You need to compile with -s LEGACY_GL_EMULATION=1 to enable legacy GL emulation.'; }, - glMatrixMode__deps: [function() { -#if INCLUDE_FULL_LIBRARY == 0 - warn('Legacy GL function (glMatrixMode) called. You need to compile with -s LEGACY_GL_EMULATION=1 to enable legacy GL emulation.'); -#endif - }], - glMatrixMode: function(){ throw 'Legacy GL function (glMatrixMode) called. You need to compile with -s LEGACY_GL_EMULATION=1 to enable legacy GL emulation.'; }, - glBegin__deps: [function() { -#if INCLUDE_FULL_LIBRARY == 0 - warn('Legacy GL function (glBegin) called. You need to compile with -s LEGACY_GL_EMULATION=1 to enable legacy GL emulation.'); -#endif - }], - glBegin: function(){ throw 'Legacy GL function (glBegin) called. You need to compile with -s LEGACY_GL_EMULATION=1 to enable legacy GL emulation.'; }, - glLoadIdentity__deps: [function() { -#if INCLUDE_FULL_LIBRARY == 0 - warn('Legacy GL function (glLoadIdentity) called. You need to compile with -s LEGACY_GL_EMULATION=1 to enable legacy GL emulation.'); -#endif - }], - glLoadIdentity: function(){ throw 'Legacy GL function (glLoadIdentity) called. You need to compile with -s LEGACY_GL_EMULATION=1 to enable legacy GL emulation.'; }, + glVertexPointer: function(){ throw 'Legacy GL function (glVertexPointer) called. If you want legacy GL emulation, you need to compile with -s LEGACY_GL_EMULATION=1 to enable legacy GL emulation.'; }, + glMatrixMode: function(){ throw 'Legacy GL function (glMatrixMode) called. If you want legacy GL emulation, you need to compile with -s LEGACY_GL_EMULATION=1 to enable legacy GL emulation.'; }, + glBegin: function(){ throw 'Legacy GL function (glBegin) called. If you want legacy GL emulation, you need to compile with -s LEGACY_GL_EMULATION=1 to enable legacy GL emulation.'; }, + glLoadIdentity: function(){ throw 'Legacy GL function (glLoadIdentity) called. If you want legacy GL emulation, you need to compile with -s LEGACY_GL_EMULATION=1 to enable legacy GL emulation.'; }, #endif // LEGACY_GL_EMULATION + // Open GLES1.1 vao compatibility (Could work w/o -s LEGACY_GL_EMULATION=1) + + glGenVertexArraysOES: 'glGenVertexArrays', + glDeleteVertexArraysOES: 'glDeleteVertexArrays', + glBindVertexArrayOES: 'glBindVertexArray', + // GLU gluPerspective: function(fov, aspect, near, far) { @@ -4986,7 +5276,7 @@ var LibraryGL = { var buf; if (!GL.currElementArrayBuffer) { var size = GL.calcBufLength(1, type, 0, count); - buf = GL.tempIndexBuffers[GL.tempBufferIndexLookup[size]]; + buf = GL.getTempIndexBuffer(size); GLctx.bindBuffer(GLctx.ELEMENT_ARRAY_BUFFER, buf); GLctx.bufferSubData(GLctx.ELEMENT_ARRAY_BUFFER, 0, @@ -5154,53 +5444,37 @@ if (LEGACY_GL_EMULATION) { DEFAULT_LIBRARY_FUNCS_TO_INCLUDE.push('$GLEmulation'); } -// GL proc address retrieval -LibraryGL.emscripten_GetProcAddress__deps = [function() { - // ProcAddress is used, so include everything in GL. This runs before we go to the $ProcAddressTable object, - // and we fill its deps just in time, and create the lookup table - var table = {}; - LibraryManager.library.emscripten_procAddressTable__deps = keys(LibraryGL).map(function(x) { - if (x.substr(-6) == '__deps' || x.substr(-9) == '__postset' || x.substr(-5) == '__sig' || x.substr(-5) == '__asm' || x.substr(0, 2) != 'gl') return null; - var original = x; - if (('_' + x) in Functions.implementedFunctions) { - // a user-implemented function aliases this one, but we still want it to be accessible by name, so rename it - var y = x + '__procTable'; - LibraryManager.library[y] = LibraryManager.library[x]; - LibraryManager.library[y + '__deps'] = LibraryManager.library[x + '__deps']; - LibraryManager.library[y + '__postset'] = LibraryManager.library[x + '__postset']; - LibraryManager.library[y + '__sig'] = LibraryManager.library[x + '__sig'];//|| Functions.implementedFunctions['_' + x]; - LibraryManager.library[y + '__asm'] = LibraryManager.library[x + '__asm']; - x = y; - assert(!(y in Functions.implementedFunctions) && !Functions.unimplementedFunctions['_' + y]); - } - var longX = '_' + x; - var sig = LibraryManager.library[x + '__sig'] || functionStubSigs[longX]; - if (sig) { - table[original] = Functions.getIndex(longX, sig); - if (!(longX in Functions.implementedFunctions)) Functions.unimplementedFunctions[longX] = sig; - } - return x; - }).filter(function(x) { return x !== null }); - // convert table into function with switch, to not confuse closure compiler - var tableImpl = 'switch(name) {\n'; - for (var x in table) tableImpl += 'case "' + x + '": return ' + table[x] + '; break;\n'; - tableImpl += '}\nreturn 0;'; - LibraryManager.library.emscripten_procAddressTable = new Function('name', tableImpl); -}, 'emscripten_procAddressTable']; -LibraryGL.emscripten_GetProcAddress = function _LibraryGL_emscripten_GetProcAddress(name) { - name = name.replace('EXT', '').replace('ARB', ''); - switch(name) { // misc renamings - case 'glCreateProgramObject': name = 'glCreateProgram'; break; - case 'glUseProgramObject': name = 'glUseProgram'; break; - case 'glCreateShaderObject': name = 'glCreateShader'; break; - case 'glAttachObject': name = 'glAttachShader'; break; - case 'glDetachObject': name = 'glDetachShader'; break; - } - var ret = _emscripten_procAddressTable(name); - if (!ret) Module.printErr('WARNING: getProcAddress failed for ' + name); - return ret; +function copyLibEntry(a, b) { + LibraryGL[a] = LibraryGL[b]; + LibraryGL[a + '__postset'] = LibraryGL[b + '__postset']; + LibraryGL[a + '__sig'] = LibraryGL[b + '__sig']; + LibraryGL[a + '__asm'] = LibraryGL[b + '__asm']; + LibraryGL[a + '__deps'] = LibraryGL[b + '__deps'].slice(0); } +// GL proc address retrieval - allow access through glX and emscripten_glX, to allow name collisions with user-implemented things having the same name (see gl.c) +keys(LibraryGL).forEach(function(x) { + if (x.substr(-6) == '__deps' || x.substr(-9) == '__postset' || x.substr(-5) == '__sig' || x.substr(-5) == '__asm' || x.substr(0, 2) != 'gl') return; + while (typeof LibraryGL[x] === 'string') { + // resolve aliases right here, simpler for fastcomp + copyLibEntry(x, LibraryGL[x]); + } + var y = 'emscripten_' + x; + LibraryGL[x + '__deps'] = LibraryGL[x + '__deps'].map(function(dep) { + // prefix dependencies as well + if (typeof dep === 'string' && dep[0] == 'g' && dep[1] == 'l' && LibraryGL[dep]) { + var orig = dep; + dep = 'emscripten_' + dep; + var fixed = LibraryGL[x].toString().replace(new RegExp('_' + orig + '\\(', 'g'), '_' + dep + '('); + fixed = fixed.substr(0, 9) + '_' + y + fixed.substr(9); + LibraryGL[x] = eval('(function() { return ' + fixed + ' })()'); + } + return dep; + }); + // copy it + copyLibEntry(y, x); +}); + // Final merge mergeInto(LibraryManager.library, LibraryGL); diff --git a/src/library_glfw.js b/src/library_glfw.js index 17e8956a..e5782900 100644 --- a/src/library_glfw.js +++ b/src/library_glfw.js @@ -51,7 +51,7 @@ var LibraryGLFW = { DOMToGLFWKeyCode: function(keycode) { switch (keycode) { case 0x09: return 295 ; //DOM_VK_TAB -> GLFW_KEY_TAB - case 0x1B: return 255 ; //DOM_VK_ESCAPE -> GLFW_KEY_ESC + case 0x1B: return 257 ; //DOM_VK_ESCAPE -> GLFW_KEY_ESC case 0x6A: return 313 ; //DOM_VK_MULTIPLY -> GLFW_KEY_KP_MULTIPLY case 0x6B: return 315 ; //DOM_VK_ADD -> GLFW_KEY_KP_ADD case 0x6D: return 314 ; //DOM_VK_SUBTRACT -> GLFW_KEY_KP_SUBTRACT @@ -197,13 +197,7 @@ var LibraryGLFW = { }, onMouseWheel: function(event) { - if (event.detail > 0) { - GLFW.wheelPos++; - } - - if (event.detail < 0) { - GLFW.wheelPos--; - } + GLFW.wheelPos += Browser.getMouseWheelDelta(event); if (GLFW.mouseWheelFunc && event.target == Module["canvas"]) { Runtime.dynCall('vi', GLFW.mouseWheelFunc, [GLFW.wheelPos]); diff --git a/src/library_glut.js b/src/library_glut.js index 65ac10c4..445e08a4 100644 --- a/src/library_glut.js +++ b/src/library_glut.js @@ -22,6 +22,7 @@ var LibraryGLUT = { windowY: 0, windowWidth: 0, windowHeight: 0, + requestedAnimationFrame: false, saveModifiers: function(event) { GLUT.modifiers = 0; @@ -229,7 +230,7 @@ var LibraryGLUT = { // cross-browser wheel delta var e = window.event || event; // old IE support - var delta = Math.max(-1, Math.min(1, (e.wheelDelta || -e.detail))); + var delta = -Browser.getMouseWheelDelta(event); var button = 3; // wheel up if (delta < 0) { @@ -484,8 +485,10 @@ var LibraryGLUT = { glutSwapBuffers: function() {}, glutPostRedisplay: function() { - if (GLUT.displayFunc) { + if (GLUT.displayFunc && !GLUT.requestedAnimationFrame) { + GLUT.requestedAnimationFrame = true; Browser.requestAnimationFrame(function() { + GLUT.requestedAnimationFrame = false; if (ABORT) return; Runtime.dynCall('v', GLUT.displayFunc); }); diff --git a/src/library_html5.js b/src/library_html5.js new file mode 100644 index 00000000..d9376c2a --- /dev/null +++ b/src/library_html5.js @@ -0,0 +1,1313 @@ +var LibraryJSEvents = { + $JSEvents: { + // pointers to structs malloc()ed to Emscripten HEAP for JS->C interop. + keyEvent: 0, + mouseEvent: 0, + wheelEvent: 0, + uiEvent: 0, + focusEvent: 0, + deviceOrientationEvent: 0, + deviceMotionEvent: 0, + fullscreenChangeEvent: 0, + pointerlockChangeEvent: 0, + visibilityChangeEvent: 0, + touchEvent: 0, + + // When we transition from fullscreen to windowed mode, we remember here the element that was just in fullscreen mode + // so that we can report information about that element in the event message. + previousFullscreenElement: null, + + // Remember the current mouse coordinates in case we need to emulate movementXY generation for browsers that don't support it. + // Some browsers (e.g. Safari 6.0.5) only give movementXY when Pointerlock is active. + previousScreenX: null, + previousScreenY: null, + + // When the C runtime exits via exit(), we unregister all event handlers added by this library to be nice and clean. + // Track in this field whether we have yet registered that __ATEXIT__ handler. + removeEventListenersRegistered: false, + + registerRemoveEventListeners: function() { + if (!JSEvents.removeEventListenersRegistered) { + __ATEXIT__.push({ func: function() { + for(var i = JSEvents.eventHandlers.length-1; i >= 0; --i) { + JSEvents._removeHandler(i); + } + } }); + JSEvents.removeEventListenersRegistered = true; + } + }, + + findEventTarget: function(target) { + if (target) { + if (typeof target == "number") { + target = Pointer_stringify(target); + } + if (target == '#window') return window; + else if (target == '#document') return document; + else if (target == '#screen') return window.screen; + else if (target == '#canvas') return Module['canvas']; + + if (typeof target == 'string') return document.getElementById(target); + else return target; + } else { + // The sensible target varies between events, but use window as the default + // since DOM events mostly can default to that. Specific callback registrations + // override their own defaults. + return window; + } + }, + + deferredCalls: [], + + // Queues the given function call to occur the next time we enter an event handler. + // Existing implementations of pointerlock apis have required that + // the target element is active in fullscreen mode first. Thefefore give + // fullscreen mode request a precedence of 1 and pointer lock a precedence of 2 + // and sort by that to always request fullscreen before pointer lock. + deferCall: function(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: targetFunction, + precedence: precedence, + argsList: argsList + }); + + JSEvents.deferredCalls.sort(function(x,y) { return x.precedence < y.precedence; }); + }, + + // Erases all deferred calls to the given target function from the queue list. + removeDeferredCalls: function(targetFunction) { + for(var i = 0; i < JSEvents.deferredCalls.length; ++i) { + if (JSEvents.deferredCalls[i].targetFunction == targetFunction) { + JSEvents.deferredCalls.splice(i, 1); + --i; + } + } + }, + + canPerformEventHandlerRequests: function() { + return JSEvents.inEventHandler && JSEvents.currentEventHandler.allowsDeferredCalls; + }, + + runDeferredCalls: function() { + 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.apply(this, call.argsList); + } + }, + + // If positive, we are currently executing in a JS event handler. + inEventHandler: 0, + // If we are in an event handler, specifies the event handler object from the eventHandlers array that is currently running. + currentEventHandler: null, + + // Stores objects representing each currently registered JS event handler. + eventHandlers: [], + + isInternetExplorer: function() { return navigator.userAgent.indexOf('MSIE') !== -1 || navigator.appVersion.indexOf('Trident/') > 0; }, + + _removeHandler: function(i) { + var h = JSEvents.eventHandlers[i]; + h.target.removeEventListener(h.eventTypeString, h.eventListenerFunc, h.useCapture); + JSEvents.eventHandlers.splice(i, 1); + }, + + registerOrRemoveHandler: function(eventHandler) { + var jsEventHandler = function jsEventHandler(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; + } + + if (eventHandler.callbackfunc) { + eventHandler.eventListenerFunc = jsEventHandler; + eventHandler.target.addEventListener(eventHandler.eventTypeString, jsEventHandler, eventHandler.useCapture); + JSEvents.eventHandlers.push(eventHandler); + JSEvents.registerRemoveEventListeners(); + } 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--); + } + } + } + }, + + registerKeyEventCallback: function(target, userData, useCapture, callbackfunc, eventTypeId, eventTypeString) { + if (!JSEvents.keyEvent) { + JSEvents.keyEvent = _malloc( {{{ C_STRUCTS.EmscriptenKeyboardEvent.__size__ }}} ); + } + var handlerFunc = function(event) { + var e = event || window.event; + writeStringToMemory(e.key ? e.key : "", JSEvents.keyEvent + {{{ C_STRUCTS.EmscriptenKeyboardEvent.key }}} ); + writeStringToMemory(e.code ? e.code : "", JSEvents.keyEvent + {{{ C_STRUCTS.EmscriptenKeyboardEvent.code }}} ); + {{{ makeSetValue('JSEvents.keyEvent', C_STRUCTS.EmscriptenKeyboardEvent.location, 'e.location', 'i32') }}}; + {{{ makeSetValue('JSEvents.keyEvent', C_STRUCTS.EmscriptenKeyboardEvent.ctrlKey, 'e.ctrlKey', 'i32') }}}; + {{{ makeSetValue('JSEvents.keyEvent', C_STRUCTS.EmscriptenKeyboardEvent.shiftKey, 'e.shiftKey', 'i32') }}}; + {{{ makeSetValue('JSEvents.keyEvent', C_STRUCTS.EmscriptenKeyboardEvent.altKey, 'e.altKey', 'i32') }}}; + {{{ makeSetValue('JSEvents.keyEvent', C_STRUCTS.EmscriptenKeyboardEvent.metaKey, 'e.metaKey', 'i32') }}}; + {{{ makeSetValue('JSEvents.keyEvent', C_STRUCTS.EmscriptenKeyboardEvent.repeat, 'e.repeat', 'i32') }}}; + writeStringToMemory(e.locale ? e.locale : "", JSEvents.keyEvent + {{{ C_STRUCTS.EmscriptenKeyboardEvent.locale }}} ); + writeStringToMemory(e.char ? e.char : "", JSEvents.keyEvent + {{{ C_STRUCTS.EmscriptenKeyboardEvent.charValue }}} ); + {{{ makeSetValue('JSEvents.keyEvent', C_STRUCTS.EmscriptenKeyboardEvent.charCode, 'e.charCode', 'i32') }}}; + {{{ makeSetValue('JSEvents.keyEvent', C_STRUCTS.EmscriptenKeyboardEvent.keyCode, 'e.keyCode', 'i32') }}}; + {{{ makeSetValue('JSEvents.keyEvent', C_STRUCTS.EmscriptenKeyboardEvent.which, 'e.which', 'i32') }}}; + var shouldCancel = Runtime.dynCall('iiii', callbackfunc, [eventTypeId, JSEvents.keyEvent, userData]); + if (shouldCancel) { + e.preventDefault(); + } + }; + + var eventHandler = { + target: JSEvents.findEventTarget(target), + allowsDeferredCalls: JSEvents.isInternetExplorer() ? false : true, // MSIE doesn't allow fullscreen and pointerlock requests from key handlers, others do. + eventTypeString: eventTypeString, + callbackfunc: callbackfunc, + handlerFunc: handlerFunc, + useCapture: useCapture + }; + JSEvents.registerOrRemoveHandler(eventHandler); + }, + + fillMouseEventData: function(eventStruct, e) { + var rect = Module['canvas'].getBoundingClientRect(); + {{{ makeSetValue('eventStruct', C_STRUCTS.EmscriptenMouseEvent.timestamp, 'JSEvents.tick()', 'double') }}}; + {{{ makeSetValue('eventStruct', C_STRUCTS.EmscriptenMouseEvent.screenX, 'e.screenX', 'i32') }}}; + {{{ makeSetValue('eventStruct', C_STRUCTS.EmscriptenMouseEvent.screenY, 'e.screenY', 'i32') }}}; + {{{ makeSetValue('eventStruct', C_STRUCTS.EmscriptenMouseEvent.clientX, 'e.clientX', 'i32') }}}; + {{{ makeSetValue('eventStruct', C_STRUCTS.EmscriptenMouseEvent.clientY, 'e.clientY', 'i32') }}}; + {{{ makeSetValue('eventStruct', C_STRUCTS.EmscriptenMouseEvent.ctrlKey, 'e.ctrlKey', 'i32') }}}; + {{{ makeSetValue('eventStruct', C_STRUCTS.EmscriptenMouseEvent.shiftKey, 'e.shiftKey', 'i32') }}}; + {{{ makeSetValue('eventStruct', C_STRUCTS.EmscriptenMouseEvent.altKey, 'e.altKey', 'i32') }}}; + {{{ makeSetValue('eventStruct', C_STRUCTS.EmscriptenMouseEvent.metaKey, 'e.metaKey', 'i32') }}}; + {{{ makeSetValue('eventStruct', C_STRUCTS.EmscriptenMouseEvent.button, 'e.button', 'i16') }}}; + {{{ makeSetValue('eventStruct', C_STRUCTS.EmscriptenMouseEvent.buttons, 'e.buttons', 'i16') }}}; + {{{ makeSetValue('eventStruct', C_STRUCTS.EmscriptenMouseEvent.movementX, 'e["movementX"] || e["mozMovementX"] || e["webkitMovementX"] || (e.screenX-JSEvents.previousScreenX)', 'i32') }}}; + {{{ makeSetValue('eventStruct', C_STRUCTS.EmscriptenMouseEvent.movementY, 'e["movementY"] || e["mozMovementY"] || e["webkitMovementY"] || (e.screenY-JSEvents.previousScreenY)', 'i32') }}}; + {{{ makeSetValue('eventStruct', C_STRUCTS.EmscriptenMouseEvent.canvasX, 'e.clientX - rect.left', 'i32') }}}; + {{{ makeSetValue('eventStruct', C_STRUCTS.EmscriptenMouseEvent.canvasY, 'e.clientY - rect.top', 'i32') }}}; + JSEvents.previousScreenX = e.screenX; + JSEvents.previousScreenY = e.screenY; + }, + + registerMouseEventCallback: function(target, userData, useCapture, callbackfunc, eventTypeId, eventTypeString) { + if (!JSEvents.mouseEvent) { + JSEvents.mouseEvent = _malloc( {{{ C_STRUCTS.EmscriptenMouseEvent.__size__ }}} ); + } + var handlerFunc = function(event) { + var e = event || window.event; + JSEvents.fillMouseEventData(JSEvents.mouseEvent, e); + var shouldCancel = Runtime.dynCall('iiii', callbackfunc, [eventTypeId, JSEvents.mouseEvent, userData]); + if (shouldCancel) { + e.preventDefault(); + } + }; + + var eventHandler = { + target: JSEvents.findEventTarget(target), + allowsDeferredCalls: eventTypeString != 'mousemove', // Mouse move events do not allow fullscreen/pointer lock requests to be handled in them! + eventTypeString: eventTypeString, + callbackfunc: callbackfunc, + handlerFunc: handlerFunc, + useCapture: useCapture + }; + // In IE, mousedown events don't either allow deferred calls to be run! + if (JSEvents.isInternetExplorer() && eventTypeString == 'mousedown') eventHandler.allowsDeferredCalls = false; + JSEvents.registerOrRemoveHandler(eventHandler); + }, + + registerWheelEventCallback: function(target, userData, useCapture, callbackfunc, eventTypeId, eventTypeString) { + if (!JSEvents.wheelEvent) { + JSEvents.wheelEvent = _malloc( {{{ C_STRUCTS.EmscriptenWheelEvent.__size__ }}} ); + } + // The DOM Level 3 events spec event 'wheel' + var wheelHandlerFunc = function(event) { + var e = event || window.event; + JSEvents.fillMouseEventData(JSEvents.wheelEvent, e); + {{{ makeSetValue('JSEvents.wheelEvent', C_STRUCTS.EmscriptenWheelEvent.deltaX, 'e["deltaX"]', 'double') }}}; + {{{ makeSetValue('JSEvents.wheelEvent', C_STRUCTS.EmscriptenWheelEvent.deltaY, 'e["deltaY"]', 'double') }}}; + {{{ makeSetValue('JSEvents.wheelEvent', C_STRUCTS.EmscriptenWheelEvent.deltaZ, 'e["deltaZ"]', 'double') }}}; + {{{ makeSetValue('JSEvents.wheelEvent', C_STRUCTS.EmscriptenWheelEvent.deltaMode, 'e["deltaMode"]', 'i32') }}}; + var shouldCancel = Runtime.dynCall('iiii', callbackfunc, [eventTypeId, JSEvents.wheelEvent, userData]); + if (shouldCancel) { + e.preventDefault(); + } + }; + // The 'mousewheel' event as implemented in Safari 6.0.5 + var mouseWheelHandlerFunc = function(event) { + var e = event || window.event; + JSEvents.fillMouseEventData(JSEvents.wheelEvent, e); + {{{ makeSetValue('JSEvents.wheelEvent', C_STRUCTS.EmscriptenWheelEvent.deltaX, 'e["wheelDeltaX"]', 'double') }}}; + {{{ makeSetValue('JSEvents.wheelEvent', C_STRUCTS.EmscriptenWheelEvent.deltaY, '-e["wheelDeltaY"] /* Invert to unify direction with the DOM Level 3 wheel event. */', 'double') }}}; + {{{ makeSetValue('JSEvents.wheelEvent', C_STRUCTS.EmscriptenWheelEvent.deltaZ, '0 /* Not available */', 'double') }}}; + {{{ makeSetValue('JSEvents.wheelEvent', C_STRUCTS.EmscriptenWheelEvent.deltaMode, '0 /* DOM_DELTA_PIXEL */', 'i32') }}}; + var shouldCancel = Runtime.dynCall('iiii', callbackfunc, [eventTypeId, JSEvents.wheelEvent, userData]); + if (shouldCancel) { + e.preventDefault(); + } + }; + + var eventHandler = { + target: JSEvents.findEventTarget(target), + allowsDeferredCalls: true, + eventTypeString: eventTypeString, + callbackfunc: callbackfunc, + handlerFunc: (eventTypeString == 'wheel') ? wheelHandlerFunc : mouseWheelHandlerFunc, + useCapture: useCapture + }; + JSEvents.registerOrRemoveHandler(eventHandler); + }, + + pageScrollPos: function() { + if (window.pageXOffset > 0 || window.pageYOffset > 0) { + return [window.pageXOffset, window.pageYOffset]; + } + if (typeof document.documentElement.scrollLeft !== 'undefined' || typeof document.documentElement.scrollTop !== 'undefined') { + return [document.documentElement.scrollLeft, document.documentElement.scrollTop]; + } + return [document.body.scrollLeft|0, document.body.scrollTop|0]; + }, + + registerUiEventCallback: function(target, userData, useCapture, callbackfunc, eventTypeId, eventTypeString) { + if (!JSEvents.uiEvent) { + JSEvents.uiEvent = _malloc( {{{ C_STRUCTS.EmscriptenUiEvent.__size__ }}} ); + } + + if (eventTypeString == "scroll" && !target) { + target = document; // By default read scroll events on document rather than window. + } else { + target = JSEvents.findEventTarget(target); + } + + var handlerFunc = function(event) { + var e = event || window.event; + if (e.target != target) { + // Never take ui events such as scroll via a 'bubbled' route, but always from the direct element that + // was targeted. Otherwise e.g. if app logs a message in response to a page scroll, the Emscripten log + // message box could cause to scroll, generating a new (bubbled) scroll message, causing a new log print, + // causing a new scroll, etc.. + return; + } + var scrollPos = JSEvents.pageScrollPos(); + {{{ makeSetValue('JSEvents.uiEvent', C_STRUCTS.EmscriptenUiEvent.detail, 'e.detail', 'i32') }}}; + {{{ makeSetValue('JSEvents.uiEvent', C_STRUCTS.EmscriptenUiEvent.documentBodyClientWidth, 'document.body.clientWidth', 'i32') }}}; + {{{ makeSetValue('JSEvents.uiEvent', C_STRUCTS.EmscriptenUiEvent.documentBodyClientHeight, 'document.body.clientHeight', 'i32') }}}; + {{{ makeSetValue('JSEvents.uiEvent', C_STRUCTS.EmscriptenUiEvent.windowInnerWidth, 'window.innerWidth', 'i32') }}}; + {{{ makeSetValue('JSEvents.uiEvent', C_STRUCTS.EmscriptenUiEvent.windowInnerHeight, 'window.innerHeight', 'i32') }}}; + {{{ makeSetValue('JSEvents.uiEvent', C_STRUCTS.EmscriptenUiEvent.windowOuterWidth, 'window.outerWidth', 'i32') }}}; + {{{ makeSetValue('JSEvents.uiEvent', C_STRUCTS.EmscriptenUiEvent.windowOuterHeight, 'window.outerHeight', 'i32') }}}; + {{{ makeSetValue('JSEvents.uiEvent', C_STRUCTS.EmscriptenUiEvent.scrollTop, 'scrollPos[0]', 'i32') }}}; + {{{ makeSetValue('JSEvents.uiEvent', C_STRUCTS.EmscriptenUiEvent.scrollLeft, 'scrollPos[1]', 'i32') }}}; + var shouldCancel = Runtime.dynCall('iiii', callbackfunc, [eventTypeId, JSEvents.uiEvent, userData]); + if (shouldCancel) { + e.preventDefault(); + } + }; + + var eventHandler = { + target: target, + allowsDeferredCalls: false, // Neither scroll or resize events allow running requests inside them. + eventTypeString: eventTypeString, + callbackfunc: callbackfunc, + handlerFunc: handlerFunc, + useCapture: useCapture + }; + JSEvents.registerOrRemoveHandler(eventHandler); + }, + + getNodeNameForTarget: function(target) { + if (!target) return ''; + if (target == window) return '#window'; + if (target == window.screen) return '#screen'; + return (target && target.nodeName) ? target.nodeName : ''; + }, + + registerFocusEventCallback: function(target, userData, useCapture, callbackfunc, eventTypeId, eventTypeString) { + if (!JSEvents.focusEvent) { + JSEvents.focusEvent = _malloc( {{{ C_STRUCTS.EmscriptenFocusEvent.__size__ }}} ); + } + var handlerFunc = function(event) { + var e = event || window.event; + + var nodeName = JSEvents.getNodeNameForTarget(e.target); + var id = e.target.id ? e.target.id : ''; + writeStringToMemory(nodeName, JSEvents.focusEvent + {{{ C_STRUCTS.EmscriptenFocusEvent.nodeName }}} ); + writeStringToMemory(id, JSEvents.focusEvent + {{{ C_STRUCTS.EmscriptenFocusEvent.id }}} ); + var shouldCancel = Runtime.dynCall('iiii', callbackfunc, [eventTypeId, JSEvents.focusEvent, userData]); + if (shouldCancel) { + e.preventDefault(); + } + }; + + var eventHandler = { + target: JSEvents.findEventTarget(target), + allowsDeferredCalls: false, + eventTypeString: eventTypeString, + callbackfunc: callbackfunc, + handlerFunc: handlerFunc, + useCapture: useCapture + }; + JSEvents.registerOrRemoveHandler(eventHandler); + }, + + tick: function() { + if (window['performance'] && window['performance']['now']) return window['performance']['now'](); + else return Date.now(); + }, + + registerDeviceOrientationEventCallback: function(target, userData, useCapture, callbackfunc, eventTypeId, eventTypeString) { + if (!JSEvents.deviceOrientationEvent) { + JSEvents.deviceOrientationEvent = _malloc( {{{ C_STRUCTS.EmscriptenDeviceOrientationEvent.__size__ }}} ); + } + var handlerFunc = function(event) { + var e = event || window.event; + + {{{ makeSetValue('JSEvents.deviceOrientationEvent', C_STRUCTS.EmscriptenDeviceOrientationEvent.timestamp, 'JSEvents.tick()', 'double') }}}; + {{{ makeSetValue('JSEvents.deviceOrientationEvent', C_STRUCTS.EmscriptenDeviceOrientationEvent.alpha, 'e.alpha', 'double') }}}; + {{{ makeSetValue('JSEvents.deviceOrientationEvent', C_STRUCTS.EmscriptenDeviceOrientationEvent.beta, 'e.beta', 'double') }}}; + {{{ makeSetValue('JSEvents.deviceOrientationEvent', C_STRUCTS.EmscriptenDeviceOrientationEvent.gamma, 'e.gamma', 'double') }}}; + {{{ makeSetValue('JSEvents.deviceOrientationEvent', C_STRUCTS.EmscriptenDeviceOrientationEvent.absolute, 'e.absolute', 'i32') }}}; + + var shouldCancel = Runtime.dynCall('iiii', callbackfunc, [eventTypeId, JSEvents.deviceOrientationEvent, userData]); + if (shouldCancel) { + e.preventDefault(); + } + }; + + var eventHandler = { + target: JSEvents.findEventTarget(target), + allowsDeferredCalls: false, + eventTypeString: eventTypeString, + callbackfunc: callbackfunc, + handlerFunc: handlerFunc, + useCapture: useCapture + }; + JSEvents.registerOrRemoveHandler(eventHandler); + }, + + registerDeviceMotionEventCallback: function(target, userData, useCapture, callbackfunc, eventTypeId, eventTypeString) { + if (!JSEvents.deviceMotionEvent) { + JSEvents.deviceMotionEvent = _malloc( {{{ C_STRUCTS.EmscriptenDeviceMotionEvent.__size__ }}} ); + } + var handlerFunc = function(event) { + var e = event || window.event; + + {{{ makeSetValue('JSEvents.deviceOrientationEvent', C_STRUCTS.EmscriptenDeviceMotionEvent.timestamp, 'JSEvents.tick()', 'double') }}}; + {{{ makeSetValue('JSEvents.deviceMotionEvent', C_STRUCTS.EmscriptenDeviceMotionEvent.accelerationX, 'e.acceleration.x', 'double') }}}; + {{{ makeSetValue('JSEvents.deviceMotionEvent', C_STRUCTS.EmscriptenDeviceMotionEvent.accelerationY, 'e.acceleration.y', 'double') }}}; + {{{ makeSetValue('JSEvents.deviceMotionEvent', C_STRUCTS.EmscriptenDeviceMotionEvent.accelerationZ, 'e.acceleration.z', 'double') }}}; + {{{ makeSetValue('JSEvents.deviceMotionEvent', C_STRUCTS.EmscriptenDeviceMotionEvent.accelerationIncludingGravityX, 'e.accelerationIncludingGravity.x', 'double') }}}; + {{{ makeSetValue('JSEvents.deviceMotionEvent', C_STRUCTS.EmscriptenDeviceMotionEvent.accelerationIncludingGravityY, 'e.accelerationIncludingGravity.y', 'double') }}}; + {{{ makeSetValue('JSEvents.deviceMotionEvent', C_STRUCTS.EmscriptenDeviceMotionEvent.accelerationIncludingGravityZ, 'e.accelerationIncludingGravity.z', 'double') }}}; + {{{ makeSetValue('JSEvents.deviceMotionEvent', C_STRUCTS.EmscriptenDeviceMotionEvent.rotationRateAlpha, 'e.rotationRate.alpha', 'double') }}}; + {{{ makeSetValue('JSEvents.deviceMotionEvent', C_STRUCTS.EmscriptenDeviceMotionEvent.rotationRateBeta, 'e.rotationRate.beta', 'double') }}}; + {{{ makeSetValue('JSEvents.deviceMotionEvent', C_STRUCTS.EmscriptenDeviceMotionEvent.rotationRateGamma, 'e.rotationRate.gamma', 'double') }}}; + + var shouldCancel = Runtime.dynCall('iiii', callbackfunc, [eventTypeId, JSEvents.deviceMotionEvent, userData]); + if (shouldCancel) { + e.preventDefault(); + } + }; + + var eventHandler = { + target: JSEvents.findEventTarget(target), + allowsDeferredCalls: false, + eventTypeString: eventTypeString, + callbackfunc: callbackfunc, + handlerFunc: handlerFunc, + useCapture: useCapture + }; + JSEvents.registerOrRemoveHandler(eventHandler); + }, + + screenOrientation: function() { + if (!window.screen) return undefined; + return window.screen.orientation || window.screen.mozOrientation || window.screen.webkitOrientation || window.screen.msOrientation; + }, + + fillOrientationChangeEventData: function(eventStruct, e) { + var orientations = ["portrait-primary", "portrait-secondary", "landscape-primary", "landscape-secondary"]; + var orientations2 = ["portrait", "portrait", "landscape", "landscape"]; + + var orientationString = JSEvents.screenOrientation(); + var orientation = orientations.indexOf(orientationString); + if (orientation == -1) { + orientation = orientations2.indexOf(orientationString); + } + + {{{ makeSetValue('eventStruct', C_STRUCTS.EmscriptenOrientationChangeEvent.orientationIndex, '1 << orientation', 'i32') }}}; + {{{ makeSetValue('eventStruct', C_STRUCTS.EmscriptenOrientationChangeEvent.orientationAngle, 'window.orientation', 'i32') }}}; + }, + + registerOrientationChangeEventCallback: function(target, userData, useCapture, callbackfunc, eventTypeId, eventTypeString) { + if (!JSEvents.orientationChangeEvent) { + JSEvents.orientationChangeEvent = _malloc( {{{ C_STRUCTS.EmscriptenOrientationChangeEvent.__size__ }}} ); + } + + if (!target) { + target = window.screen; // Orientation events need to be captured from 'window.screen' instead of 'window' + } else { + target = JSEvents.findEventTarget(target); + } + + var handlerFunc = function(event) { + var e = event || window.event; + + JSEvents.fillOrientationChangeEventData(JSEvents.orientationChangeEvent, e); + + var shouldCancel = Runtime.dynCall('iiii', callbackfunc, [eventTypeId, JSEvents.orientationChangeEvent, userData]); + if (shouldCancel) { + e.preventDefault(); + } + }; + + if (eventTypeString == "orientationchange" && window.screen.mozOrientation !== undefined) { + eventTypeString = "mozorientationchange"; + } + + var eventHandler = { + target: target, + allowsDeferredCalls: false, + eventTypeString: eventTypeString, + callbackfunc: callbackfunc, + handlerFunc: handlerFunc, + useCapture: useCapture + }; + JSEvents.registerOrRemoveHandler(eventHandler); + }, + + fullscreenEnabled: function() { + return document.fullscreenEnabled || document.mozFullscreenEnabled || document.mozFullScreenEnabled || document.webkitFullscreenEnabled || document.msFullscreenEnabled; + }, + + fillFullscreenChangeEventData: function(eventStruct, e) { + var fullscreenElement = document.fullscreenElement || document.mozFullScreenElement || document.webkitFullscreenElement || document.msFullscreenElement; + var isFullscreen = !!fullscreenElement; + {{{ makeSetValue('eventStruct', C_STRUCTS.EmscriptenFullscreenChangeEvent.isFullscreen, 'isFullscreen', 'i32') }}}; + {{{ makeSetValue('eventStruct', C_STRUCTS.EmscriptenFullscreenChangeEvent.fullscreenEnabled, 'JSEvents.fullscreenEnabled()', 'i32') }}}; + // If transitioning to fullscreen, report info about the element that is now fullscreen. + // If transitioning to windowed mode, report info about the element that just was fullscreen. + var reportedElement = isFullscreen ? fullscreenElement : JSEvents.previousFullscreenElement; + var nodeName = JSEvents.getNodeNameForTarget(reportedElement); + var id = (reportedElement && reportedElement.id) ? reportedElement.id : ''; + writeStringToMemory(nodeName, eventStruct + {{{ C_STRUCTS.EmscriptenFullscreenChangeEvent.nodeName }}} ); + writeStringToMemory(id, eventStruct + {{{ C_STRUCTS.EmscriptenFullscreenChangeEvent.id }}} ); + {{{ makeSetValue('eventStruct', C_STRUCTS.EmscriptenFullscreenChangeEvent.elementWidth, 'reportedElement ? reportedElement.clientWidth : 0', 'i32') }}}; + {{{ makeSetValue('eventStruct', C_STRUCTS.EmscriptenFullscreenChangeEvent.elementHeight, 'reportedElement ? reportedElement.clientHeight : 0', 'i32') }}}; + {{{ makeSetValue('eventStruct', C_STRUCTS.EmscriptenFullscreenChangeEvent.screenWidth, 'screen.width', 'i32') }}}; + {{{ makeSetValue('eventStruct', C_STRUCTS.EmscriptenFullscreenChangeEvent.screenHeight, 'screen.height', 'i32') }}}; + if (isFullscreen) { + JSEvents.previousFullscreenElement = fullscreenElement; + } + }, + + registerFullscreenChangeEventCallback: function(target, userData, useCapture, callbackfunc, eventTypeId, eventTypeString) { + if (!JSEvents.fullscreenChangeEvent) { + JSEvents.fullscreenChangeEvent = _malloc( {{{ C_STRUCTS.EmscriptenFullscreenChangeEvent.__size__ }}} ); + } + + if (!target) { + target = document; // Fullscreen change events need to be captured from 'document' by default instead of 'window' + } else { + target = JSEvents.findEventTarget(target); + } + + var handlerFunc = function(event) { + var e = event || window.event; + + JSEvents.fillFullscreenChangeEventData(JSEvents.fullscreenChangeEvent, e); + + var shouldCancel = Runtime.dynCall('iiii', callbackfunc, [eventTypeId, JSEvents.fullscreenChangeEvent, userData]); + if (shouldCancel) { + e.preventDefault(); + } + }; + + var eventHandler = { + target: target, + allowsDeferredCalls: false, + eventTypeString: eventTypeString, + callbackfunc: callbackfunc, + handlerFunc: handlerFunc, + useCapture: useCapture + }; + JSEvents.registerOrRemoveHandler(eventHandler); + }, + + requestFullscreen: function(target) { + if (target.requestFullscreen) { + target.requestFullscreen(); + } else if (target.msRequestFullscreen) { + target.msRequestFullscreen(); + } else if (target.mozRequestFullScreen) { + target.mozRequestFullScreen(); + } else if (target.mozRequestFullscreen) { + target.mozRequestFullscreen(); + } else if (target.webkitRequestFullscreen) { + target.webkitRequestFullscreen(Element.ALLOW_KEYBOARD_INPUT); + } else { + if (typeof JSEvents.fullscreenEnabled() === 'undefined') { + return {{{ cDefine('EMSCRIPTEN_RESULT_NOT_SUPPORTED') }}}; + } else { + return {{{ cDefine('EMSCRIPTEN_RESULT_INVALID_TARGET') }}}; + } + } + return {{{ cDefine('EMSCRIPTEN_RESULT_SUCCESS') }}}; + }, + + fillPointerlockChangeEventData: function(eventStruct, e) { + var pointerLockElement = document.pointerLockElement || document.mozPointerLockElement || document.webkitPointerLockElement || document.msPointerLockElement; + var isPointerlocked = !!pointerLockElement; + {{{ makeSetValue('eventStruct', C_STRUCTS.EmscriptenPointerlockChangeEvent.isActive, 'isPointerlocked', 'i32') }}}; + var nodeName = JSEvents.getNodeNameForTarget(pointerLockElement); + var id = (pointerLockElement && pointerLockElement.id) ? pointerLockElement.id : ''; + writeStringToMemory(nodeName, eventStruct + {{{ C_STRUCTS.EmscriptenPointerlockChangeEvent.nodeName }}} ); + writeStringToMemory(id, eventStruct + {{{ C_STRUCTS.EmscriptenPointerlockChangeEvent.id }}}); + }, + + registerPointerlockChangeEventCallback: function(target, userData, useCapture, callbackfunc, eventTypeId, eventTypeString) { + if (!JSEvents.pointerlockChangeEvent) { + JSEvents.pointerlockChangeEvent = _malloc( {{{ C_STRUCTS.EmscriptenPointerlockChangeEvent.__size__ }}} ); + } + + if (!target) { + target = document; // Pointer lock change events need to be captured from 'document' by default instead of 'window' + } else { + target = JSEvents.findEventTarget(target); + } + + var handlerFunc = function(event) { + var e = event || window.event; + + JSEvents.fillPointerlockChangeEventData(JSEvents.pointerlockChangeEvent, e); + + var shouldCancel = Runtime.dynCall('iiii', callbackfunc, [eventTypeId, JSEvents.pointerlockChangeEvent, userData]); + if (shouldCancel) { + e.preventDefault(); + } + }; + + var eventHandler = { + target: target, + allowsDeferredCalls: false, + eventTypeString: eventTypeString, + callbackfunc: callbackfunc, + handlerFunc: handlerFunc, + useCapture: useCapture + }; + JSEvents.registerOrRemoveHandler(eventHandler); + }, + + requestPointerLock: function(target) { + if (target.requestPointerLock) { + target.requestPointerLock(); + } else if (target.mozRequestPointerLock) { + target.mozRequestPointerLock(); + } else if (target.webkitRequestPointerLock) { + target.webkitRequestPointerLock(); + } else if (target.msRequestPointerLock) { + target.msRequestPointerLock(); + } else { + // document.body is known to accept pointer lock, so use that to differentiate if the user passed a bad element, + // or if the whole browser just doesn't support the feature. + if (document.body.requestPointerLock || document.body.mozRequestPointerLock || document.body.webkitRequestPointerLock || document.body.msRequestPointerLock) { + return {{{ cDefine('EMSCRIPTEN_RESULT_INVALID_TARGET') }}}; + } else { + return {{{ cDefine('EMSCRIPTEN_RESULT_NOT_SUPPORTED') }}}; + } + } + return {{{ cDefine('EMSCRIPTEN_RESULT_SUCCESS') }}}; + }, + + fillVisibilityChangeEventData: function(eventStruct, e) { + var visibilityStates = [ "hidden", "visible", "prerender", "unloaded" ]; + var visibilityState = visibilityStates.indexOf(document.visibilityState); + + {{{ makeSetValue('eventStruct', C_STRUCTS.EmscriptenVisibilityChangeEvent.hidden, 'document.hidden', 'i32') }}}; + {{{ makeSetValue('eventStruct', C_STRUCTS.EmscriptenVisibilityChangeEvent.visibilityState, 'visibilityState', 'i32') }}}; + }, + + registerVisibilityChangeEventCallback: function(target, userData, useCapture, callbackfunc, eventTypeId, eventTypeString) { + if (!JSEvents.visibilityChangeEvent) { + JSEvents.visibilityChangeEvent = _malloc( {{{ C_STRUCTS.EmscriptenVisibilityChangeEvent.__size__ }}} ); + } + + if (!target) { + target = document; // Visibility change events need to be captured from 'document' by default instead of 'window' + } else { + target = JSEvents.findEventTarget(target); + } + + var handlerFunc = function(event) { + var e = event || window.event; + + JSEvents.fillVisibilityChangeEventData(JSEvents.visibilityChangeEvent, e); + + var shouldCancel = Runtime.dynCall('iiii', callbackfunc, [eventTypeId, JSEvents.visibilityChangeEvent, userData]); + if (shouldCancel) { + e.preventDefault(); + } + }; + + var eventHandler = { + target: target, + allowsDeferredCalls: false, + eventTypeString: eventTypeString, + callbackfunc: callbackfunc, + handlerFunc: handlerFunc, + useCapture: useCapture + }; + JSEvents.registerOrRemoveHandler(eventHandler); + }, + + registerTouchEventCallback: function(target, userData, useCapture, callbackfunc, eventTypeId, eventTypeString) { + if (!JSEvents.touchEvent) { + JSEvents.touchEvent = _malloc( {{{ C_STRUCTS.EmscriptenTouchEvent.__size__ }}} ); + } + + var handlerFunc = function(event) { + var e = event || window.event; + + var touches = {}; + for(var i = 0; i < e.touches.length; ++i) { + var touch = e.touches[i]; + touches[touch.identifier] = touch; + } + for(var i = 0; i < e.changedTouches.length; ++i) { + var touch = e.changedTouches[i]; + touches[touch.identifier] = touch; + touch.changed = true; + } + for(var i = 0; i < e.targetTouches.length; ++i) { + var touch = e.targetTouches[i]; + touches[touch.identifier].onTarget = true; + } + + var ptr = JSEvents.touchEvent; + {{{ makeSetValue('ptr', C_STRUCTS.EmscriptenTouchEvent.ctrlKey, 'e.ctrlKey', 'i32') }}}; + {{{ makeSetValue('ptr', C_STRUCTS.EmscriptenTouchEvent.shiftKey, 'e.shiftKey', 'i32') }}}; + {{{ makeSetValue('ptr', C_STRUCTS.EmscriptenTouchEvent.altKey, 'e.altKey', 'i32') }}}; + {{{ makeSetValue('ptr', C_STRUCTS.EmscriptenTouchEvent.metaKey, 'e.metaKey', 'i32') }}}; + ptr += {{{ C_STRUCTS.EmscriptenTouchEvent.touches }}}; // Advance to the start of the touch array. + var rect = Module['canvas'].getBoundingClientRect(); + var numTouches = 0; + for(var i in touches) { + var t = touches[i]; + {{{ makeSetValue('ptr', C_STRUCTS.EmscriptenTouchPoint.identifier, 't.identifier', 'i32') }}}; + {{{ makeSetValue('ptr', C_STRUCTS.EmscriptenTouchPoint.screenX, 't.screenX', 'i32') }}}; + {{{ makeSetValue('ptr', C_STRUCTS.EmscriptenTouchPoint.screenY, 't.screenY', 'i32') }}}; + {{{ makeSetValue('ptr', C_STRUCTS.EmscriptenTouchPoint.clientX, 't.clientX', 'i32') }}}; + {{{ makeSetValue('ptr', C_STRUCTS.EmscriptenTouchPoint.clientY, 't.clientY', 'i32') }}}; + {{{ makeSetValue('ptr', C_STRUCTS.EmscriptenTouchPoint.pageX, 't.pageX', 'i32') }}}; + {{{ makeSetValue('ptr', C_STRUCTS.EmscriptenTouchPoint.pageY, 't.pageY', 'i32') }}}; + {{{ makeSetValue('ptr', C_STRUCTS.EmscriptenTouchPoint.isChanged, 't.changed', 'i32') }}}; + {{{ makeSetValue('ptr', C_STRUCTS.EmscriptenTouchPoint.onTarget, 't.onTarget', 'i32') }}}; + {{{ makeSetValue('ptr', C_STRUCTS.EmscriptenTouchPoint.canvasX, 't.clientX - rect.left', 'i32') }}}; + {{{ makeSetValue('ptr', C_STRUCTS.EmscriptenTouchPoint.canvasY, 't.clientY - rect.top', 'i32') }}}; + ptr += {{{ C_STRUCTS.EmscriptenTouchPoint.__size__ }}}; + + if (++numTouches >= 32) { + break; + } + } + {{{ makeSetValue('JSEvents.touchEvent', C_STRUCTS.EmscriptenTouchEvent.numTouches, 'numTouches', 'i32') }}}; + + var shouldCancel = Runtime.dynCall('iiii', callbackfunc, [eventTypeId, JSEvents.touchEvent, userData]); + if (shouldCancel) { + e.preventDefault(); + } + }; + + var eventHandler = { + target: JSEvents.findEventTarget(target), + allowsDeferredCalls: false, // XXX Currently disabled, see bug https://bugzilla.mozilla.org/show_bug.cgi?id=966493 + // Once the above bug is resolved, enable the following condition if possible: + // allowsDeferredCalls: eventTypeString == 'touchstart', + eventTypeString: eventTypeString, + callbackfunc: callbackfunc, + handlerFunc: handlerFunc, + useCapture: useCapture + }; + JSEvents.registerOrRemoveHandler(eventHandler); + }, + + fillGamepadEventData: function(eventStruct, e) { + {{{ makeSetValue('eventStruct', C_STRUCTS.EmscriptenGamepadEvent.timestamp, 'e.timestamp', 'double') }}}; + for(var i = 0; i < e.axes.length; ++i) { + {{{ makeSetValue('eventStruct+i*8', C_STRUCTS.EmscriptenGamepadEvent.axis, 'e.axes[i]', 'double') }}}; + } + for(var i = 0; i < e.buttons.length; ++i) { + {{{ makeSetValue('eventStruct+i*8', C_STRUCTS.EmscriptenGamepadEvent.analogButton, 'e.buttons[i].value', 'double') }}}; + } + for(var i = 0; i < e.buttons.length; ++i) { + {{{ makeSetValue('eventStruct+i*4', C_STRUCTS.EmscriptenGamepadEvent.digitalButton, 'e.buttons[i].pressed', 'i32') }}}; + } + {{{ makeSetValue('eventStruct', C_STRUCTS.EmscriptenGamepadEvent.connected, 'e.connected', 'i32') }}}; + {{{ makeSetValue('eventStruct', C_STRUCTS.EmscriptenGamepadEvent.index, 'e.index', 'i32') }}}; + {{{ makeSetValue('eventStruct', C_STRUCTS.EmscriptenGamepadEvent.numAxes, 'e.axes.length', 'i32') }}}; + {{{ makeSetValue('eventStruct', C_STRUCTS.EmscriptenGamepadEvent.numButtons, 'e.buttons.length', 'i32') }}}; + writeStringToMemory(e.id, eventStruct + {{{ C_STRUCTS.EmscriptenGamepadEvent.id }}} ); + writeStringToMemory(e.mapping, eventStruct + {{{ C_STRUCTS.EmscriptenGamepadEvent.mapping }}} ); + }, + + registerGamepadEventCallback: function(target, userData, useCapture, callbackfunc, eventTypeId, eventTypeString) { + if (!JSEvents.gamepadEvent) { + JSEvents.gamepadEvent = _malloc( {{{ C_STRUCTS.EmscriptenGamepadEvent.__size__ }}} ); + } + + var handlerFunc = function(event) { + var e = event || window.event; + + JSEvents.fillGamepadEventData(JSEvents.gamepadEvent, e.gamepad); + + var shouldCancel = Runtime.dynCall('iiii', callbackfunc, [eventTypeId, JSEvents.gamepadEvent, userData]); + if (shouldCancel) { + e.preventDefault(); + } + }; + + var eventHandler = { + target: JSEvents.findEventTarget(target), + allowsDeferredCalls: true, + eventTypeString: eventTypeString, + callbackfunc: callbackfunc, + handlerFunc: handlerFunc, + useCapture: useCapture + }; + JSEvents.registerOrRemoveHandler(eventHandler); + }, + + registerBeforeUnloadEventCallback: function(target, userData, useCapture, callbackfunc, eventTypeId, eventTypeString) { + var handlerFunc = function(event) { + var e = event || window.event; + + var confirmationMessage = Runtime.dynCall('iiii', callbackfunc, [eventTypeId, 0, userData]); + + if (confirmationMessage) { + confirmationMessage = Pointer_stringify(confirmationMessage); + } + if (confirmationMessage) { + e.preventDefault(); + e.returnValue = confirmationMessage; + return confirmationMessage; + } + }; + + var eventHandler = { + target: JSEvents.findEventTarget(target), + allowsDeferredCalls: false, + eventTypeString: eventTypeString, + callbackfunc: callbackfunc, + handlerFunc: handlerFunc, + useCapture: useCapture + }; + JSEvents.registerOrRemoveHandler(eventHandler); + }, + + battery: function() { return navigator.battery || navigator.mozBattery || navigator.webkitBattery; }, + + fillBatteryEventData: function(eventStruct, e) { + {{{ makeSetValue('eventStruct', C_STRUCTS.EmscriptenBatteryEvent.chargingTime, 'e.chargingTime', 'double') }}}; + {{{ makeSetValue('eventStruct', C_STRUCTS.EmscriptenBatteryEvent.dischargingTime, 'e.dischargingTime', 'double') }}}; + {{{ makeSetValue('eventStruct', C_STRUCTS.EmscriptenBatteryEvent.level, 'e.level', 'double') }}}; + {{{ makeSetValue('eventStruct', C_STRUCTS.EmscriptenBatteryEvent.charging, 'e.charging', 'i32') }}}; + }, + + registerBatteryEventCallback: function(target, userData, useCapture, callbackfunc, eventTypeId, eventTypeString) { + if (!JSEvents.batteryEvent) { + JSEvents.batteryEvent = _malloc( {{{ C_STRUCTS.EmscriptenBatteryEvent.__size__ }}} ); + } + + var handlerFunc = function(event) { + var e = event || window.event; + + JSEvents.fillBatteryEventData(JSEvents.batteryEvent, JSEvents.battery()); + + var shouldCancel = Runtime.dynCall('iiii', callbackfunc, [eventTypeId, JSEvents.batteryEvent, userData]); + if (shouldCancel) { + e.preventDefault(); + } + }; + + var eventHandler = { + target: JSEvents.findEventTarget(target), + allowsDeferredCalls: false, + eventTypeString: eventTypeString, + callbackfunc: callbackfunc, + handlerFunc: handlerFunc, + useCapture: useCapture + }; + JSEvents.registerOrRemoveHandler(eventHandler); + }, + + registerWebGlEventCallback: function(target, userData, useCapture, callbackfunc, eventTypeId, eventTypeString) { + if (!target) { + target = Module['canvas']; + } + var handlerFunc = function(event) { + var e = event || window.event; + + var shouldCancel = Runtime.dynCall('iiii', callbackfunc, [eventTypeId, 0, userData]); + if (shouldCancel) { + e.preventDefault(); + } + }; + + var eventHandler = { + target: JSEvents.findEventTarget(target), + allowsDeferredCalls: false, + eventTypeString: eventTypeString, + callbackfunc: callbackfunc, + handlerFunc: handlerFunc, + useCapture: useCapture + }; + JSEvents.registerOrRemoveHandler(eventHandler); + }, + }, + + emscripten_set_keypress_callback: function(target, userData, useCapture, callbackfunc) { + JSEvents.registerKeyEventCallback(target, userData, useCapture, callbackfunc, {{{ cDefine('EMSCRIPTEN_EVENT_KEYPRESS') }}}, "keypress"); + return {{{ cDefine('EMSCRIPTEN_RESULT_SUCCESS') }}}; + }, + + emscripten_set_keydown_callback: function(target, userData, useCapture, callbackfunc) { + JSEvents.registerKeyEventCallback(target, userData, useCapture, callbackfunc, {{{ cDefine('EMSCRIPTEN_EVENT_KEYDOWN') }}}, "keydown"); + return {{{ cDefine('EMSCRIPTEN_RESULT_SUCCESS') }}}; + }, + + emscripten_set_keyup_callback: function(target, userData, useCapture, callbackfunc) { + JSEvents.registerKeyEventCallback(target, userData, useCapture, callbackfunc, {{{ cDefine('EMSCRIPTEN_EVENT_KEYUP') }}}, "keyup"); + return {{{ cDefine('EMSCRIPTEN_RESULT_SUCCESS') }}}; + }, + + emscripten_set_click_callback: function(target, userData, useCapture, callbackfunc) { + JSEvents.registerMouseEventCallback(target, userData, useCapture, callbackfunc, {{{ cDefine('EMSCRIPTEN_EVENT_CLICK') }}}, "click"); + return {{{ cDefine('EMSCRIPTEN_RESULT_SUCCESS') }}}; + }, + + emscripten_set_mousedown_callback: function(target, userData, useCapture, callbackfunc) { + JSEvents.registerMouseEventCallback(target, userData, useCapture, callbackfunc, {{{ cDefine('EMSCRIPTEN_EVENT_MOUSEDOWN') }}}, "mousedown"); + return {{{ cDefine('EMSCRIPTEN_RESULT_SUCCESS') }}}; + }, + + emscripten_set_mouseup_callback: function(target, userData, useCapture, callbackfunc) { + JSEvents.registerMouseEventCallback(target, userData, useCapture, callbackfunc, {{{ cDefine('EMSCRIPTEN_EVENT_MOUSEUP') }}}, "mouseup"); + return {{{ cDefine('EMSCRIPTEN_RESULT_SUCCESS') }}}; + }, + + emscripten_set_dblclick_callback: function(target, userData, useCapture, callbackfunc) { + JSEvents.registerMouseEventCallback(target, userData, useCapture, callbackfunc, {{{ cDefine('EMSCRIPTEN_EVENT_DBLCLICK') }}}, "dblclick"); + return {{{ cDefine('EMSCRIPTEN_RESULT_SUCCESS') }}}; + }, + + emscripten_set_mousemove_callback: function(target, userData, useCapture, callbackfunc) { + JSEvents.registerMouseEventCallback(target, userData, useCapture, callbackfunc, {{{ cDefine('EMSCRIPTEN_EVENT_MOUSEMOVE') }}}, "mousemove"); + return {{{ cDefine('EMSCRIPTEN_RESULT_SUCCESS') }}}; + }, + + emscripten_get_mouse_status: function(mouseState) { + if (!JSEvents.mouseEvent) return {{{ cDefine('EMSCRIPTEN_RESULT_NO_DATA') }}}; + // HTML5 does not really have a polling API for mouse events, so implement one manually by + // returning the data from the most recently received event. This requires that user has registered + // at least some no-op function as an event handler to any of the mouse function. + HEAP32.set(HEAP32.subarray(JSEvents.mouseEvent, {{{ C_STRUCTS.EmscriptenMouseEvent.__size__ }}}), mouseState); + return {{{ cDefine('EMSCRIPTEN_RESULT_SUCCESS') }}}; + }, + + emscripten_set_wheel_callback: function(target, userData, useCapture, callbackfunc) { + target = JSEvents.findEventTarget(target); + if (typeof target.onwheel !== 'undefined') { + JSEvents.registerWheelEventCallback(target, userData, useCapture, callbackfunc, {{{ cDefine('EMSCRIPTEN_EVENT_WHEEL') }}}, "wheel"); + return {{{ cDefine('EMSCRIPTEN_RESULT_SUCCESS') }}}; + } else if (typeof target.onmousewheel !== 'undefined') { + JSEvents.registerWheelEventCallback(target, userData, useCapture, callbackfunc, {{{ cDefine('EMSCRIPTEN_EVENT_WHEEL') }}}, "mousewheel"); + return {{{ cDefine('EMSCRIPTEN_RESULT_SUCCESS') }}}; + } else { + return {{{ cDefine('EMSCRIPTEN_RESULT_NOT_SUPPORTED') }}}; + } + }, + + emscripten_set_resize_callback: function(target, userData, useCapture, callbackfunc) { + JSEvents.registerUiEventCallback(target, userData, useCapture, callbackfunc, {{{ cDefine('EMSCRIPTEN_EVENT_RESIZE') }}}, "resize"); + return {{{ cDefine('EMSCRIPTEN_RESULT_SUCCESS') }}}; + }, + + emscripten_set_scroll_callback: function(target, userData, useCapture, callbackfunc) { + JSEvents.registerUiEventCallback(target, userData, useCapture, callbackfunc, {{{ cDefine('EMSCRIPTEN_EVENT_SCROLL') }}}, "scroll"); + return {{{ cDefine('EMSCRIPTEN_RESULT_SUCCESS') }}}; + }, + + emscripten_set_blur_callback: function(target, userData, useCapture, callbackfunc) { + JSEvents.registerFocusEventCallback(target, userData, useCapture, callbackfunc, {{{ cDefine('EMSCRIPTEN_EVENT_BLUR') }}}, "blur"); + return {{{ cDefine('EMSCRIPTEN_RESULT_SUCCESS') }}}; + }, + + emscripten_set_focus_callback: function(target, userData, useCapture, callbackfunc) { + JSEvents.registerFocusEventCallback(target, userData, useCapture, callbackfunc, {{{ cDefine('EMSCRIPTEN_EVENT_FOCUS') }}}, "focus"); + return {{{ cDefine('EMSCRIPTEN_RESULT_SUCCESS') }}}; + }, + + emscripten_set_focusin_callback: function(target, userData, useCapture, callbackfunc) { + JSEvents.registerFocusEventCallback(target, userData, useCapture, callbackfunc, {{{ cDefine('EMSCRIPTEN_EVENT_FOCUSIN') }}}, "focusin"); + return {{{ cDefine('EMSCRIPTEN_RESULT_SUCCESS') }}}; + }, + + emscripten_set_focusout_callback: function(target, userData, useCapture, callbackfunc) { + JSEvents.registerFocusEventCallback(target, userData, useCapture, callbackfunc, {{{ cDefine('EMSCRIPTEN_EVENT_FOCUSOUT') }}}, "focusout"); + return {{{ cDefine('EMSCRIPTEN_RESULT_SUCCESS') }}}; + }, + + emscripten_set_deviceorientation_callback: function(userData, useCapture, callbackfunc) { + JSEvents.registerDeviceOrientationEventCallback(window, userData, useCapture, callbackfunc, {{{ cDefine('EMSCRIPTEN_EVENT_DEVICEORIENTATION') }}}, "deviceorientation"); + return {{{ cDefine('EMSCRIPTEN_RESULT_SUCCESS') }}}; + }, + + emscripten_get_deviceorientation_status: function(orientationState) { + if (!JSEvents.deviceOrientationEvent) return {{{ cDefine('EMSCRIPTEN_RESULT_NO_DATA') }}}; + // HTML5 does not really have a polling API for device orientation events, so implement one manually by + // returning the data from the most recently received event. This requires that user has registered + // at least some no-op function as an event handler. + HEAP32.set(HEAP32.subarray(JSEvents.deviceOrientationEvent, {{{ C_STRUCTS.EmscriptenDeviceOrientationEvent.__size__ }}}), orientationState); + return {{{ cDefine('EMSCRIPTEN_RESULT_SUCCESS') }}}; + }, + + emscripten_set_devicemotion_callback: function(userData, useCapture, callbackfunc) { + JSEvents.registerDeviceMotionEventCallback(window, userData, useCapture, callbackfunc, {{{ cDefine('EMSCRIPTEN_EVENT_DEVICEMOTION') }}}, "devicemotion"); + return {{{ cDefine('EMSCRIPTEN_RESULT_SUCCESS') }}}; + }, + + emscripten_get_devicemotion_status: function(motionState) { + if (!JSEvents.deviceMotionEvent) return {{{ cDefine('EMSCRIPTEN_RESULT_NO_DATA') }}}; + // HTML5 does not really have a polling API for device motion events, so implement one manually by + // returning the data from the most recently received event. This requires that user has registered + // at least some no-op function as an event handler. + HEAP32.set(HEAP32.subarray(JSEvents.deviceMotionEvent, {{{ C_STRUCTS.EmscriptenDeviceMotionEvent.__size__ }}}), motionState); + return {{{ cDefine('EMSCRIPTEN_RESULT_SUCCESS') }}}; + }, + + emscripten_set_orientationchange_callback: function(userData, useCapture, callbackfunc) { + if (!window.screen || !window.screen.addEventListener) return {{{ cDefine('EMSCRIPTEN_RESULT_NOT_SUPPORTED') }}}; + JSEvents.registerOrientationChangeEventCallback(window.screen, userData, useCapture, callbackfunc, {{{ cDefine('EMSCRIPTEN_EVENT_ORIENTATIONCHANGE') }}}, "orientationchange"); + return {{{ cDefine('EMSCRIPTEN_RESULT_SUCCESS') }}}; + }, + + emscripten_get_orientation_status: function(orientationChangeEvent) { + if (!JSEvents.screenOrientation() && typeof window.orientation === 'undefined') return {{{ cDefine('EMSCRIPTEN_RESULT_NOT_SUPPORTED') }}}; + JSEvents.fillOrientationChangeEventData(orientationChangeEvent); + return {{{ cDefine('EMSCRIPTEN_RESULT_SUCCESS') }}}; + }, + + emscripten_lock_orientation: function(allowedOrientations) { + var orientations = []; + if (allowedOrientations & 1) orientations.push("portrait-primary"); + if (allowedOrientations & 2) orientations.push("portrait-secondary"); + if (allowedOrientations & 4) orientations.push("landscape-primary"); + if (allowedOrientations & 8) orientations.push("landscape-secondary"); + var succeeded; + if (window.screen.lockOrientation) { + succeeded = window.screen.lockOrientation(orientations); + } else if (window.screen.mozLockOrientation) { + succeeded = window.screen.mozLockOrientation(orientations); + } else if (window.screen.webkitLockOrientation) { + succeeded = window.screen.webkitLockOrientation(orientations); + } else if (window.screen.msLockOrientation) { + succeeded = window.screen.msLockOrientation(orientations); + } else { + return {{{ cDefine('EMSCRIPTEN_RESULT_NOT_SUPPORTED') }}}; + } + if (succeeded) { + return {{{ cDefine('EMSCRIPTEN_RESULT_SUCCESS') }}}; + } else { + return {{{ cDefine('EMSCRIPTEN_RESULT_FAILED') }}}; + } + }, + + emscripten_unlock_orientation: function() { + if (window.screen.unlockOrientation) { + window.screen.unlockOrientation(); + } else if (window.screen.mozUnlockOrientation) { + window.screen.mozUnlockOrientation(); + } else if (window.screen.webkitUnlockOrientation) { + window.screen.webkitUnlockOrientation(); + } else if (window.screen.msUnlockOrientation) { + window.screen.msUnlockOrientation(); + } else { + return {{{ cDefine('EMSCRIPTEN_RESULT_NOT_SUPPORTED') }}}; + } + return {{{ cDefine('EMSCRIPTEN_RESULT_SUCCESS') }}}; + }, + + emscripten_set_fullscreenchange_callback: function(target, userData, useCapture, callbackfunc) { + if (typeof JSEvents.fullscreenEnabled() === 'undefined') return {{{ cDefine('EMSCRIPTEN_RESULT_NOT_SUPPORTED') }}}; + if (!target) target = document; + else { + target = JSEvents.findEventTarget(target); + if (!target) return {{{ cDefine('EMSCRIPTEN_RESULT_UNKNOWN_TARGET') }}}; + } + JSEvents.registerFullscreenChangeEventCallback(target, userData, useCapture, callbackfunc, {{{ cDefine('EMSCRIPTEN_EVENT_FULLSCREENCHANGE') }}}, "fullscreenchange"); + JSEvents.registerFullscreenChangeEventCallback(target, userData, useCapture, callbackfunc, {{{ cDefine('EMSCRIPTEN_EVENT_FULLSCREENCHANGE') }}}, "mozfullscreenchange"); + JSEvents.registerFullscreenChangeEventCallback(target, userData, useCapture, callbackfunc, {{{ cDefine('EMSCRIPTEN_EVENT_FULLSCREENCHANGE') }}}, "webkitfullscreenchange"); + JSEvents.registerFullscreenChangeEventCallback(target, userData, useCapture, callbackfunc, {{{ cDefine('EMSCRIPTEN_EVENT_FULLSCREENCHANGE') }}}, "msfullscreenchange"); + return {{{ cDefine('EMSCRIPTEN_RESULT_SUCCESS') }}}; + }, + + emscripten_get_fullscreen_status: function(fullscreenStatus) { + if (typeof JSEvents.fullscreenEnabled() === 'undefined') return {{{ cDefine('EMSCRIPTEN_RESULT_NOT_SUPPORTED') }}}; + JSEvents.fillFullscreenChangeEventData(fullscreenStatus); + return {{{ cDefine('EMSCRIPTEN_RESULT_SUCCESS') }}}; + }, + + // https://developer.mozilla.org/en-US/docs/Web/Guide/API/DOM/Using_full_screen_mode + emscripten_request_fullscreen: function(target, deferUntilInEventHandler) { + if (typeof JSEvents.fullscreenEnabled() === 'undefined') return {{{ cDefine('EMSCRIPTEN_RESULT_NOT_SUPPORTED') }}}; + if (!JSEvents.fullscreenEnabled()) return {{{ cDefine('EMSCRIPTEN_RESULT_INVALID_TARGET') }}}; + if (!target) target = '#canvas'; + target = JSEvents.findEventTarget(target); + if (!target) return {{{ cDefine('EMSCRIPTEN_RESULT_UNKNOWN_TARGET') }}}; + + if (!target.requestFullscreen && !target.msRequestFullscreen && !target.mozRequestFullScreen && !target.mozRequestFullscreen && !target.webkitRequestFullscreen) { + return {{{ cDefine('EMSCRIPTEN_RESULT_INVALID_TARGET') }}}; + } + + var canPerformRequests = JSEvents.canPerformEventHandlerRequests(); + + // Queue this function call if we're not currently in an event handler and the user saw it appropriate to do so. + if (!canPerformRequests) { + if (deferUntilInEventHandler) { + JSEvents.deferCall(JSEvents.requestFullscreen, 1 /* priority over pointer lock */, [target]); + return {{{ cDefine('EMSCRIPTEN_RESULT_DEFERRED') }}}; + } else { + return {{{ cDefine('EMSCRIPTEN_RESULT_FAILED_NOT_DEFERRED') }}}; + } + } + + return JSEvents.requestFullscreen(target); + }, + + emscripten_exit_fullscreen: function() { + if (typeof JSEvents.fullscreenEnabled() === 'undefined') return {{{ cDefine('EMSCRIPTEN_RESULT_NOT_SUPPORTED') }}}; + // Make sure no queued up calls will fire after this. + JSEvents.removeDeferredCalls(JSEvents.requestFullscreen); + + if (document.exitFullscreen) { + document.exitFullscreen(); + } else if (document.msExitFullscreen) { + document.msExitFullscreen(); + } else if (document.mozCancelFullScreen) { + document.mozCancelFullScreen(); + } else if (document.webkitExitFullscreen) { + document.webkitExitFullscreen(); + } else { + return {{{ cDefine('EMSCRIPTEN_RESULT_NOT_SUPPORTED') }}}; + } + return {{{ cDefine('EMSCRIPTEN_RESULT_SUCCESS') }}}; + }, + + emscripten_set_pointerlockchange_callback: function(target, userData, useCapture, callbackfunc) { + if (!document.body.requestPointerLock && !document.body.mozRequestPointerLock && !document.body.webkitRequestPointerLock && !document.body.msRequestPointerLock) { + return {{{ cDefine('EMSCRIPTEN_RESULT_NOT_SUPPORTED') }}}; + } + if (!target) target = document; + else { + target = JSEvents.findEventTarget(target); + if (!target) return {{{ cDefine('EMSCRIPTEN_RESULT_UNKNOWN_TARGET') }}}; + } + JSEvents.registerPointerlockChangeEventCallback(target, userData, useCapture, callbackfunc, {{{ cDefine('EMSCRIPTEN_EVENT_POINTERLOCKCHANGE') }}}, "pointerlockchange"); + JSEvents.registerPointerlockChangeEventCallback(target, userData, useCapture, callbackfunc, {{{ cDefine('EMSCRIPTEN_EVENT_POINTERLOCKCHANGE') }}}, "mozpointerlockchange"); + JSEvents.registerPointerlockChangeEventCallback(target, userData, useCapture, callbackfunc, {{{ cDefine('EMSCRIPTEN_EVENT_POINTERLOCKCHANGE') }}}, "webkitpointerlockchange"); + JSEvents.registerPointerlockChangeEventCallback(target, userData, useCapture, callbackfunc, {{{ cDefine('EMSCRIPTEN_EVENT_POINTERLOCKCHANGE') }}}, "mspointerlockchange"); + return {{{ cDefine('EMSCRIPTEN_RESULT_SUCCESS') }}}; + }, + + emscripten_get_pointerlock_status: function(pointerlockStatus) { + if (!document.body.requestPointerLock && !document.body.mozRequestPointerLock && !document.body.webkitRequestPointerLock && !document.body.msRequestPointerLock) { + return {{{ cDefine('EMSCRIPTEN_RESULT_NOT_SUPPORTED') }}}; + } + JSEvents.fillPointerlockChangeEventData(pointerlockStatus); + return {{{ cDefine('EMSCRIPTEN_RESULT_SUCCESS') }}}; + }, + + emscripten_request_pointerlock: function(target, deferUntilInEventHandler) { + if (!target) target = '#canvas'; + target = JSEvents.findEventTarget(target); + if (!target) return {{{ cDefine('EMSCRIPTEN_RESULT_UNKNOWN_TARGET') }}}; + if (!target.requestPointerLock && !target.mozRequestPointerLock && !target.webkitRequestPointerLock && !target.msRequestPointerLock) { + return {{{ cDefine('EMSCRIPTEN_RESULT_NOT_SUPPORTED') }}}; + } + + var canPerformRequests = JSEvents.canPerformEventHandlerRequests(); + + // Queue this function call if we're not currently in an event handler and the user saw it appropriate to do so. + if (!canPerformRequests) { + if (deferUntilInEventHandler) { + JSEvents.deferCall(JSEvents.requestPointerLock, 2 /* priority below fullscreen */, [target]); + return {{{ cDefine('EMSCRIPTEN_RESULT_DEFERRED') }}}; + } else { + return {{{ cDefine('EMSCRIPTEN_RESULT_FAILED_NOT_DEFERRED') }}}; + } + } + + return JSEvents.requestPointerLock(target); + }, + + emscripten_exit_pointerlock: function() { + // Make sure no queued up calls will fire after this. + JSEvents.removeDeferredCalls(JSEvents.requestPointerLock); + + if (document.exitPointerLock) { + document.exitPointerLock(); + } else if (document.msExitPointerLock) { + document.msExitPointerLock(); + } else if (document.mozExitPointerLock) { + document.mozExitPointerLock(); + } else if (document.webkitExitPointerLock) { + document.webkitExitPointerLock(); + } else { + return {{{ cDefine('EMSCRIPTEN_RESULT_NOT_SUPPORTED') }}}; + } + return {{{ cDefine('EMSCRIPTEN_RESULT_SUCCESS') }}}; + }, + + emscripten_vibrate: function(msecs) { + if (!navigator.vibrate) return {{{ cDefine('EMSCRIPTEN_RESULT_NOT_SUPPORTED') }}}; + navigator.vibrate(msecs); + return {{{ cDefine('EMSCRIPTEN_RESULT_SUCCESS') }}}; + }, + + emscripten_vibrate_pattern: function(msecsArray, numEntries) { + if (!navigator.vibrate) return {{{ cDefine('EMSCRIPTEN_RESULT_NOT_SUPPORTED') }}}; + + var vibrateList = []; + for(var i = 0; i < numEntries; ++i) { + var msecs = {{{ makeGetValue('msecsArray', 'i*4', 'i32') }}}; + vibrateList.push(msecs); + } + navigator.vibrate(vibrateList); + return {{{ cDefine('EMSCRIPTEN_RESULT_SUCCESS') }}}; + }, + + emscripten_set_visibilitychange_callback: function(userData, useCapture, callbackfunc) { + JSEvents.registerVisibilityChangeEventCallback(document, userData, useCapture, callbackfunc, {{{ cDefine('EMSCRIPTEN_EVENT_VISIBILITYCHANGE') }}}, "visibilitychange"); + return {{{ cDefine('EMSCRIPTEN_RESULT_SUCCESS') }}}; + }, + + emscripten_get_visibility_status: function(visibilityStatus) { + if (typeof document.visibilityState === 'undefined' && typeof document.hidden === 'undefined') { + return {{{ cDefine('EMSCRIPTEN_RESULT_NOT_SUPPORTED') }}}; + } + JSEvents.fillVisibilityChangeEventData(visibilityStatus); + return {{{ cDefine('EMSCRIPTEN_RESULT_SUCCESS') }}}; + }, + + emscripten_set_touchstart_callback: function(target, userData, useCapture, callbackfunc) { + JSEvents.registerTouchEventCallback(target, userData, useCapture, callbackfunc, {{{ cDefine('EMSCRIPTEN_EVENT_TOUCHSTART') }}}, "touchstart"); + return {{{ cDefine('EMSCRIPTEN_RESULT_SUCCESS') }}}; + }, + + emscripten_set_touchend_callback: function(target, userData, useCapture, callbackfunc) { + JSEvents.registerTouchEventCallback(target, userData, useCapture, callbackfunc, {{{ cDefine('EMSCRIPTEN_EVENT_TOUCHEND') }}}, "touchend"); + return {{{ cDefine('EMSCRIPTEN_RESULT_SUCCESS') }}}; + }, + + emscripten_set_touchmove_callback: function(target, userData, useCapture, callbackfunc) { + JSEvents.registerTouchEventCallback(target, userData, useCapture, callbackfunc, {{{ cDefine('EMSCRIPTEN_EVENT_TOUCHMOVE') }}}, "touchmove"); + return {{{ cDefine('EMSCRIPTEN_RESULT_SUCCESS') }}}; + }, + + emscripten_set_touchcancel_callback: function(target, userData, useCapture, callbackfunc) { + JSEvents.registerTouchEventCallback(target, userData, useCapture, callbackfunc, {{{ cDefine('EMSCRIPTEN_EVENT_TOUCHCANCEL') }}}, "touchcancel"); + return {{{ cDefine('EMSCRIPTEN_RESULT_SUCCESS') }}}; + }, + + emscripten_set_gamepadconnected_callback: function(userData, useCapture, callbackfunc) { + if (!navigator.getGamepads) return {{{ cDefine('EMSCRIPTEN_RESULT_NOT_SUPPORTED') }}}; + JSEvents.registerGamepadEventCallback(window, userData, useCapture, callbackfunc, {{{ cDefine('EMSCRIPTEN_EVENT_GAMEPADCONNECTED') }}}, "gamepadconnected"); + return {{{ cDefine('EMSCRIPTEN_RESULT_SUCCESS') }}}; + }, + + emscripten_set_gamepaddisconnected_callback: function(userData, useCapture, callbackfunc) { + if (!navigator.getGamepads) return {{{ cDefine('EMSCRIPTEN_RESULT_NOT_SUPPORTED') }}}; + JSEvents.registerGamepadEventCallback(window, userData, useCapture, callbackfunc, {{{ cDefine('EMSCRIPTEN_EVENT_GAMEPADDISCONNECTED') }}}, "gamepaddisconnected"); + return {{{ cDefine('EMSCRIPTEN_RESULT_SUCCESS') }}}; + }, + + emscripten_get_num_gamepads: function() { + if (!navigator.getGamepads) return {{{ cDefine('EMSCRIPTEN_RESULT_NOT_SUPPORTED') }}}; + return navigator.getGamepads().length; + }, + + emscripten_get_gamepad_status: function(index, gamepadState) { + if (!navigator.getGamepads) return {{{ cDefine('EMSCRIPTEN_RESULT_NOT_SUPPORTED') }}}; + var gamepads = navigator.getGamepads(); + if (index < 0 || index >= gamepads.length) { + return {{{ cDefine('EMSCRIPTEN_RESULT_INVALID_PARAM') }}}; + } + JSEvents.fillGamepadEventData(gamepadState, gamepads[index]); + return {{{ cDefine('EMSCRIPTEN_RESULT_SUCCESS') }}}; + }, + + emscripten_set_beforeunload_callback: function(userData, callbackfunc) { + if (typeof window.onbeforeunload === 'undefined') return {{{ cDefine('EMSCRIPTEN_RESULT_NOT_SUPPORTED') }}}; + JSEvents.registerBeforeUnloadEventCallback(window, userData, true, callbackfunc, {{{ cDefine('EMSCRIPTEN_EVENT_BEFOREUNLOAD') }}}, "beforeunload"); + return {{{ cDefine('EMSCRIPTEN_RESULT_SUCCESS') }}}; + }, + + emscripten_set_batterychargingchange_callback: function(userData, callbackfunc) { + if (!JSEvents.battery()) return {{{ cDefine('EMSCRIPTEN_RESULT_NOT_SUPPORTED') }}}; + JSEvents.registerBatteryEventCallback(JSEvents.battery(), userData, true, callbackfunc, {{{ cDefine('EMSCRIPTEN_EVENT_BATTERYCHARGINGCHANGE') }}}, "chargingchange"); + return {{{ cDefine('EMSCRIPTEN_RESULT_SUCCESS') }}}; + }, + + emscripten_set_batterylevelchange_callback: function(userData, callbackfunc) { + if (!JSEvents.battery()) return {{{ cDefine('EMSCRIPTEN_RESULT_NOT_SUPPORTED') }}}; + JSEvents.registerBatteryEventCallback(JSEvents.battery(), userData, true, callbackfunc, {{{ cDefine('EMSCRIPTEN_EVENT_BATTERYLEVELCHANGE') }}}, "levelchange"); + return {{{ cDefine('EMSCRIPTEN_RESULT_SUCCESS') }}}; + }, + + emscripten_get_battery_status: function(batteryState) { + if (!JSEvents.battery()) return {{{ cDefine('EMSCRIPTEN_RESULT_NOT_SUPPORTED') }}}; + JSEvents.fillBatteryEventData(batteryState, JSEvents.battery()); + return {{{ cDefine('EMSCRIPTEN_RESULT_SUCCESS') }}}; + }, + + emscripten_set_webglcontextlost_callback: function(target, userData, useCapture, callbackfunc) { + JSEvents.registerWebGlEventCallback(target, userData, useCapture, callbackfunc, {{{ cDefine('EMSCRIPTEN_EVENT_WEBGLCONTEXTLOST') }}}, "webglcontextlost"); + return {{{ cDefine('EMSCRIPTEN_RESULT_SUCCESS') }}}; + }, + + emscripten_set_webglcontextrestored_callback: function(target, userData, useCapture, callbackfunc) { + JSEvents.registerWebGlEventCallback(target, userData, useCapture, callbackfunc, {{{ cDefine('EMSCRIPTEN_EVENT_WEBGLCONTEXTRESTORED') }}}, "webglcontextrestored"); + return {{{ cDefine('EMSCRIPTEN_RESULT_SUCCESS') }}}; + }, +}; + +autoAddDeps(LibraryJSEvents, '$JSEvents'); +mergeInto(LibraryManager.library, LibraryJSEvents); diff --git a/src/library_idbfs.js b/src/library_idbfs.js index 7f50f17e..91015e77 100644 --- a/src/library_idbfs.js +++ b/src/library_idbfs.js @@ -5,14 +5,12 @@ mergeInto(LibraryManager.library, { indexedDB: function() { return window.indexedDB || window.mozIndexedDB || window.webkitIndexedDB || window.msIndexedDB; }, - DB_VERSION: 20, + DB_VERSION: 21, DB_STORE_NAME: 'FILE_DATA', - // reuse all of the core MEMFS functionality mount: function(mount) { + // reuse all of the core MEMFS functionality return MEMFS.mount.apply(null, arguments); }, - // the only custom function IDBFS implements is to handle - // synchronizing the wrapped MEMFS with a backing IDB instance syncfs: function(mount, populate, callback) { IDBFS.getLocalSet(mount, function(err, local) { if (err) return callback(err); @@ -27,103 +25,46 @@ mergeInto(LibraryManager.library, { }); }); }, - reconcile: function(src, dst, callback) { - var total = 0; - - var create = {}; - for (var key in src.files) { - if (!src.files.hasOwnProperty(key)) continue; - var e = src.files[key]; - var e2 = dst.files[key]; - if (!e2 || e.timestamp > e2.timestamp) { - create[key] = e; - total++; - } - } - - var remove = {}; - for (var key in dst.files) { - if (!dst.files.hasOwnProperty(key)) continue; - var e = dst.files[key]; - var e2 = src.files[key]; - if (!e2) { - remove[key] = e; - total++; - } + getDB: function(name, callback) { + // check the cache first + var db = IDBFS.dbs[name]; + if (db) { + return callback(null, db); } - if (!total) { - // early out - return callback(null); + var req; + try { + req = IDBFS.indexedDB().open(name, IDBFS.DB_VERSION); + } catch (e) { + return callback(e); } + req.onupgradeneeded = function(e) { + var db = e.target.result; + var transaction = e.target.transaction; - var completed = 0; - function done(err) { - if (err) return callback(err); - if (++completed >= total) { - return callback(null); - } - }; - - // create a single transaction to handle and IDB reads / writes we'll need to do - var db = src.type === 'remote' ? src.db : dst.db; - var transaction = db.transaction([IDBFS.DB_STORE_NAME], 'readwrite'); - transaction.onerror = function transaction_onerror() { callback(this.error); }; - var store = transaction.objectStore(IDBFS.DB_STORE_NAME); - - for (var path in create) { - if (!create.hasOwnProperty(path)) continue; - var entry = create[path]; + var fileStore; - if (dst.type === 'local') { - // save file to local - try { - if (FS.isDir(entry.mode)) { - FS.mkdir(path, entry.mode); - } else if (FS.isFile(entry.mode)) { - var stream = FS.open(path, 'w+', 0666); - FS.write(stream, entry.contents, 0, entry.contents.length, 0, true /* canOwn */); - FS.close(stream); - } - done(null); - } catch (e) { - return done(e); - } + if (db.objectStoreNames.contains(IDBFS.DB_STORE_NAME)) { + fileStore = transaction.objectStore(IDBFS.DB_STORE_NAME); } else { - // save file to IDB - var req = store.put(entry, path); - req.onsuccess = function req_onsuccess() { done(null); }; - req.onerror = function req_onerror() { done(this.error); }; + fileStore = db.createObjectStore(IDBFS.DB_STORE_NAME); } - } - for (var path in remove) { - if (!remove.hasOwnProperty(path)) continue; - var entry = remove[path]; + fileStore.createIndex('timestamp', 'timestamp', { unique: false }); + }; + req.onsuccess = function() { + db = req.result; - if (dst.type === 'local') { - // delete file from local - try { - if (FS.isDir(entry.mode)) { - // TODO recursive delete? - FS.rmdir(path); - } else if (FS.isFile(entry.mode)) { - FS.unlink(path); - } - done(null); - } catch (e) { - return done(e); - } - } else { - // delete file from IDB - var req = store.delete(path); - req.onsuccess = function req_onsuccess() { done(null); }; - req.onerror = function req_onerror() { done(this.error); }; - } - } + // add to the cache + IDBFS.dbs[name] = db; + callback(null, db); + }; + req.onerror = function() { + callback(this.error); + }; }, getLocalSet: function(mount, callback) { - var files = {}; + var entries = {}; function isRealDir(p) { return p !== '.' && p !== '..'; @@ -134,83 +75,192 @@ mergeInto(LibraryManager.library, { } }; - var check = FS.readdir(mount.mountpoint) - .filter(isRealDir) - .map(toAbsolute(mount.mountpoint)); + var check = FS.readdir(mount.mountpoint).filter(isRealDir).map(toAbsolute(mount.mountpoint)); while (check.length) { var path = check.pop(); - var stat, node; + var stat; try { - var lookup = FS.lookupPath(path); - node = lookup.node; stat = FS.stat(path); } catch (e) { return callback(e); } if (FS.isDir(stat.mode)) { - check.push.apply(check, FS.readdir(path) - .filter(isRealDir) - .map(toAbsolute(path))); - - files[path] = { mode: stat.mode, timestamp: stat.mtime }; - } else if (FS.isFile(stat.mode)) { - files[path] = { contents: node.contents, mode: stat.mode, timestamp: stat.mtime }; - } else { - return callback(new Error('node type not supported')); + check.push.apply(check, FS.readdir(path).filter(isRealDir).map(toAbsolute(path))); } - } - return callback(null, { type: 'local', files: files }); - }, - getDB: function(name, callback) { - // look it up in the cache - var db = IDBFS.dbs[name]; - if (db) { - return callback(null, db); - } - var req; - try { - req = IDBFS.indexedDB().open(name, IDBFS.DB_VERSION); - } catch (e) { - return onerror(e); + entries[path] = { timestamp: stat.mtime }; } - req.onupgradeneeded = function req_onupgradeneeded() { - db = req.result; - db.createObjectStore(IDBFS.DB_STORE_NAME); - }; - req.onsuccess = function req_onsuccess() { - db = req.result; - // add to the cache - IDBFS.dbs[name] = db; - callback(null, db); - }; - req.onerror = function req_onerror() { - callback(this.error); - }; + + return callback(null, { type: 'local', entries: entries }); }, getRemoteSet: function(mount, callback) { - var files = {}; + var entries = {}; IDBFS.getDB(mount.mountpoint, function(err, db) { if (err) return callback(err); var transaction = db.transaction([IDBFS.DB_STORE_NAME], 'readonly'); - transaction.onerror = function transaction_onerror() { callback(this.error); }; + transaction.onerror = function() { callback(this.error); }; var store = transaction.objectStore(IDBFS.DB_STORE_NAME); - store.openCursor().onsuccess = function store_openCursor_onsuccess(event) { + var index = store.index('timestamp'); + + index.openKeyCursor().onsuccess = function(event) { var cursor = event.target.result; + if (!cursor) { - return callback(null, { type: 'remote', db: db, files: files }); + return callback(null, { type: 'remote', db: db, entries: entries }); } - files[cursor.key] = cursor.value; + entries[cursor.primaryKey] = { timestamp: cursor.key }; + cursor.continue(); }; }); + }, + loadLocalEntry: function(path, callback) { + var stat, node; + + try { + var lookup = FS.lookupPath(path); + node = lookup.node; + stat = FS.stat(path); + } catch (e) { + return callback(e); + } + + if (FS.isDir(stat.mode)) { + return callback(null, { timestamp: stat.mtime, mode: stat.mode }); + } else if (FS.isFile(stat.mode)) { + return callback(null, { timestamp: stat.mtime, mode: stat.mode, contents: node.contents }); + } else { + return callback(new Error('node type not supported')); + } + }, + storeLocalEntry: function(path, entry, callback) { + try { + if (FS.isDir(entry.mode)) { + FS.mkdir(path, entry.mode); + } else if (FS.isFile(entry.mode)) { + FS.writeFile(path, entry.contents, { encoding: 'binary', canOwn: true }); + } else { + return callback(new Error('node type not supported')); + } + + FS.utime(path, entry.timestamp, entry.timestamp); + } catch (e) { + return callback(e); + } + + callback(null); + }, + removeLocalEntry: function(path, callback) { + try { + var lookup = FS.lookupPath(path); + var stat = FS.stat(path); + + if (FS.isDir(stat.mode)) { + FS.rmdir(path); + } else if (FS.isFile(stat.mode)) { + FS.unlink(path); + } + } catch (e) { + return callback(e); + } + + callback(null); + }, + loadRemoteEntry: function(store, path, callback) { + var req = store.get(path); + req.onsuccess = function(event) { callback(null, event.target.result); }; + req.onerror = function() { callback(this.error); }; + }, + storeRemoteEntry: function(store, path, entry, callback) { + var req = store.put(entry, path); + req.onsuccess = function() { callback(null); }; + req.onerror = function() { callback(this.error); }; + }, + removeRemoteEntry: function(store, path, callback) { + var req = store.delete(path); + req.onsuccess = function() { callback(null); }; + req.onerror = function() { callback(this.error); }; + }, + reconcile: function(src, dst, callback) { + var total = 0; + + var create = []; + Object.keys(src.entries).forEach(function (key) { + var e = src.entries[key]; + var e2 = dst.entries[key]; + if (!e2 || e.timestamp > e2.timestamp) { + create.push(key); + total++; + } + }); + + var remove = []; + Object.keys(dst.entries).forEach(function (key) { + var e = dst.entries[key]; + var e2 = src.entries[key]; + if (!e2) { + remove.push(key); + total++; + } + }); + + if (!total) { + return callback(null); + } + + var errored = false; + var completed = 0; + var db = src.type === 'remote' ? src.db : dst.db; + var transaction = db.transaction([IDBFS.DB_STORE_NAME], 'readwrite'); + var store = transaction.objectStore(IDBFS.DB_STORE_NAME); + + function done(err) { + if (err) { + if (!done.errored) { + done.errored = true; + return callback(err); + } + return; + } + if (++completed >= total) { + return callback(null); + } + }; + + transaction.onerror = function() { done(this.error); }; + + // sort paths in ascending order so directory entries are created + // before the files inside them + create.sort().forEach(function (path) { + if (dst.type === 'local') { + IDBFS.loadRemoteEntry(store, path, function (err, entry) { + if (err) return done(err); + IDBFS.storeLocalEntry(path, entry, done); + }); + } else { + IDBFS.loadLocalEntry(path, function (err, entry) { + if (err) return done(err); + IDBFS.storeRemoteEntry(store, path, entry, done); + }); + } + }); + + // sort paths in descending order so files are deleted before their + // parent directories + remove.sort().reverse().forEach(function(path) { + if (dst.type === 'local') { + IDBFS.removeLocalEntry(path, done); + } else { + IDBFS.removeRemoteEntry(store, path, done); + } + }); } } }); diff --git a/src/library_memfs.js b/src/library_memfs.js index d3148d8b..95c3ae65 100644 --- a/src/library_memfs.js +++ b/src/library_memfs.js @@ -8,7 +8,7 @@ mergeInto(LibraryManager.library, { CONTENT_FLEXIBLE: 2, // has been modified or never set to anything, and is a flexible js array that can grow/shrink CONTENT_FIXED: 3, // contains some fixed-size content written into it, in a typed array mount: function(mount) { - return MEMFS.createNode(null, '/', {{{ cDefine('S_IFDIR') }}} | 0777, 0); + return MEMFS.createNode(null, '/', {{{ cDefine('S_IFDIR') }}} | 511 /* 0777 */, 0); }, createNode: function(parent, name, mode, dev) { if (FS.isBlkdev(mode) || FS.isFIFO(mode)) { @@ -23,7 +23,6 @@ mergeInto(LibraryManager.library, { setattr: MEMFS.node_ops.setattr, lookup: MEMFS.node_ops.lookup, mknod: MEMFS.node_ops.mknod, - mknod: MEMFS.node_ops.mknod, rename: MEMFS.node_ops.rename, unlink: MEMFS.node_ops.unlink, rmdir: MEMFS.node_ops.rmdir, @@ -185,7 +184,7 @@ mergeInto(LibraryManager.library, { return entries; }, symlink: function(parent, newname, oldpath) { - var node = MEMFS.createNode(parent, newname, 0777 | {{{ cDefine('S_IFLNK') }}}, 0); + var node = MEMFS.createNode(parent, newname, 511 /* 0777 */ | {{{ cDefine('S_IFLNK') }}}, 0); node.link = oldpath; return node; }, diff --git a/src/library_openal.js b/src/library_openal.js index eb152f62..58b11607 100644 --- a/src/library_openal.js +++ b/src/library_openal.js @@ -5,6 +5,10 @@ var LibraryOpenAL = { $AL: { contexts: [], currentContext: null, + + stringCache: {}, + alcStringCache: {}, + QUEUE_INTERVAL: 25, QUEUE_LOOKAHEAD: 100, @@ -54,9 +58,21 @@ var LibraryOpenAL = { entry.src = AL.currentContext.ctx.createBufferSource(); entry.src.buffer = entry.buffer; entry.src.connect(src.gain); - entry.src.start(startTime, offset); - + if (typeof(entry.src.start) !== 'undefined') { + entry.src.start(startTime, offset); + } else if (typeof(entry.src.noteOn) !== 'undefined') { + entry.src.noteOn(startTime); +#if OPENAL_DEBUG + if (offset > 0) { + Runtime.warnOnce('The current browser does not support AudioBufferSourceNode.start(when, offset); method, so cannot play back audio with an offset '+offset+' secs! Audio glitches will occur!'); + } +#endif + } #if OPENAL_DEBUG + else { + Runtime.warnOnce('Unable to start AudioBufferSourceNode playback! Not supported by the browser?'); + } + console.log('updateSource queuing buffer ' + i + ' for source ' + idx + ' at ' + startTime + ' (offset by ' + offset + ')'); #endif } @@ -170,15 +186,14 @@ var LibraryOpenAL = { }, alcOpenDevice: function(deviceName) { - if (typeof(AudioContext) == "function" || - typeof(webkitAudioContext) == "function") { + if (typeof(AudioContext) !== "undefined" || + typeof(webkitAudioContext) !== "undefined") { return 1; // non-null pointer -- we just simulate one device } else { return 0; } }, - alcCreateContext__deps: ['updateSources'], alcCreateContext: function(device, attrList) { if (device != 1) { return 0; @@ -201,12 +216,18 @@ var LibraryOpenAL = { } if (ctx) { + // Old Web Audio API (e.g. Safari 6.0.5) had an inconsistently named createGainNode function. + if (typeof(ctx.createGain) === 'undefined') ctx.createGain = ctx.createGainNode; + + var gain = ctx.createGain(); + gain.connect(ctx.destination); var context = { ctx: ctx, err: 0, src: [], buf: [], - interval: setInterval(function() { AL.updateSources(context); }, AL.QUEUE_INTERVAL) + interval: setInterval(function() { AL.updateSources(context); }, AL.QUEUE_INTERVAL), + gain: gain }; AL.contexts.push(context); return AL.contexts.length; @@ -232,6 +253,42 @@ var LibraryOpenAL = { return _alGetError(); }, + alcGetIntegerv: function(device, param, size, data) { + if (size == 0 || !data) { + AL.currentContext.err = 0xA003 /* AL_INVALID_VALUE */; + return; + } + + switch(param) { + case 0x1000 /* ALC_MAJOR_VERSION */: + {{{ makeSetValue('data', '0', '1', 'i32') }}}; + break; + case 0x1001 /* ALC_MINOR_VERSION */: + {{{ makeSetValue('data', '0', '1', 'i32') }}}; + break; + case 0x1002 /* ALC_ATTRIBUTES_SIZE */: + if (!device) { + AL.currentContext.err = 0xA001 /* ALC_INVALID_DEVICE */; + return 0; + } + {{{ makeSetValue('data', '0', '1', 'i32') }}}; + break; + case 0x1003 /* ALC_ALL_ATTRIBUTES */: + if (!device) { + AL.currentContext.err = 0xA001 /* ALC_INVALID_DEVICE */; + return 0; + } + {{{ makeSetValue('data', '0', '0', 'i32') }}}; + break; + default: +#if OPENAL_DEBUG + console.log("alcGetIntegerv with param " + param + " not implemented yet"); +#endif + AL.currentContext.err = 0xA003 /* ALC_INVALID_ENUM */; + break; + } + }, + alDeleteSources: function(count, sources) { if (!AL.currentContext) { #if OPENAL_DEBUG @@ -254,7 +311,7 @@ var LibraryOpenAL = { } for (var i = 0; i < count; ++i) { var gain = AL.currentContext.ctx.createGain(); - gain.connect(AL.currentContext.ctx.destination); + gain.connect(AL.currentContext.gain); AL.currentContext.src.push({ state: 0x1011 /* AL_INITIAL */, queue: [], @@ -294,6 +351,34 @@ var LibraryOpenAL = { this._velocity = val; if (this.panner) this.panner.setVelocity(val[0], val[1], val[2]); }, + get direction() { + return this._direction || [0, 0, 0]; + }, + set direction(val) { + this._direction = val; + if (this.panner) this.panner.setOrientation(val[0], val[1], val[2]); + }, + get coneOuterGain() { + return this._coneOuterGain || 0.0; + }, + set coneOuterGain(val) { + this._coneOuterGain = val; + if (this.panner) this.panner.coneOuterGain = val; + }, + get coneInnerAngle() { + return this._coneInnerAngle || 360.0; + }, + set coneInnerAngle(val) { + this._coneInnerAngle = val; + if (this.panner) this.panner.coneInnerAngle = val; + }, + get coneOuterAngle() { + return this._coneOuterAngle || 360.0; + }, + set coneOuterAngle(val) { + this._coneOuterAngle = val; + if (this.panner) this.panner.coneOuterAngle = val; + }, gain: gain, panner: null, buffersPlayed: 0, @@ -303,7 +388,18 @@ var LibraryOpenAL = { } }, - alSourcei__deps: ['updateSource'], + alIsSource: function(sourceId) { + if (!AL.currentContext) { + return false; + } + + if (!AL.currentContext.src[sourceId - 1]) { + return false; + } else { + return true; + } + }, + alSourcei: function(source, param, value) { if (!AL.currentContext) { #if OPENAL_DEBUG @@ -320,6 +416,12 @@ var LibraryOpenAL = { return; } switch (param) { + case 0x1001 /* AL_CONE_INNER_ANGLE */: + src.coneInnerAngle = value; + break; + case 0x1002 /* AL_CONE_OUTER_ANGLE */: + src.coneOuterAngle = value; + break; case 0x1007 /* AL_LOOPING */: src.loop = (value === 1 /* AL_TRUE */); break; @@ -406,12 +508,15 @@ var LibraryOpenAL = { case 0x1021 /* AL_ROLLOFF_FACTOR */: src.rolloffFactor = value; break; - // case 0x1022 /* AL_CONE_OUTER_GAIN */: - // break; - // case 0x1001 /* AL_CONE_INNER_ANGLE */: - // break; - // case 0x1002 /* AL_CONE_OUTER_ANGLE */: - // break; + case 0x1022 /* AL_CONE_OUTER_GAIN */: + src.coneOuterGain = value; + break; + case 0x1001 /* AL_CONE_INNER_ANGLE */: + src.coneInnerAngle = value; + break; + case 0x1002 /* AL_CONE_OUTER_ANGLE */: + src.coneOuterAngle = value; + break; case 0x1020 /* AL_REFERENCE_DISTANCE */: src.refDistance = value; break; @@ -443,6 +548,9 @@ var LibraryOpenAL = { case 0x1004 /* AL_POSITION */: src.position = [v1, v2, v3]; break; + case 0x1005 /* AL_DIRECTION */: + src.direction = [v1, v2, v3]; + break; case 0x1006 /* AL_VELOCITY */: src.velocity = [v1, v2, v3]; break; @@ -463,7 +571,6 @@ var LibraryOpenAL = { {{{ makeGetValue('value', '8', 'float') }}}); }, - alSourceQueueBuffers__deps: ["updateSource"], alSourceQueueBuffers: function(source, count, buffers) { if (!AL.currentContext) { #if OPENAL_DEBUG @@ -499,7 +606,6 @@ var LibraryOpenAL = { AL.updateSource(src); }, - alSourceUnqueueBuffers__deps: ["updateSource"], alSourceUnqueueBuffers: function(source, count, buffers) { if (!AL.currentContext) { #if OPENAL_DEBUG @@ -594,6 +700,21 @@ var LibraryOpenAL = { } }, + alIsBuffer: function(bufferId) { + if (!AL.currentContext) { + return false; + } + if (bufferId > AL.currentContext.buf.length) { + return false; + } + + if (!AL.currentContext.buf[bufferId - 1]) { + return false; + } else { + return true; + } + }, + alBufferData: function(buffer, format, data, size, freq) { if (!AL.currentContext) { #if OPENAL_DEBUG @@ -633,6 +754,7 @@ var LibraryOpenAL = { } try { AL.currentContext.buf[buffer - 1] = AL.currentContext.ctx.createBuffer(channels, size / (bytes * channels), freq); + AL.currentContext.buf[buffer - 1].bytesPerSample = bytes; } catch (e) { AL.currentContext.err = 0xA003 /* AL_INVALID_VALUE */; return; @@ -657,6 +779,41 @@ var LibraryOpenAL = { } }, + alGetBufferi: function(buffer, param, value) + { + if (!AL.currentContext) { +#if OPENAL_DEBUG + console.error("alGetBufferi called without a valid context"); +#endif + return; + } + var buf = AL.currentContext.buf[buffer - 1]; + if (!buf) { +#if OPENAL_DEBUG + console.error("alGetBufferi called with an invalid buffer"); +#endif + AL.currentContext.err = 0xA001 /* AL_INVALID_NAME */; + return; + } + switch (param) { + case 0x2001 /* AL_FREQUENCY */: + {{{ makeSetValue('value', '0', 'buf.sampleRate', 'i32') }}}; + break; + case 0x2002 /* AL_BITS */: + {{{ makeSetValue('value', '0', 'buf.bytesPerSample * 8', 'i32') }}}; + break; + case 0x2003 /* AL_CHANNELS */: + {{{ makeSetValue('value', '0', 'buf.numberOfChannels', 'i32') }}}; + break; + case 0x2004 /* AL_SIZE */: + {{{ makeSetValue('value', '0', 'buf.length * buf.bytesPerSample * buf.numberOfChannels', 'i32') }}}; + break; + default: + AL.currentContext.err = 0xA002 /* AL_INVALID_ENUM */; + break; + } + }, + alSourcePlay__deps: ['setSourceState'], alSourcePlay: function(source) { if (!AL.currentContext) { @@ -714,7 +871,6 @@ var LibraryOpenAL = { AL.setSourceState(src, 0x1013 /* AL_PAUSED */); }, - alGetSourcei__deps: ['updateSource'], alGetSourcei: function(source, param, value) { if (!AL.currentContext) { #if OPENAL_DEBUG @@ -743,6 +899,12 @@ var LibraryOpenAL = { case 0x202 /* AL_SOURCE_RELATIVE */: {{{ makeSetValue('value', '0', 'src.panner ? 1 : 0', 'i32') }}}; break; + case 0x1001 /* AL_CONE_INNER_ANGLE */: + {{{ makeSetValue('value', '0', 'src.coneInnerAngle', 'i32') }}}; + break; + case 0x1002 /* AL_CONE_OUTER_ANGLE */: + {{{ makeSetValue('value', '0', 'src.coneOuterAngle', 'i32') }}}; + break; case 0x1009 /* AL_BUFFER */: if (!src.queue.length) { {{{ makeSetValue('value', '0', '0', 'i32') }}}; @@ -809,12 +971,15 @@ var LibraryOpenAL = { case 0x1021 /* AL_ROLLOFF_FACTOR */: {{{ makeSetValue('value', '0', 'src.rolloffFactor', 'float') }}} break; - // case 0x1022 /* AL_CONE_OUTER_GAIN */: - // break; - // case 0x1001 /* AL_CONE_INNER_ANGLE */: - // break; - // case 0x1002 /* AL_CONE_OUTER_ANGLE */: - // break; + case 0x1022 /* AL_CONE_OUTER_GAIN */: + {{{ makeSetValue('value', '0', 'src.coneOuterGain', 'float') }}} + break; + case 0x1001 /* AL_CONE_INNER_ANGLE */: + {{{ makeSetValue('value', '0', 'src.coneInnerAngle', 'float') }}} + break; + case 0x1002 /* AL_CONE_OUTER_ANGLE */: + {{{ makeSetValue('value', '0', 'src.coneOuterAngle', 'float') }}} + break; case 0x1020 /* AL_REFERENCE_DISTANCE */: {{{ makeSetValue('value', '0', 'src.refDistance', 'float') }}} break; @@ -830,6 +995,49 @@ var LibraryOpenAL = { } }, + alGetSourcefv: function(source, param, values) { + if (!AL.currentContext) { +#if OPENAL_DEBUG + console.error("alGetSourcefv called without a valid context"); +#endif + return; + } + var src = AL.currentContext.src[source - 1]; + if (!src) { +#if OPENAL_DEBUG + console.error("alGetSourcefv called with an invalid source"); +#endif + AL.currentContext.err = 0xA001 /* AL_INVALID_NAME */; + return; + } + switch (param) { + case 0x1004 /* AL_POSITION */: + var position = src.position; + {{{ makeSetValue('values', '0', 'position[0]', 'float') }}} + {{{ makeSetValue('values', '4', 'position[1]', 'float') }}} + {{{ makeSetValue('values', '8', 'position[2]', 'float') }}} + break; + case 0x1005 /* AL_DIRECTION */: + var direction = src.direction; + {{{ makeSetValue('values', '0', 'direction[0]', 'float') }}} + {{{ makeSetValue('values', '4', 'direction[1]', 'float') }}} + {{{ makeSetValue('values', '8', 'direction[2]', 'float') }}} + break; + case 0x1006 /* AL_VELOCITY */: + var velocity = src.velocity; + {{{ makeSetValue('values', '0', 'velocity[0]', 'float') }}} + {{{ makeSetValue('values', '4', 'velocity[1]', 'float') }}} + {{{ makeSetValue('values', '8', 'velocity[2]', 'float') }}} + break; + default: +#if OPENAL_DEBUG + console.error("alGetSourcefv with param " + param + " not implemented yet"); +#endif + AL.currentContext.err = 0xA002 /* AL_INVALID_ENUM */; + break; + } + }, + alDistanceModel: function(model) { if (model !== 0 /* AL_NONE */) { #if OPENAL_DEBUG @@ -838,6 +1046,136 @@ var LibraryOpenAL = { } }, + alGetListenerf: function(pname, values) { + if (!AL.currentContext) { +#if OPENAL_DEBUG + console.error("alGetListenerf called without a valid context"); +#endif + return; + } + switch (pname) { + case 0x100A /* AL_GAIN */: + {{{ makeSetValue('value', '0', 'AL.currentContext.gain.gain', 'float') }}} + break; + default: +#if OPENAL_DEBUG + console.error("alGetListenerf with param " + pname + " not implemented yet"); +#endif + AL.currentContext.err = 0xA002 /* AL_INVALID_ENUM */; + break; + } + + }, + + alGetListenerfv: function(pname, values) { + if (!AL.currentContext) { +#if OPENAL_DEBUG + console.error("alGetListenerfv called without a valid context"); +#endif + return; + } + switch (pname) { + case 0x1004 /* AL_POSITION */: + var position = AL.currentContext.ctx.listener._position || [0,0,0]; + {{{ makeSetValue('values', '0', 'position[0]', 'float') }}} + {{{ makeSetValue('values', '4', 'position[1]', 'float') }}} + {{{ makeSetValue('values', '8', 'position[2]', 'float') }}} + break; + case 0x1006 /* AL_VELOCITY */: + var velocity = AL.currentContext.ctx.listener._velocity || [0,0,0]; + {{{ makeSetValue('values', '0', 'velocity[0]', 'float') }}} + {{{ makeSetValue('values', '4', 'velocity[1]', 'float') }}} + {{{ makeSetValue('values', '8', 'velocity[2]', 'float') }}} + break; + case 0x100F /* AL_ORIENTATION */: + var orientation = AL.currentContext.ctx.listener._orientation || [0,0,0,0,0,0]; + {{{ makeSetValue('values', '0', 'orientation[0]', 'float') }}} + {{{ makeSetValue('values', '4', 'orientation[1]', 'float') }}} + {{{ makeSetValue('values', '8', 'orientation[2]', 'float') }}} + {{{ makeSetValue('values', '12', 'orientation[3]', 'float') }}} + {{{ makeSetValue('values', '16', 'orientation[4]', 'float') }}} + {{{ makeSetValue('values', '20', 'orientation[5]', 'float') }}} + break; + default: +#if OPENAL_DEBUG + console.error("alGetListenerfv with param " + pname + " not implemented yet"); +#endif + AL.currentContext.err = 0xA002 /* AL_INVALID_ENUM */; + break; + } + }, + + alGetListeneri: function(pname, value) { + if (!AL.currentContext) { +#if OPENAL_DEBUG + console.error("alGetListeneri called without a valid context"); +#endif + return; + } + switch (pname) { + default: +#if OPENAL_DEBUG + console.error("alGetListeneri with param " + pname + " not implemented yet"); +#endif + AL.currentContext.err = 0xA002 /* AL_INVALID_ENUM */; + break; + } + }, + + alListenerf: function(param, value) { + if (!AL.currentContext) { +#if OPENAL_DEBUG + console.error("alListenerf called without a valid context"); +#endif + return; + } + switch (param) { + case 0x100A /* AL_GAIN */: + AL.currentContext.gain.value = value; + break; + default: +#if OPENAL_DEBUG + console.error("alListenerf with param " + param + " not implemented yet"); +#endif + AL.currentContext.err = 0xA002 /* AL_INVALID_ENUM */; + break; + } + }, + + alEnable: function(param) { + if (!AL.currentContext) { +#if OPENAL_DEBUG + console.error("alEnable called without a valid context"); +#endif + return; + } + switch (param) { + default: +#if OPENAL_DEBUG + console.error("alEnable with param " + param + " not implemented yet"); +#endif + AL.currentContext.err = 0xA002 /* AL_INVALID_ENUM */; + break; + } + }, + + alDisable: function(param) { + if (!AL.currentContext) { +#if OPENAL_DEBUG + console.error("alDisable called without a valid context"); +#endif + return; + } + switch (pname) { + default: +#if OPENAL_DEBUG + console.error("alDisable with param " + param + " not implemented yet"); +#endif + AL.currentContext.err = 0xA002 /* AL_INVALID_ENUM */; + break; + } + }, + alListenerfv: function(param, values) { if (!AL.currentContext) { #if OPENAL_DEBUG @@ -847,32 +1185,32 @@ var LibraryOpenAL = { } switch (param) { case 0x1004 /* AL_POSITION */: - AL.currentContext.ctx.listener.setPosition( - {{{ makeGetValue('values', '0', 'float') }}}, - {{{ makeGetValue('values', '4', 'float') }}}, - {{{ makeGetValue('values', '8', 'float') }}} - ); + var x = {{{ makeGetValue('values', '0', 'float') }}}; + var y = {{{ makeGetValue('values', '4', 'float') }}}; + var z = {{{ makeGetValue('values', '8', 'float') }}}; + AL.currentContext.ctx.listener._position = [x, y, z]; + AL.currentContext.ctx.listener.setPosition(x, y, z); break; case 0x1006 /* AL_VELOCITY */: - AL.currentContext.ctx.listener.setVelocity( - {{{ makeGetValue('values', '0', 'float') }}}, - {{{ makeGetValue('values', '4', 'float') }}}, - {{{ makeGetValue('values', '8', 'float') }}} - ); + var x = {{{ makeGetValue('values', '0', 'float') }}}; + var y = {{{ makeGetValue('values', '4', 'float') }}}; + var z = {{{ makeGetValue('values', '8', 'float') }}}; + AL.currentContext.ctx.listener._velocity = [x, y, z]; + AL.currentContext.ctx.listener.setVelocity(x, y, z); break; case 0x100F /* AL_ORIENTATION */: - AL.currentContext.ctx.listener.setOrientation( - {{{ makeGetValue('values', '0', 'float') }}}, - {{{ makeGetValue('values', '4', 'float') }}}, - {{{ makeGetValue('values', '8', 'float') }}}, - {{{ makeGetValue('values', '12', 'float') }}}, - {{{ makeGetValue('values', '16', 'float') }}}, - {{{ makeGetValue('values', '20', 'float') }}} - ); + var x = {{{ makeGetValue('values', '0', 'float') }}}; + var y = {{{ makeGetValue('values', '4', 'float') }}}; + var z = {{{ makeGetValue('values', '8', 'float') }}}; + var x2 = {{{ makeGetValue('values', '12', 'float') }}}; + var y2 = {{{ makeGetValue('values', '16', 'float') }}}; + var z2 = {{{ makeGetValue('values', '20', 'float') }}}; + AL.currentContext.ctx.listener._orientation = [x, y, z, x2, y2, z2]; + AL.currentContext.ctx.listener.setOrientation(x, y, z, x2, y2, z2); break; default: #if OPENAL_DEBUG - console.log("alListenerfv with param " + param + " not implemented yet"); + console.error("alListenerfv with param " + param + " not implemented yet"); #endif AL.currentContext.err = 0xA002 /* AL_INVALID_ENUM */; break; @@ -888,15 +1226,116 @@ var LibraryOpenAL = { }, alGetString: function(param) { - return allocate(intArrayFromString('NA'), 'i8', ALLOC_NORMAL); + if (AL.stringCache[param]) return AL.stringCache[param]; + var ret; + switch (param) { + case 0 /* AL_NO_ERROR */: + ret = 'No Error'; + break; + case 0xA001 /* AL_INVALID_NAME */: + ret = 'Invalid Name'; + break; + case 0xA002 /* AL_INVALID_ENUM */: + ret = 'Invalid Enum'; + break; + case 0xA003 /* AL_INVALID_VALUE */: + ret = 'Invalid Value'; + break; + case 0xA004 /* AL_INVALID_OPERATION */: + ret = 'Invalid Operation'; + break; + case 0xA005 /* AL_OUT_OF_MEMORY */: + ret = 'Out of Memory'; + break; + case 0xB001 /* AL_VENDOR */: + ret = 'Emscripten'; + break; + case 0xB002 /* AL_VERSION */: + ret = '1.1'; + break; + case 0xB003 /* AL_RENDERER */: + ret = 'WebAudio'; + break; + case 0xB004 /* AL_EXTENSIONS */: + ret = ''; + break; + default: + AL.currentContext.err = 0xA002 /* AL_INVALID_ENUM */; + return 0; + } + + ret = allocate(intArrayFromString(ret), 'i8', ALLOC_NORMAL); + + AL.stringCache[param] = ret; + + return ret; }, alGetProcAddress: function(fname) { return 0; }, - alcGetString: function(param) { - return allocate(intArrayFromString('NA'), 'i8', ALLOC_NORMAL); + alcGetString: function(device, param) { + if (AL.alcStringCache[param]) return AL.alcStringCache[param]; + var ret; + switch (param) { + case 0 /* ALC_NO_ERROR */: + ret = 'No Error'; + break; + case 0xA001 /* ALC_INVALID_DEVICE */: + ret = 'Invalid Device'; + break; + case 0xA002 /* ALC_INVALID_CONTEXT */: + ret = 'Invalid Context'; + break; + case 0xA003 /* ALC_INVALID_ENUM */: + ret = 'Invalid Enum'; + break; + case 0xA004 /* ALC_INVALID_VALUE */: + ret = 'Invalid Value'; + break; + case 0xA005 /* ALC_OUT_OF_MEMORY */: + ret = 'Out of Memory'; + break; + case 0x1004 /* ALC_DEFAULT_DEVICE_SPECIFIER */: + if (typeof(AudioContext) !== "undefined" || + typeof(webkitAudioContext) !== "undefined") { + ret = 'Device'; + } else { + return 0; + } + break; + case 0x1005 /* ALC_DEVICE_SPECIFIER */: + if (typeof(AudioContext) !== "undefined" || + typeof(webkitAudioContext) !== "undefined") { + ret = 'Device\0'; + } else { + ret = '\0'; + } + break; + case 0x311 /* ALC_CAPTURE_DEFAULT_DEVICE_SPECIFIER */: + return 0; + break; + case 0x310 /* ALC_CAPTURE_DEVICE_SPECIFIER */: + ret = '\0' + break; + case 0x1006 /* ALC_EXTENSIONS */: + if (!device) { + AL.currentContext.err = 0xA001 /* ALC_INVALID_DEVICE */; + return 0; + } + ret = ''; + break; + default: + AL.currentContext.err = 0xA003 /* ALC_INVALID_ENUM */; + return 0; + } + + ret = allocate(intArrayFromString(ret), 'i8', ALLOC_NORMAL); + + AL.alcStringCache[param] = ret; + + return ret; }, alcGetProcAddress: function(device, fname) { diff --git a/src/library_sdl.js b/src/library_sdl.js index fc38dd1c..2606bafc 100644 --- a/src/library_sdl.js +++ b/src/library_sdl.js @@ -91,13 +91,20 @@ var LibrarySDL = { 33: 1099, // pagedup 34: 1102, // pagedown - + + 35: 1101, // end + 36: 1098, // home + + 45: 1097, // insert + 17: 1248, // control (right, or left) 18: 1250, // alt 173: 45, // minus 16: 1249, // shift - 96: 88 | 1<<10, // keypad 0 + 20: 301, // caps lock + + 96: 98 | 1<<10, // keypad 0 97: 89 | 1<<10, // keypad 1 98: 90 | 1<<10, // keypad 2 99: 91 | 1<<10, // keypad 3 @@ -107,6 +114,15 @@ var LibrarySDL = { 103: 95 | 1<<10, // keypad 7 104: 96 | 1<<10, // keypad 8 105: 97 | 1<<10, // keypad 9 + + 106: 85 | 1<<10, // keypad multiply + 107: 87 | 1<<10, // keypad plus + 109: 86 | 1<<10, // keypad minus + 111: 84 | 1<<10, // keypad divide + + 110: 99 | 1<<10, // keypad decimal point + + 144: 83 | 1<<10, // keypad num lock 112: 58 | 1<<10, // F1 113: 59 | 1<<10, // F2 @@ -168,16 +184,19 @@ var LibrarySDL = { 27: 41, // escape 8: 42, // backspace 9: 43, // tab + 301: 57, // caps lock 32: 44, // space 61: 46, // equals 91: 47, // left bracket 93: 48, // right bracket 92: 49, // backslash + 96: 43, // grave 59: 51, // ; 96: 52, // apostrophe 44: 54, // comma 46: 55, // period 47: 56, // slash + 127: 76, // delete 305: 224, // ctrl 308: 226, // alt }, @@ -214,39 +233,44 @@ var LibrarySDL = { makeSurface: function(width, height, flags, usePageCanvas, source, rmask, gmask, bmask, amask) { flags = flags || 0; - var surf = _malloc({{{ C_STRUCTS.SDL_Surface.__size__ }}}); // SDL_Surface has 15 fields of quantum size - var buffer = _malloc(width*height*4); // TODO: only allocate when locked the first time - var pixelFormat = _malloc({{{ C_STRUCTS.SDL_PixelFormat.__size__ }}}); - flags |= 1; // SDL_HWSURFACE - this tells SDL_MUSTLOCK that this needs to be locked + var is_SDL_HWSURFACE = flags & 0x00000001; + var is_SDL_HWPALETTE = flags & 0x00200000; + var is_SDL_OPENGL = flags & 0x04000000; + var surf = _malloc({{{ C_STRUCTS.SDL_Surface.__size__ }}}); + var pixelFormat = _malloc({{{ C_STRUCTS.SDL_PixelFormat.__size__ }}}); //surface with SDL_HWPALETTE flag is 8bpp surface (1 byte) - var is_SDL_HWPALETTE = flags & 0x00200000; var bpp = is_SDL_HWPALETTE ? 1 : 4; - - {{{ makeSetValue('surf', C_STRUCTS.SDL_Surface.flags, 'flags', 'i32') }}} // SDL_Surface.flags - {{{ makeSetValue('surf', C_STRUCTS.SDL_Surface.format, 'pixelFormat', 'void*') }}} // SDL_Surface.format TODO - {{{ makeSetValue('surf', C_STRUCTS.SDL_Surface.w, 'width', 'i32') }}} // SDL_Surface.w - {{{ makeSetValue('surf', C_STRUCTS.SDL_Surface.h, 'height', 'i32') }}} // SDL_Surface.h - {{{ makeSetValue('surf', C_STRUCTS.SDL_Surface.pitch, 'width * bpp', 'i32') }}} // SDL_Surface.pitch, assuming RGBA or indexed for now, - // since that is what ImageData gives us in browsers - {{{ makeSetValue('surf', C_STRUCTS.SDL_Surface.pixels, 'buffer', 'void*') }}} // SDL_Surface.pixels - {{{ makeSetValue('surf', C_STRUCTS.SDL_Surface.clip_rect, '0', 'i32*') }}} // SDL_Surface.offset - - {{{ makeSetValue('surf', C_STRUCTS.SDL_Surface.refcount, '1', 'i32') }}} - - {{{ makeSetValue('pixelFormat', C_STRUCTS.SDL_PixelFormat.format, cDefine('SDL_PIXELFORMAT_RGBA8888'), 'i32') }}} // SDL_PIXELFORMAT_RGBA8888 - {{{ makeSetValue('pixelFormat', C_STRUCTS.SDL_PixelFormat.palette, '0', 'i32') }}} // TODO - {{{ makeSetValue('pixelFormat', C_STRUCTS.SDL_PixelFormat.BitsPerPixel, 'bpp * 8', 'i8') }}} - {{{ makeSetValue('pixelFormat', C_STRUCTS.SDL_PixelFormat.BytesPerPixel, 'bpp', 'i8') }}} - - {{{ makeSetValue('pixelFormat', C_STRUCTS.SDL_PixelFormat.Rmask, 'rmask || 0x000000ff', 'i32') }}} - {{{ makeSetValue('pixelFormat', C_STRUCTS.SDL_PixelFormat.Gmask, 'gmask || 0x0000ff00', 'i32') }}} - {{{ makeSetValue('pixelFormat', C_STRUCTS.SDL_PixelFormat.Bmask, 'bmask || 0x00ff0000', 'i32') }}} - {{{ makeSetValue('pixelFormat', C_STRUCTS.SDL_PixelFormat.Amask, 'amask || 0xff000000', 'i32') }}} + var buffer = 0; + + // preemptively initialize this for software surfaces, + // otherwise it will be lazily initialized inside of SDL_LockSurface + if (!is_SDL_HWSURFACE && !is_SDL_OPENGL) { + buffer = _malloc(width * height * 4); + } + + {{{ makeSetValue('surf', C_STRUCTS.SDL_Surface.flags, 'flags', 'i32') }}}; + {{{ makeSetValue('surf', C_STRUCTS.SDL_Surface.format, 'pixelFormat', 'void*') }}}; + {{{ makeSetValue('surf', C_STRUCTS.SDL_Surface.w, 'width', 'i32') }}}; + {{{ makeSetValue('surf', C_STRUCTS.SDL_Surface.h, 'height', 'i32') }}}; + {{{ makeSetValue('surf', C_STRUCTS.SDL_Surface.pitch, 'width * bpp', 'i32') }}}; // assuming RGBA or indexed for now, + // since that is what ImageData gives us in browsers + {{{ makeSetValue('surf', C_STRUCTS.SDL_Surface.pixels, 'buffer', 'void*') }}}; + {{{ makeSetValue('surf', C_STRUCTS.SDL_Surface.clip_rect, '0', 'i32*') }}}; + {{{ makeSetValue('surf', C_STRUCTS.SDL_Surface.refcount, '1', 'i32') }}}; + + {{{ makeSetValue('pixelFormat', C_STRUCTS.SDL_PixelFormat.format, cDefine('SDL_PIXELFORMAT_RGBA8888'), 'i32') }}}; + {{{ makeSetValue('pixelFormat', C_STRUCTS.SDL_PixelFormat.palette, '0', 'i32') }}};// TODO + {{{ makeSetValue('pixelFormat', C_STRUCTS.SDL_PixelFormat.BitsPerPixel, 'bpp * 8', 'i8') }}}; + {{{ makeSetValue('pixelFormat', C_STRUCTS.SDL_PixelFormat.BytesPerPixel, 'bpp', 'i8') }}}; + + {{{ makeSetValue('pixelFormat', C_STRUCTS.SDL_PixelFormat.Rmask, 'rmask || 0x000000ff', 'i32') }}}; + {{{ makeSetValue('pixelFormat', C_STRUCTS.SDL_PixelFormat.Gmask, 'gmask || 0x0000ff00', 'i32') }}}; + {{{ makeSetValue('pixelFormat', C_STRUCTS.SDL_PixelFormat.Bmask, 'bmask || 0x00ff0000', 'i32') }}}; + {{{ makeSetValue('pixelFormat', C_STRUCTS.SDL_PixelFormat.Amask, 'amask || 0xff000000', 'i32') }}}; // Decide if we want to use WebGL or not - var useWebGL = (flags & 0x04000000) != 0; // SDL_OPENGL - SDL.GL = SDL.GL || useWebGL; + SDL.GL = SDL.GL || is_SDL_OPENGL; var canvas; if (!usePageCanvas) { if (SDL.canvasPool.length > 0) { @@ -266,7 +290,7 @@ var LibrarySDL = { stencil: (SDL.glAttributes[7 /*SDL_GL_STENCIL_SIZE*/] > 0) }; - var ctx = Browser.createContext(canvas, useWebGL, usePageCanvas, webGLContextAttributes); + var ctx = Browser.createContext(canvas, is_SDL_OPENGL, usePageCanvas, webGLContextAttributes); SDL.surfaces[surf] = { width: width, @@ -337,7 +361,7 @@ var LibrarySDL = { var info = SDL.surfaces[surf]; if (!info.usePageCanvas && info.canvas) SDL.canvasPool.push(info.canvas); - _free(info.buffer); + if (info.buffer) _free(info.buffer); _free(info.pixelFormat); _free(surf); SDL.surfaces[surf] = null; @@ -407,12 +431,12 @@ var LibrarySDL = { // won't fire. However, it's fine (and in some cases necessary) to // preventDefault for keys that don't generate a character. Otherwise, // preventDefault is the right thing to do in general. - if (event.type !== 'keydown' || (event.keyCode === 8 /* backspace */ || event.keyCode === 9 /* tab */)) { + if (event.type !== 'keydown' || (!SDL.unicode && !SDL.textInput) || (event.keyCode === 8 /* backspace */ || event.keyCode === 9 /* tab */)) { event.preventDefault(); } if (event.type == 'DOMMouseScroll' || event.type == 'mousewheel') { - var button = (event.type == 'DOMMouseScroll' ? event.detail : -event.wheelDelta) > 0 ? 4 : 3; + var button = Browser.getMouseWheelDelta(event) > 0 ? 4 : 3; var event2 = { type: 'mousedown', button: button, @@ -445,9 +469,9 @@ var LibrarySDL = { // keyup. This isn't perfect, but it enables SDL_WM_ToggleFullScreen // to work as the result of a keypress (which is an extremely // common use case). - if (event.type === 'keydown') { + if (event.type === 'keydown' || event.type === 'mousedown') { SDL.canRequestFullscreen = true; - } else if (event.type === 'keyup') { + } else if (event.type === 'keyup' || event.type === 'mouseup') { if (SDL.isRequestingFullscreen) { Module['requestFullScreen'](true, true); SDL.isRequestingFullscreen = false; @@ -592,19 +616,19 @@ var LibrarySDL = { scan = SDL.scanCodes[key] || key; } - {{{ makeSetValue('ptr', C_STRUCTS.SDL_KeyboardEvent.type, 'SDL.DOMEventToSDLEvent[event.type]', 'i32') }}} - {{{ makeSetValue('ptr', C_STRUCTS.SDL_KeyboardEvent.state, 'down ? 1 : 0', 'i8') }}} - {{{ makeSetValue('ptr', C_STRUCTS.SDL_KeyboardEvent.repeat, '0', 'i8') }}} // TODO - {{{ makeSetValue('ptr', C_STRUCTS.SDL_KeyboardEvent.keysym + C_STRUCTS.SDL_Keysym.scancode, 'scan', 'i32') }}} - {{{ makeSetValue('ptr', C_STRUCTS.SDL_KeyboardEvent.keysym + C_STRUCTS.SDL_Keysym.sym, 'key', 'i32') }}} - {{{ makeSetValue('ptr', C_STRUCTS.SDL_KeyboardEvent.keysym + C_STRUCTS.SDL_Keysym.mod, 'SDL.modState', 'i16') }}} + {{{ makeSetValue('ptr', C_STRUCTS.SDL_KeyboardEvent.type, 'SDL.DOMEventToSDLEvent[event.type]', 'i32') }}}; + {{{ makeSetValue('ptr', C_STRUCTS.SDL_KeyboardEvent.state, 'down ? 1 : 0', 'i8') }}}; + {{{ makeSetValue('ptr', C_STRUCTS.SDL_KeyboardEvent.repeat, '0', 'i8') }}}; // TODO + {{{ makeSetValue('ptr', C_STRUCTS.SDL_KeyboardEvent.keysym + C_STRUCTS.SDL_Keysym.scancode, 'scan', 'i32') }}}; + {{{ makeSetValue('ptr', C_STRUCTS.SDL_KeyboardEvent.keysym + C_STRUCTS.SDL_Keysym.sym, 'key', 'i32') }}}; + {{{ makeSetValue('ptr', C_STRUCTS.SDL_KeyboardEvent.keysym + C_STRUCTS.SDL_Keysym.mod, 'SDL.modState', 'i16') }}}; // some non-character keys (e.g. backspace and tab) won't have keypressCharCode set, fill in with the keyCode. - {{{ makeSetValue('ptr', C_STRUCTS.SDL_KeyboardEvent.keysym + C_STRUCTS.SDL_Keysym.unicode, 'event.keypressCharCode || key', 'i32') }}} + {{{ makeSetValue('ptr', C_STRUCTS.SDL_KeyboardEvent.keysym + C_STRUCTS.SDL_Keysym.unicode, 'event.keypressCharCode || key', 'i32') }}}; break; } case 'keypress': { - {{{ makeSetValue('ptr', C_STRUCTS.SDL_TextInputEvent.type, 'SDL.DOMEventToSDLEvent[event.type]', 'i32') }}} + {{{ makeSetValue('ptr', C_STRUCTS.SDL_TextInputEvent.type, 'SDL.DOMEventToSDLEvent[event.type]', 'i32') }}}; // Not filling in windowID for now var cStr = intArrayFromString(String.fromCharCode(event.charCode)); for (var i = 0; i < cStr.length; ++i) { @@ -701,6 +725,29 @@ var LibrarySDL = { return ret; }, + fillWebAudioBufferFromHeap: function(heapPtr, sizeSamplesPerChannel, dstAudioBuffer) { + // The input audio data is interleaved across the channels, i.e. [L, R, L, R, L, R, ...] and is either 8-bit or 16-bit as + // supported by the SDL API. The output audio wave data for Web Audio API must be in planar buffers of [-1,1]-normalized Float32 data, + // so perform a buffer conversion for the data. + var numChannels = SDL.audio.channels; + for(var c = 0; c < numChannels; ++c) { + var channelData = dstAudioBuffer['getChannelData'](c); + if (channelData.length != sizeSamplesPerChannel) { + throw 'Web Audio output buffer length mismatch! Destination size: ' + channelData.length + ' samples vs expected ' + sizeSamplesPerChannel + ' samples!'; + } + if (SDL.audio.format == 0x8010 /*AUDIO_S16LSB*/) { + for(var j = 0; j < sizeSamplesPerChannel; ++j) { + channelData[j] = ({{{ makeGetValue('heapPtr', '(j*numChannels + c)*2', 'i16', 0, 0) }}}) / 0x8000; + } + } else if (SDL.audio.format == 0x0008 /*AUDIO_U8*/) { + for(var j = 0; j < sizeSamplesPerChannel; ++j) { + var v = ({{{ makeGetValue('heapPtr', 'j*numChannels + c', 'i8', 0, 0) }}}); + channelData[j] = ((v >= 0) ? v-128 : v+128) /128; + } + } + } + }, + // Debugging debugSurface: function(surfData) { @@ -819,9 +866,9 @@ var LibrarySDL = { SDL_Linked_Version: function() { if (SDL.version === null) { SDL.version = _malloc({{{ C_STRUCTS.SDL_version.__size__ }}}); - {{{ makeSetValue('SDL.version + ' + C_STRUCTS.SDL_version.major, '0', '1', 'i8') }}} - {{{ makeSetValue('SDL.version + ' + C_STRUCTS.SDL_version.minor, '0', '3', 'i8') }}} - {{{ makeSetValue('SDL.version + ' + C_STRUCTS.SDL_version.patch, '0', '0', 'i8') }}} + {{{ makeSetValue('SDL.version + ' + C_STRUCTS.SDL_version.major, '0', '1', 'i8') }}}; + {{{ makeSetValue('SDL.version + ' + C_STRUCTS.SDL_version.minor, '0', '3', 'i8') }}}; + {{{ makeSetValue('SDL.version + ' + C_STRUCTS.SDL_version.patch, '0', '0', 'i8') }}}; } return SDL.version; }, @@ -879,11 +926,11 @@ var LibrarySDL = { SDL_GetVideoInfo: function() { // %struct.SDL_VideoInfo = type { i32, i32, %struct.SDL_PixelFormat*, i32, i32 } - 5 fields of quantum size var ret = _malloc(5*Runtime.QUANTUM_SIZE); - {{{ makeSetValue('ret+Runtime.QUANTUM_SIZE*0', '0', '0', 'i32') }}} // TODO - {{{ makeSetValue('ret+Runtime.QUANTUM_SIZE*1', '0', '0', 'i32') }}} // TODO - {{{ makeSetValue('ret+Runtime.QUANTUM_SIZE*2', '0', '0', 'void*') }}} - {{{ makeSetValue('ret+Runtime.QUANTUM_SIZE*3', '0', 'Module["canvas"].width', 'i32') }}} - {{{ makeSetValue('ret+Runtime.QUANTUM_SIZE*4', '0', 'Module["canvas"].height', 'i32') }}} + {{{ makeSetValue('ret+Runtime.QUANTUM_SIZE*0', '0', '0', 'i32') }}}; // TODO + {{{ makeSetValue('ret+Runtime.QUANTUM_SIZE*1', '0', '0', 'i32') }}}; // TODO + {{{ makeSetValue('ret+Runtime.QUANTUM_SIZE*2', '0', '0', 'void*') }}}; + {{{ makeSetValue('ret+Runtime.QUANTUM_SIZE*3', '0', 'Module["canvas"].width', 'i32') }}}; + {{{ makeSetValue('ret+Runtime.QUANTUM_SIZE*4', '0', 'Module["canvas"].height', 'i32') }}}; return ret; }, @@ -986,6 +1033,11 @@ var LibrarySDL = { surfData.locked++; if (surfData.locked > 1) return 0; + if (!surfData.buffer) { + surfData.buffer = _malloc(surfData.width * surfData.height * 4); + {{{ makeSetValue('surf', C_STRUCTS.SDL_Surface.pixels, 'surfData.buffer', 'void*') }}}; + } + // Mark in C/C++-accessible SDL structure // SDL_Surface has the following fields: Uint32 flags, SDL_PixelFormat *format; int w, h; Uint16 pitch; void *pixels; ... // So we have fields all of the same size, and 5 of them before us. @@ -1045,8 +1097,9 @@ var LibrarySDL = { var surfData = SDL.surfaces[surf]; - surfData.locked--; - if (surfData.locked > 0) return; + if (!surfData.locked || --surfData.locked > 0) { + return; + } // Copy pixel data to image if (surfData.isFlagSet(0x00200000 /* SDL_HWPALETTE */)) { @@ -1220,6 +1273,11 @@ var LibrarySDL = { return SDL.errorMessage; }, + SDL_SetError: function() {}, + + SDL_Malloc: 'malloc', + SDL_Free: 'free', + SDL_CreateRGBSurface: function(flags, width, height, depth, rmask, gmask, bmask, amask) { return SDL.makeSurface(width, height, flags, false, 'CreateRGBSurface', rmask, gmask, bmask, amask); }, @@ -1265,7 +1323,7 @@ var LibrarySDL = { dstData.ctx.globalAlpha = oldAlpha; if (dst != SDL.screen) { // XXX As in IMG_Load, for compatibility we write out |pixels| - console.log('WARNING: copying canvas data to memory for compatibility'); + Runtime.warnOnce('WARNING: copying canvas data to memory for compatibility'); _SDL_LockSurface(dst); dstData.locked--; // The surface is not actually locked in this hack } @@ -1707,9 +1765,9 @@ var LibrarySDL = { // Initialize Web Audio API if we haven't done so yet. Note: Only initialize Web Audio context ever once on the web page, // since initializing multiple times fails on Chrome saying 'audio resources have been exhausted'. if (!SDL.audioContext) { - if (typeof(AudioContext) === 'function') { + if (typeof(AudioContext) !== 'undefined') { SDL.audioContext = new AudioContext(); - } else if (typeof(webkitAudioContext) === 'function') { + } else if (typeof(webkitAudioContext) !== 'undefined') { SDL.audioContext = new webkitAudioContext(); } else { throw 'Web Audio API is not available!'; @@ -1723,6 +1781,7 @@ var LibrarySDL = { SDL.audio.pushAudio=function(ptr,sizeBytes) { try { --SDL.audio.numAudioTimersPending; + if (SDL.audio.paused) return; var sizeSamples = sizeBytes / SDL.audio.bytesPerSample; // How many samples fit in the callback buffer? var sizeSamplesPerChannel = sizeSamples / SDL.audio.channels; // How many samples per a single channel fit in the cb buffer? @@ -1738,26 +1797,7 @@ var LibrarySDL = { var soundBuffer = SDL.audioContext['createBuffer'](SDL.audio.channels,sizeSamplesPerChannel,SDL.audio.freq); SDL.audio.soundSource[SDL.audio.nextSoundSource]['connect'](SDL.audioContext['destination']); - // The input audio data is interleaved across the channels, i.e. [L, R, L, R, L, R, ...] and is either 8-bit or 16-bit as - // supported by the SDL API. The output audio wave data for Web Audio API must be in planar buffers of [-1,1]-normalized Float32 data, - // so perform a buffer conversion for the data. - var numChannels = SDL.audio.channels; - for(var i = 0; i < numChannels; ++i) { - var channelData = soundBuffer['getChannelData'](i); - if (channelData.length != sizeSamplesPerChannel) { - throw 'Web Audio output buffer length mismatch! Destination size: ' + channelData.length + ' samples vs expected ' + sizeSamplesPerChannel + ' samples!'; - } - if (SDL.audio.format == 0x8010 /*AUDIO_S16LSB*/) { - for(var j = 0; j < sizeSamplesPerChannel; ++j) { - channelData[j] = ({{{ makeGetValue('ptr', '(j*numChannels + i)*2', 'i16', 0, 0) }}}) / 0x8000; - } - } else if (SDL.audio.format == 0x0008 /*AUDIO_U8*/) { - for(var j = 0; j < sizeSamplesPerChannel; ++j) { - var v = ({{{ makeGetValue('ptr', 'j*numChannels + i', 'i8', 0, 0) }}}); - channelData[j] = ((v >= 0) ? v-128 : v+128) /128; - } - } - } + SDL.fillWebAudioBufferFromHeap(ptr, sizeSamplesPerChannel, soundBuffer); // Workaround https://bugzilla.mozilla.org/show_bug.cgi?id=883675 by setting the buffer only after filling. The order is important here! source['buffer'] = soundBuffer; @@ -1770,21 +1810,26 @@ var LibrarySDL = { } #endif var playtime = Math.max(curtime, SDL.audio.nextPlayTime); - SDL.audio.soundSource[SDL.audio.nextSoundSource]['start'](playtime); + var ss = SDL.audio.soundSource[SDL.audio.nextSoundSource]; + if (typeof ss['start'] !== 'undefined') { + ss['start'](playtime); + } else if (typeof ss['noteOn'] !== 'undefined') { + ss['noteOn'](playtime); + } var buffer_duration = sizeSamplesPerChannel / SDL.audio.freq; SDL.audio.nextPlayTime = playtime + buffer_duration; - SDL.audio.nextSoundSource = (SDL.audio.nextSoundSource + 1) % 4; + // Timer will be scheduled before the buffer completed playing. + // Extra buffers are needed to avoid disturbing playing buffer. + SDL.audio.nextSoundSource = (SDL.audio.nextSoundSource + 1) % (SDL.audio.numSimultaneouslyQueuedBuffers + 2); var secsUntilNextCall = playtime-curtime; // Queue the next audio frame push to be performed when the previously queued buffer has finished playing. - if (SDL.audio.numAudioTimersPending == 0) { - var preemptBufferFeedMSecs = buffer_duration/2.0; - SDL.audio.timer = Browser.safeSetTimeout(SDL.audio.caller, Math.max(0.0, 1000.0*secsUntilNextCall-preemptBufferFeedMSecs)); - ++SDL.audio.numAudioTimersPending; - } + var preemptBufferFeedMSecs = 1000*buffer_duration/2.0; + SDL.audio.timer = Browser.safeSetTimeout(SDL.audio.caller, Math.max(0.0, 1000.0*secsUntilNextCall-preemptBufferFeedMSecs)); + ++SDL.audio.numAudioTimersPending; // If we are risking starving, immediately queue extra buffers. - if (secsUntilNextCall <= buffer_duration && SDL.audio.numAudioTimersPending < SDL.audio.numSimultaneouslyQueuedBuffers) { + if (SDL.audio.numAudioTimersPending < SDL.audio.numSimultaneouslyQueuedBuffers) { ++SDL.audio.numAudioTimersPending; Browser.safeSetTimeout(SDL.audio.caller, 1.0); } @@ -1836,11 +1881,27 @@ var LibrarySDL = { SDL.audio.numAudioTimersPending = 0; SDL.audio.timer = undefined; } - } else if (!SDL.audio.timer) { - // Start the audio playback timer callback loop. - SDL.audio.numAudioTimersPending = 1; - SDL.audio.timer = Browser.safeSetTimeout(SDL.audio.caller, 1); - SDL.audio.startTime = Date.now() / 1000.0; // Only used for Mozilla Audio Data API. Not needed for Web Audio API. + if (SDL.audio.scriptProcessorNode !== undefined) { + SDL.audio.scriptProcessorNode['disconnect'](); + SDL.audio.scriptProcessorNode = undefined; + } + } else if (!SDL.audio.timer && !SDL.audio.scriptProcessorNode) { + // If we are using the same sampling frequency as the native sampling rate of the Web Audio graph is using, we can feed our buffers via + // Web Audio ScriptProcessorNode, which is a pull-mode API that calls back to our code to get audio data. + if (SDL.audioContext !== undefined && SDL.audio.freq == SDL.audioContext['sampleRate'] && typeof SDL.audioContext['createScriptProcessor'] !== 'undefined') { + var sizeSamplesPerChannel = SDL.audio.bufferSize / SDL.audio.bytesPerSample / SDL.audio.channels; // How many samples per a single channel fit in the cb buffer? + SDL.audio.scriptProcessorNode = SDL.audioContext['createScriptProcessor'](sizeSamplesPerChannel, 0, SDL.audio.channels); + SDL.audio.scriptProcessorNode['onaudioprocess'] = function (e) { + Runtime.dynCall('viii', SDL.audio.callback, [SDL.audio.userdata, SDL.audio.buffer, SDL.audio.bufferSize]); + SDL.fillWebAudioBufferFromHeap(SDL.audio.buffer, sizeSamplesPerChannel, e['outputBuffer']); + } + SDL.audio.scriptProcessorNode['connect'](SDL.audioContext['destination']); + } else { // If we are using a different sampling rate, must manually queue audio data to the graph via timers. + // Start the audio playback timer callback loop. + SDL.audio.numAudioTimersPending = 1; + SDL.audio.timer = Browser.safeSetTimeout(SDL.audio.caller, 1); + SDL.audio.startTime = Date.now() / 1000.0; // Only used for Mozilla Audio Data API. Not needed for Web Audio API. + } } SDL.audio.paused = pauseOn; }, @@ -2482,7 +2543,7 @@ var LibrarySDL = { SDL_GL_GetProcAddress__deps: ['emscripten_GetProcAddress'], SDL_GL_GetProcAddress: function(name_) { - return _emscripten_GetProcAddress(Pointer_stringify(name_)); + return _emscripten_GetProcAddress(name_); }, SDL_GL_SwapBuffers: function() {}, @@ -2676,6 +2737,14 @@ var LibrarySDL = { } }, + SDL_GetNumAudioDrivers: function() { return 1 }, + SDL_GetCurrentAudioDriver: function() { + return allocate(intArrayFromString('Emscripten Audio'), 'i8', ALLOC_NORMAL); + }, + + SDL_GetAudioDriver__deps: ['SDL_GetCurrentAudioDriver'], + SDL_GetAudioDriver: function(index) { return _SDL_GetCurrentAudioDriver() }, + SDL_EnableUNICODE: function(on) { var ret = SDL.unicode || 0; SDL.unicode = on; @@ -2705,6 +2774,9 @@ var LibrarySDL = { SDL_WM_IconifyWindow: function() { throw 'SDL_WM_IconifyWindow TODO' }, Mix_SetPostMix: function() { Runtime.warnOnce('Mix_SetPostMix: TODO') }, + + Mix_VolumeChunk: function(chunk, volume) { throw 'Mix_VolumeChunk: TODO' }, + Mix_SetPosition: function(channel, angle, distance) { throw 'Mix_SetPosition: TODO' }, Mix_QuerySpec: function() { throw 'Mix_QuerySpec: TODO' }, Mix_FadeInChannelTimed: function() { throw 'Mix_FadeInChannelTimed' }, Mix_FadeOutChannel: function() { throw 'Mix_FadeOutChannel' }, diff --git a/src/library_sockfs.js b/src/library_sockfs.js index 3f758e5c..23641464 100644 --- a/src/library_sockfs.js +++ b/src/library_sockfs.js @@ -3,7 +3,7 @@ mergeInto(LibraryManager.library, { $SOCKFS__deps: ['$FS', 'mkport'], $SOCKFS: { mount: function(mount) { - return FS.createNode(null, '/', {{{ cDefine('S_IFDIR') }}} | 0777, 0); + return FS.createNode(null, '/', {{{ cDefine('S_IFDIR') }}} | 511 /* 0777 */, 0); }, createSocket: function(family, type, protocol) { var streaming = type == {{{ cDefine('SOCK_STREAM') }}}; diff --git a/src/modules.js b/src/modules.js index ad467ba7..2d2a75d0 100644 --- a/src/modules.js +++ b/src/modules.js @@ -424,9 +424,22 @@ var LibraryManager = { load: function() { if (this.library) return; - var libraries = ['library.js', 'library_path.js', 'library_fs.js', 'library_idbfs.js', 'library_memfs.js', 'library_nodefs.js', 'library_sockfs.js', 'library_tty.js', 'library_browser.js', 'library_sdl.js', 'library_gl.js', 'library_glut.js', 'library_xlib.js', 'library_egl.js', 'library_gc.js', 'library_jansson.js', 'library_openal.js', 'library_glfw.js', 'library_uuid.js', 'library_glew.js'].concat(additionalLibraries); + var libraries = ['library.js', 'library_path.js', 'library_fs.js', 'library_idbfs.js', 'library_memfs.js', 'library_nodefs.js', 'library_sockfs.js', 'library_tty.js', 'library_browser.js', 'library_sdl.js', 'library_gl.js', 'library_glut.js', 'library_xlib.js', 'library_egl.js', 'library_gc.js', 'library_jansson.js', 'library_openal.js', 'library_glfw.js', 'library_uuid.js', 'library_glew.js', 'library_html5.js'].concat(additionalLibraries); for (var i = 0; i < libraries.length; i++) { - eval(processMacros(preprocess(read(libraries[i])))); + var filename = libraries[i]; + var src = read(filename); + try { + var processed = processMacros(preprocess(src)); + eval(processed); + } catch(e) { + var details = [e, e.lineNumber ? 'line number: ' + e.lineNumber : '', (e.stack || "").toString().replace('Object.<anonymous>', filename)]; + if (processed) { + error('failure to execute js library "' + filename + '": ' + details + '\npreprocessed source (you can run a js engine on this to get a clearer error message sometimes):\n=============\n' + processed + '\n=============\n'); + } else { + error('failure to process js library "' + filename + '": ' + details + '\noriginal source:\n=============\n' + src + '\n=============\n'); + } + throw e; + } } /* diff --git a/src/parseTools.js b/src/parseTools.js index b7f97a40..4fb76196 100644 --- a/src/parseTools.js +++ b/src/parseTools.js @@ -46,7 +46,11 @@ function preprocess(text) { error('unsupported preprecessor op ' + op); } } else { - showStack.push(ident in this && this[ident] > 0); + if (ident[0] === '!') { + showStack.push(!(this[ident.substr(1)] > 0)); + } else { + showStack.push(ident in this && this[ident] > 0); + } } } else if (line[2] == 'n') { // include var included = read(line.substr(line.indexOf(' ')+1)); @@ -158,7 +162,7 @@ function isArrayType(type) { function isStructType(type) { if (isPointerType(type)) return false; if (isArrayType(type)) return true; - if (/<?{ ?[^}]* ?}>?/.test(type)) return true; // { i32, i8 } etc. - anonymous struct types + if (/<?\{ ?[^}]* ?\}>?/.test(type)) return true; // { i32, i8 } etc. - anonymous struct types // See comment in isStructPointerType() return type[0] == '%'; } @@ -168,7 +172,7 @@ function isVectorType(type) { } function isStructuralType(type) { - return /^{ ?[^}]* ?}$/.test(type); // { i32, i8 } etc. - anonymous struct types + return /^\{ ?[^}]* ?\}$/.test(type); // { i32, i8 } etc. - anonymous struct types } function getStructuralTypeParts(type) { // split { i32, i8 } etc. into parts @@ -1157,7 +1161,7 @@ function getHeapOffset(offset, type, forceAsm) { if (Runtime.getNativeFieldSize(type) > 4) { if (type == 'i64' || TARGET_X86) { - type = 'i32'; // XXX we emulate 64-bit values as 32 in x86, and also in le32 but only i64, not double + type = 'i32'; // XXX we emulate 64-bit values as 32 in x86, and also in asmjs-unknown-emscripten but only i64, not double } } @@ -1283,7 +1287,7 @@ function makeGetValue(ptr, pos, type, noNeedFirst, unsigned, ignore, align, noSa return '{ ' + ret.join(', ') + ' }'; } - // In double mode 1, in x86 we always assume unaligned because we can't trust that; otherwise in le32 + // In double mode 1, in x86 we always assume unaligned because we can't trust that; otherwise in asmjs-unknown-emscripten // we need this code path if we are not fully aligned. if (DOUBLE_MODE == 1 && USE_TYPED_ARRAYS == 2 && type == 'double' && (TARGET_X86 || align < 8)) { return '(' + makeSetTempDouble(0, 'i32', makeGetValue(ptr, pos, 'i32', noNeedFirst, unsigned, ignore, align, noSafe)) + ',' + @@ -1327,18 +1331,22 @@ function makeGetValue(ptr, pos, type, noNeedFirst, unsigned, ignore, align, noSa var printType = type; if (printType !== 'null' && printType[0] !== '#') printType = '"' + safeQuote(printType) + '"'; if (printType[0] === '#') printType = printType.substr(1); - return asmCoercion('SAFE_HEAP_LOAD(' + asmCoercion(offset, 'i32') + ', ' + (ASM_JS ? 0 : printType) + ', ' + (!!unsigned+0) + ', ' + ((!checkSafeHeap() || ignore)|0) + ')', type); - } else { - var ret = makeGetSlabs(ptr, type, false, unsigned)[0] + '[' + getHeapOffset(offset, type, forceAsm) + ']'; - if (ASM_JS && (phase == 'funcs' || forceAsm)) { - ret = asmCoercion(ret, type); - } - if (ASM_HEAP_LOG) { - ret = makeInlineCalculation('(asmPrint' + (type in Runtime.FLOAT_TYPES ? 'Float' : 'Int') + '(' + (asmPrintCounter++) + ',' + asmCoercion('VALUE', type) + '), VALUE)', ret, - 'temp' + (type in Runtime.FLOAT_TYPES ? 'Double' : 'Int')); + if (ASM_JS) { + if (!ignore && phase !== 'funcs') return asmCoercion('SAFE_HEAP_LOAD(' + asmCoercion(offset, 'i32') + ', ' + Runtime.getNativeTypeSize(type) + ', ' + ((type in Runtime.FLOAT_TYPES)|0) + ', ' + (!!unsigned+0) + ')', type); + // else fall through + } else { + return asmCoercion('SAFE_HEAP_LOAD(' + offset + ', ' + (ASM_JS ? 0 : printType) + ', ' + (!!unsigned+0) + ', ' + ((!checkSafeHeap() || ignore)|0) + ')', type); } - return ret; } + var ret = makeGetSlabs(ptr, type, false, unsigned)[0] + '[' + getHeapOffset(offset, type, forceAsm) + ']'; + if (ASM_JS && (phase == 'funcs' || forceAsm)) { + ret = asmCoercion(ret, type); + } + if (ASM_HEAP_LOG) { + ret = makeInlineCalculation('(asmPrint' + (type in Runtime.FLOAT_TYPES ? 'Float' : 'Int') + '(' + (asmPrintCounter++) + ',' + asmCoercion('VALUE', type) + '), VALUE)', ret, + 'temp' + (type in Runtime.FLOAT_TYPES ? 'Double' : 'Int')); + } + return ret; } function makeGetValueAsm(ptr, pos, type, unsigned) { @@ -1435,10 +1443,14 @@ function makeSetValue(ptr, pos, value, type, noNeedFirst, ignore, align, noSafe, var printType = type; if (printType !== 'null' && printType[0] !== '#') printType = '"' + safeQuote(printType) + '"'; if (printType[0] === '#') printType = printType.substr(1); - return 'SAFE_HEAP_STORE(' + asmCoercion(offset, 'i32') + ', ' + asmCoercion(value, type) + ', ' + (ASM_JS ? 0 : printType) + ', ' + ((!checkSafeHeap() || ignore)|0) + ')'; - } else { - return makeGetSlabs(ptr, type, true).map(function(slab) { return slab + '[' + getHeapOffset(offset, type, forceAsm) + ']=' + value }).join(sep); + if (ASM_JS) { + if (!ignore && phase !== 'funcs') return asmCoercion('SAFE_HEAP_STORE(' + asmCoercion(offset, 'i32') + ', ' + asmCoercion(value, type) + ', ' + Runtime.getNativeTypeSize(type) + ', ' + ((type in Runtime.FLOAT_TYPES)|0) + ')', type); + // else fall through + } else { + return 'SAFE_HEAP_STORE(' + offset + ', ' + value + ', ' + (ASM_JS ? 0 : printType) + ', ' + ((!checkSafeHeap() || ignore)|0) + ')'; + } } + return makeGetSlabs(ptr, type, true).map(function(slab) { return slab + '[' + getHeapOffset(offset, type, forceAsm) + ']=' + value }).join(sep); } function makeSetValueAsm(ptr, pos, value, type, noNeedFirst, ignore, align, noSafe, sep, forcedAlign) { @@ -1781,31 +1793,12 @@ function makePointer(slab, pos, allocator, type, ptr, finalMemoryInitialization) types = 'i8'; } - // JS engines sometimes say array initializers are too large. Work around that by chunking and calling concat to combine at runtime - var chunkSize = JS_CHUNK_SIZE; - function chunkify(array) { - // break very large slabs into parts - var ret = ''; - var index = 0; - while (index < array.length) { - ret = (ret ? ret + '.concat(' : '') + '[' + array.slice(index, index + chunkSize).map(JSON.stringify) + ']' + (ret ? ')\n' : ''); - index += chunkSize; - } - return ret; - } - if (typeof slab == 'object' && slab.length > chunkSize) { - slab = chunkify(slab); - } if (typeof types == 'object') { while (types.length < slab.length) types.push(0); } - if (typeof types != 'string' && types.length > chunkSize) { - types = chunkify(types); - } else { - types = JSON.stringify(types); - } + types = JSON.stringify(types); if (typeof slab == 'object') slab = '[' + slab.join(',') + ']'; - return 'allocate(' + slab + ', ' + types + (allocator ? ', ' + allocator : '') + (allocator == 'ALLOC_NONE' ? ', ' + ptr : '') + ')'; + return 'allocate(' + slab + ', ' + types + (allocator ? ', ' + allocator : '') + (allocator == 'ALLOC_NONE' ? ', ' + ptr : '') + ');'; } function makeGetSlabs(ptr, type, allowMultiple, unsigned) { @@ -1833,7 +1826,7 @@ function makeGetSlabs(ptr, type, allowMultiple, unsigned) { case '<4 x i32>': case 'i32': case 'i64': return [unsigned ? 'HEAPU32' : 'HEAP32']; break; case 'double': { - if (TARGET_LE32) return ['HEAPF64']; // in le32, we do have the ability to assume 64-bit alignment + if (TARGET_ASMJS_UNKNOWN_EMSCRIPTEN) return ['HEAPF64']; // in asmjs-unknown-emscripten, we do have the ability to assume 64-bit alignment // otherwise, fall through to float } case '<4 x float>': @@ -2150,9 +2143,9 @@ function makeRounding(value, bits, signed, floatConversion) { } } // Math.floor is reasonably fast if we don't care about corrections (and even correct if unsigned) - if (!correctRoundings() || !signed) return 'Math_floor(' + value + ')'; + if (!correctRoundings() || !signed) return '(+Math_floor(' + value + '))'; // We are left with >32 bits - return makeInlineCalculation(makeComparison('VALUE', '>=', '0', 'float') + ' ? Math_floor(VALUE) : Math_ceil(VALUE)', value, 'tempBigIntR'); + return makeInlineCalculation(makeComparison('VALUE', '>=', '0', 'float') + ' ? +Math_floor(VALUE) : +Math_ceil(VALUE)', value, 'tempBigIntR'); } } diff --git a/src/postamble.js b/src/postamble.js index d6c059b8..b90049bc 100644 --- a/src/postamble.js +++ b/src/postamble.js @@ -2,19 +2,21 @@ // === Auto-generated postamble setup entry stuff === if (memoryInitializer) { - function applyData(data) { + if (ENVIRONMENT_IS_NODE || ENVIRONMENT_IS_SHELL) { + var data = Module['readBinary'](memoryInitializer); #if USE_TYPED_ARRAYS == 2 HEAPU8.set(data, STATIC_BASE); #else allocate(data, 'i8', ALLOC_NONE, STATIC_BASE); #endif - } - if (ENVIRONMENT_IS_NODE || ENVIRONMENT_IS_SHELL) { - applyData(Module['readBinary'](memoryInitializer)); } else { addRunDependency('memory initializer'); Browser.asyncLoad(memoryInitializer, function(data) { - applyData(data); +#if USE_TYPED_ARRAYS == 2 + HEAPU8.set(data, STATIC_BASE); +#else + allocate(data, 'i8', ALLOC_NONE, STATIC_BASE); +#endif removeRunDependency('memory initializer'); }, function(data) { throw 'could not load memory initializer ' + memoryInitializer; @@ -46,10 +48,6 @@ Module['callMain'] = Module.callMain = function callMain(args) { args = args || []; - if (ENVIRONMENT_IS_WEB && preloadStartTime !== null) { - Module.printErr('preload time: ' + (Date.now() - preloadStartTime) + ' ms'); - } - ensureInitRuntime(); var argc = args.length+1; @@ -121,12 +119,17 @@ function run(args) { if (Module['calledRun']) return; // run may have just been called through dependencies being fulfilled just in this very frame function doRun() { + if (Module['calledRun']) return; // run may have just been called while the async setStatus time below was happening + Module['calledRun'] = true; + ensureInitRuntime(); preMain(); - assert(!Module['calledRun']); - Module['calledRun'] = true; + if (ENVIRONMENT_IS_WEB && preloadStartTime !== null) { + Module.printErr('pre-main prep time: ' + (Date.now() - preloadStartTime) + ' ms'); + } + if (Module['_main'] && shouldRunNow) { Module['callMain'](args); } @@ -178,7 +181,13 @@ function abort(text) { ABORT = true; EXITSTATUS = 1; - throw 'abort() at ' + stackTrace(); +#if ASSERTIONS == 0 + var extra = '\nIf this abort() is unexpected, build with -s ASSERTIONS=1 which can give more information.'; +#else + var extra = ''; +#endif + + throw 'abort() at ' + stackTrace() + extra; } Module['abort'] = Module.abort = abort; @@ -201,6 +210,10 @@ if (Module['noInitialRun']) { shouldRunNow = false; } +#if NO_EXIT_RUNTIME +Module["noExitRuntime"] = true; +#endif + run(); // {{POST_RUN_ADDITIONS}} diff --git a/src/preamble.js b/src/preamble.js index ac6ee7b3..89ab5026 100644 --- a/src/preamble.js +++ b/src/preamble.js @@ -6,6 +6,8 @@ {{RUNTIME}} +Module['Runtime'] = Runtime; + #if ASM_JS #if RESERVED_FUNCTION_POINTERS function jsCall() { @@ -21,6 +23,7 @@ Module.print = Module.printErr = function(){}; #endif #if SAFE_HEAP +#if ASM_JS == 0 //======================================== // Debugging tools - Heap //======================================== @@ -166,6 +169,45 @@ function SAFE_HEAP_FILL_HISTORY(from, to, type) { } //========================================== +#else +// ASM_JS safe heap + +function getSafeHeapType(bytes, isFloat) { + switch (bytes) { + case 1: return 'i8'; + case 2: return 'i16'; + case 4: return isFloat ? 'float' : 'i32'; + case 8: return 'double'; + default: assert(0); + } +} + +function SAFE_HEAP_STORE(dest, value, bytes, isFloat) { +#if SAFE_HEAP_LOG + Module.print('SAFE_HEAP store: ' + [dest, value, bytes, isFloat]); +#endif + assert(dest > 0, 'segmentation fault'); + assert(dest % bytes === 0, 'alignment error'); + assert(dest < Math.max(DYNAMICTOP, STATICTOP), 'segmentation fault (high)'); + assert(DYNAMICTOP <= TOTAL_MEMORY); + setValue(dest, value, getSafeHeapType(bytes, isFloat), 1); +} + +function SAFE_HEAP_LOAD(dest, bytes, isFloat, unsigned) { + assert(dest > 0, 'segmentation fault'); + assert(dest % bytes === 0, 'alignment error'); + assert(dest < Math.max(DYNAMICTOP, STATICTOP), 'segmentation fault (high)'); + assert(DYNAMICTOP <= TOTAL_MEMORY); + var type = getSafeHeapType(bytes, isFloat); + var ret = getValue(dest, type, 1); + if (unsigned) ret = unSign(ret, parseInt(type.substr(1)), 1); +#if SAFE_HEAP_LOG + Module.print('SAFE_HEAP load: ' + [dest, ret, bytes, isFloat, unsigned]); +#endif + return ret; +} + +#endif #endif #if CHECK_HEAP_ALIGN @@ -641,6 +683,137 @@ function stringToUTF32(str, outPtr) { Module['stringToUTF32'] = stringToUTF32; function demangle(func) { + var i = 3; + // params, etc. + var basicTypes = { + 'v': 'void', + 'b': 'bool', + 'c': 'char', + 's': 'short', + 'i': 'int', + 'l': 'long', + 'f': 'float', + 'd': 'double', + 'w': 'wchar_t', + 'a': 'signed char', + 'h': 'unsigned char', + 't': 'unsigned short', + 'j': 'unsigned int', + 'm': 'unsigned long', + 'x': 'long long', + 'y': 'unsigned long long', + 'z': '...' + }; + var subs = []; + var first = true; + function dump(x) { + //return; + if (x) Module.print(x); + Module.print(func); + var pre = ''; + for (var a = 0; a < i; a++) pre += ' '; + Module.print (pre + '^'); + } + function parseNested() { + i++; + if (func[i] === 'K') i++; // ignore const + var parts = []; + while (func[i] !== 'E') { + if (func[i] === 'S') { // substitution + i++; + var next = func.indexOf('_', i); + var num = func.substring(i, next) || 0; + parts.push(subs[num] || '?'); + i = next+1; + continue; + } + if (func[i] === 'C') { // constructor + parts.push(parts[parts.length-1]); + i += 2; + continue; + } + var size = parseInt(func.substr(i)); + var pre = size.toString().length; + if (!size || !pre) { i--; break; } // counter i++ below us + var curr = func.substr(i + pre, size); + parts.push(curr); + subs.push(curr); + i += pre + size; + } + i++; // skip E + return parts; + } + function parse(rawList, limit, allowVoid) { // main parser + limit = limit || Infinity; + var ret = '', list = []; + function flushList() { + return '(' + list.join(', ') + ')'; + } + var name; + if (func[i] === 'N') { + // namespaced N-E + name = parseNested().join('::'); + limit--; + if (limit === 0) return rawList ? [name] : name; + } else { + // not namespaced + if (func[i] === 'K' || (first && func[i] === 'L')) i++; // ignore const and first 'L' + var size = parseInt(func.substr(i)); + if (size) { + var pre = size.toString().length; + name = func.substr(i + pre, size); + i += pre + size; + } + } + first = false; + if (func[i] === 'I') { + i++; + var iList = parse(true); + var iRet = parse(true, 1, true); + ret += iRet[0] + ' ' + name + '<' + iList.join(', ') + '>'; + } else { + ret = name; + } + paramLoop: while (i < func.length && limit-- > 0) { + //dump('paramLoop'); + var c = func[i++]; + if (c in basicTypes) { + list.push(basicTypes[c]); + } else { + switch (c) { + case 'P': list.push(parse(true, 1, true)[0] + '*'); break; // pointer + case 'R': list.push(parse(true, 1, true)[0] + '&'); break; // reference + case 'L': { // literal + i++; // skip basic type + var end = func.indexOf('E', i); + var size = end - i; + list.push(func.substr(i, size)); + i += size + 2; // size + 'EE' + break; + } + case 'A': { // array + var size = parseInt(func.substr(i)); + i += size.toString().length; + if (func[i] !== '_') throw '?'; + i++; // skip _ + list.push(parse(true, 1, true)[0] + ' [' + size + ']'); + break; + } + case 'E': break paramLoop; + default: ret += '?' + c; break paramLoop; + } + } + } + if (!allowVoid && list.length === 1 && list[0] === 'void') list = []; // avoid (void) + if (rawList) { + if (ret) { + list.push(ret + '?'); + } + return list; + } else { + return ret + flushList(); + } + } try { // Special-case the entry point, since its name differs from other name mangling. if (func == 'Object._main' || func == '_main') { @@ -654,130 +827,6 @@ function demangle(func) { case 'n': return 'operator new()'; case 'd': return 'operator delete()'; } - var i = 3; - // params, etc. - var basicTypes = { - 'v': 'void', - 'b': 'bool', - 'c': 'char', - 's': 'short', - 'i': 'int', - 'l': 'long', - 'f': 'float', - 'd': 'double', - 'w': 'wchar_t', - 'a': 'signed char', - 'h': 'unsigned char', - 't': 'unsigned short', - 'j': 'unsigned int', - 'm': 'unsigned long', - 'x': 'long long', - 'y': 'unsigned long long', - 'z': '...' - }; - function dump(x) { - //return; - if (x) Module.print(x); - Module.print(func); - var pre = ''; - for (var a = 0; a < i; a++) pre += ' '; - Module.print (pre + '^'); - } - var subs = []; - function parseNested() { - i++; - if (func[i] === 'K') i++; // ignore const - var parts = []; - while (func[i] !== 'E') { - if (func[i] === 'S') { // substitution - i++; - var next = func.indexOf('_', i); - var num = func.substring(i, next) || 0; - parts.push(subs[num] || '?'); - i = next+1; - continue; - } - if (func[i] === 'C') { // constructor - parts.push(parts[parts.length-1]); - i += 2; - continue; - } - var size = parseInt(func.substr(i)); - var pre = size.toString().length; - if (!size || !pre) { i--; break; } // counter i++ below us - var curr = func.substr(i + pre, size); - parts.push(curr); - subs.push(curr); - i += pre + size; - } - i++; // skip E - return parts; - } - var first = true; - function parse(rawList, limit, allowVoid) { // main parser - limit = limit || Infinity; - var ret = '', list = []; - function flushList() { - return '(' + list.join(', ') + ')'; - } - var name; - if (func[i] === 'N') { - // namespaced N-E - name = parseNested().join('::'); - limit--; - if (limit === 0) return rawList ? [name] : name; - } else { - // not namespaced - if (func[i] === 'K' || (first && func[i] === 'L')) i++; // ignore const and first 'L' - var size = parseInt(func.substr(i)); - if (size) { - var pre = size.toString().length; - name = func.substr(i + pre, size); - i += pre + size; - } - } - first = false; - if (func[i] === 'I') { - i++; - var iList = parse(true); - var iRet = parse(true, 1, true); - ret += iRet[0] + ' ' + name + '<' + iList.join(', ') + '>'; - } else { - ret = name; - } - paramLoop: while (i < func.length && limit-- > 0) { - //dump('paramLoop'); - var c = func[i++]; - if (c in basicTypes) { - list.push(basicTypes[c]); - } else { - switch (c) { - case 'P': list.push(parse(true, 1, true)[0] + '*'); break; // pointer - case 'R': list.push(parse(true, 1, true)[0] + '&'); break; // reference - case 'L': { // literal - i++; // skip basic type - var end = func.indexOf('E', i); - var size = end - i; - list.push(func.substr(i, size)); - i += size + 2; // size + 'EE' - break; - } - case 'A': { // array - var size = parseInt(func.substr(i)); - i += size.toString().length; - if (func[i] !== '_') throw '?'; - i++; // skip _ - list.push(parse(true, 1, true)[0] + ' [' + size + ']'); - break; - } - case 'E': break paramLoop; - default: ret += '?' + c; break paramLoop; - } - } - } - if (!allowVoid && list.length === 1 && list[0] === 'void') list = []; // avoid (void) - return rawList ? list : ret + flushList(); - } return parse(); } catch(e) { return func; @@ -818,33 +867,19 @@ var DYNAMIC_BASE = 0, DYNAMICTOP = 0; // dynamic area handled by sbrk #if USE_TYPED_ARRAYS function enlargeMemory() { #if ALLOW_MEMORY_GROWTH == 0 -#if ASM_JS == 0 abort('Cannot enlarge memory arrays. Either (1) compile with -s TOTAL_MEMORY=X with X higher than the current value ' + TOTAL_MEMORY + ', (2) compile with ALLOW_MEMORY_GROWTH which adjusts the size at runtime but prevents some optimizations, or (3) set Module.TOTAL_MEMORY before the program runs.'); #else - abort('Cannot enlarge memory arrays in asm.js. Either (1) compile with -s TOTAL_MEMORY=X with X higher than the current value ' + TOTAL_MEMORY + ', or (2) set Module.TOTAL_MEMORY before the program runs.'); -#endif -#else // TOTAL_MEMORY is the current size of the actual array, and DYNAMICTOP is the new top. #if ASSERTIONS - Module.printErr('Warning: Enlarging memory arrays, this is not fast, and ALLOW_MEMORY_GROWTH is not fully tested with all optimizations on! ' + [DYNAMICTOP, TOTAL_MEMORY]); // We perform safe elimination instead of elimination in this mode, but if you see this error, try to disable it and other optimizations entirely + Module.printErr('Warning: Enlarging memory arrays, this is not fast! ' + [DYNAMICTOP, TOTAL_MEMORY]); assert(DYNAMICTOP >= TOTAL_MEMORY); assert(TOTAL_MEMORY > 4); // So the loop below will not be infinite #endif - while (TOTAL_MEMORY <= DYNAMICTOP) { // Simple heuristic. Override enlargeMemory() if your program has something more optimal for it + + while (TOTAL_MEMORY <= DYNAMICTOP) { // Simple heuristic. TOTAL_MEMORY = alignMemoryPage(2*TOTAL_MEMORY); } assert(TOTAL_MEMORY <= Math.pow(2, 30)); // 2^30==1GB is a practical maximum - 2^31 is already close to possible negative numbers etc. -#if USE_TYPED_ARRAYS == 1 - var oldIHEAP = IHEAP; - Module['HEAP'] = Module['IHEAP'] = HEAP = IHEAP = new Int32Array(TOTAL_MEMORY); - IHEAP.set(oldIHEAP); - IHEAPU = new Uint32Array(IHEAP.buffer); -#if USE_FHEAP - var oldFHEAP = FHEAP; - Module['FHEAP'] = FHEAP = new Float64Array(TOTAL_MEMORY); - FHEAP.set(oldFHEAP); -#endif -#endif #if USE_TYPED_ARRAYS == 2 var oldHEAP8 = HEAP8; var buffer = new ArrayBuffer(TOTAL_MEMORY); @@ -857,6 +892,11 @@ function enlargeMemory() { Module['HEAPF32'] = HEAPF32 = new Float32Array(buffer); Module['HEAPF64'] = HEAPF64 = new Float64Array(buffer); HEAP8.set(oldHEAP8); +#else + abort('cannot enlarge memory arrays in non-ta2 modes'); +#endif +#if ASM_JS + _emscripten_replace_memory(HEAP8, HEAP16, HEAP32, HEAPU8, HEAPU16, HEAPU32, HEAPF32, HEAPF64); #endif #endif } @@ -885,7 +925,7 @@ if (totalMemory !== TOTAL_MEMORY) { #if USE_TYPED_ARRAYS // check for full engine support (use string 'subarray' to avoid closure compiler confusion) assert(typeof Int32Array !== 'undefined' && typeof Float64Array !== 'undefined' && !!(new Int32Array(1)['subarray']) && !!(new Int32Array(1)['set']), - 'Cannot fallback to non-typed array case: Code is too specialized'); + 'JS engine does not provide full typed array support'); #if USE_TYPED_ARRAYS == 1 HEAP = IHEAP = new Int32Array(TOTAL_MEMORY); @@ -985,6 +1025,11 @@ function preMain() { } function exitRuntime() { +#if ASSERTIONS + if (ENVIRONMENT_IS_WEB || ENVIRONMENT_IS_WORKER) { + Module.printErr('Exiting runtime. Any attempt to access the compiled C code may fail from now. If you want to keep the runtime alive, set Module["noExitRuntime"] = true or build with -s NO_EXIT_RUNTIME=1'); + } +#endif callRuntimeCallbacks(__ATEXIT__); } diff --git a/src/proxyClient.js b/src/proxyClient.js index 8f4ad7a6..2d1c76fe 100644 --- a/src/proxyClient.js +++ b/src/proxyClient.js @@ -3,9 +3,39 @@ Module.ctx = Module.canvas.getContext('2d'); +// render + +var renderFrameData = null; + +function renderFrame() { + var dst = Module.canvasData.data; + if (dst.set) { + dst.set(renderFrameData); + } else { + for (var i = 0; i < renderFrameData.length; i++) { + dst[i] = renderFrameData[i]; + } + } + Module.ctx.putImageData(Module.canvasData, 0, 0); + renderFrameData = null; +} + +window.requestAnimationFrame = window.requestAnimationFrame || window.mozRequestAnimationFrame || + window.webkitRequestAnimationFrame || window.msRequestAnimationFrame || + renderFrame; + +// end render + var worker = new Worker('{{{ filename }}}.js'); +var workerResponded = false; + worker.onmessage = function worker_onmessage(event) { + if (!workerResponded) { + workerResponded = true; + if (Module.setStatus) Module.setStatus(''); + } + var data = event.data; switch (data.target) { case 'stdout': { @@ -26,20 +56,18 @@ worker.onmessage = function worker_onmessage(event) { Module.canvas.width = data.width; Module.canvas.height = data.height; Module.canvasData = Module.ctx.getImageData(0, 0, data.width, data.height); - postMessage({ target: 'canvas', boundingClientRect: Module.canvas.getBoundingClientRect() }); + worker.postMessage({ target: 'canvas', boundingClientRect: cloneObject(Module.canvas.getBoundingClientRect()) }); break; } case 'render': { - var src = data.image.data; - var dst = Module.canvasData.data; - if (dst.set) { - dst.set(src); + if (renderFrameData) { + // previous image was not rendered yet, just update image + renderFrameData = data.image.data; } else { - for (var i = 0; i < src.length; i++) { - dst[i] = src[i]; - } + // previous image was rendered so update image and request another frame + renderFrameData = data.image.data; + window.requestAnimationFrame(renderFrame); } - Module.ctx.putImageData(Module.canvasData, 0, 0); break; } default: throw 'eh?'; @@ -50,7 +78,7 @@ worker.onmessage = function worker_onmessage(event) { } }; -function cloneEvent(event) { +function cloneObject(event) { var ret = {}; for (var x in event) { if (x == x.toUpperCase()) continue; @@ -62,20 +90,20 @@ function cloneEvent(event) { ['keydown', 'keyup', 'keypress', 'blur', 'visibilitychange'].forEach(function(event) { document.addEventListener(event, function(event) { - worker.postMessage({ target: 'document', event: cloneEvent(event) }); + worker.postMessage({ target: 'document', event: cloneObject(event) }); event.preventDefault(); }); }); ['unload'].forEach(function(event) { window.addEventListener(event, function(event) { - worker.postMessage({ target: 'window', event: cloneEvent(event) }); + worker.postMessage({ target: 'window', event: cloneObject(event) }); }); }); ['mousedown', 'mouseup', 'mousemove', 'DOMMouseScroll', 'mousewheel', 'mouseout'].forEach(function(event) { Module.canvas.addEventListener(event, function(event) { - worker.postMessage({ target: 'canvas', event: cloneEvent(event) }); + worker.postMessage({ target: 'canvas', event: cloneObject(event) }); event.preventDefault(); }, true); }); diff --git a/src/proxyWorker.js b/src/proxyWorker.js index 5d34b900..cfe0c26e 100644 --- a/src/proxyWorker.js +++ b/src/proxyWorker.js @@ -79,11 +79,13 @@ document.createElement = function document_createElement(what) { } }; -var console = { - log: function(x) { - Module.printErr(x); - } -}; +if (typeof console === 'undefined') { + var console = { + log: function(x) { + Module.printErr(x); + } + }; +} Module.canvas = document.createElement('canvas'); diff --git a/src/relooper/README.markdown b/src/relooper/README.markdown deleted file mode 100644 index 9b0187b3..00000000 --- a/src/relooper/README.markdown +++ /dev/null @@ -1,14 +0,0 @@ - -Relooper -======== - -This is an optimized C++ implemention of the Relooper algorithm originally -developed as part of Emscripten. This implementation includes optimizations -added since the original academic paper [1] - see paper.pdf - was published -about it, and is written in an LLVM-friendly way with the goal of inclusion -in upstream LLVM. - -License: MIT&LLVM - -[1] Alon Zakai. 2011. Emscripten: an LLVM-to-JavaScript compiler. In Proceedings of the ACM international conference companion on Object oriented programming systems languages and applications companion (SPLASH '11). ACM, New York, NY, USA, 301-312. DOI=10.1145/2048147.2048224 http://doi.acm.org/10.1145/2048147.2048224 - diff --git a/src/relooper/README.md b/src/relooper/README.md new file mode 100644 index 00000000..a4073a77 --- /dev/null +++ b/src/relooper/README.md @@ -0,0 +1,12 @@ +Relooper +======== + +This is an optimized C++ implemention of the Relooper algorithm originally developed as part +of Emscripten. This implementation includes optimizations added since the original academic +paper [1] - see paper.pdf - was published about it, and is written in an LLVM-friendly way +with the goal of inclusion in upstream LLVM. + +[1] Alon Zakai. 2011. Emscripten: an LLVM-to-JavaScript compiler. In Proceedings of the ACM +international conference companion on Object oriented programming systems languages and +applications companion (SPLASH '11). ACM, New York, NY, USA, 301-312. +DOI=10.1145/2048147.2048224 http://doi.acm.org/10.1145/2048147.2048224 diff --git a/src/relooper/Relooper.cpp b/src/relooper/Relooper.cpp index d5772c62..780a6d59 100644 --- a/src/relooper/Relooper.cpp +++ b/src/relooper/Relooper.cpp @@ -1,3 +1,7 @@ +// We are implementing the Relooper C API, so always export from this file. +#ifndef RELOOPERDLL_EXPORTS +#define RELOOPERDLL_EXPORTS +#endif #include "Relooper.h" @@ -13,8 +17,8 @@ typedef std::string ministring; #endif -template <class T, class U> bool contains(const T& container, const U& contained) { - return container.find(contained) != container.end(); +template <class T, class U> static bool contains(const T& container, const U& contained) { + return container.count(contained); } #if DEBUG @@ -118,7 +122,7 @@ void Branch::Render(Block *Target, bool SetLabel) { if (Code) PrintIndented("%s\n", Code); if (SetLabel) PrintIndented("label = %d;\n", Target->Id); if (Ancestor) { - if (Type != Direct) { + if (Type == Break || Type == Continue) { if (Labeled) { PrintIndented("%s L%d;\n", Type == Break ? "break" : "continue", Ancestor->Id); } else { @@ -283,6 +287,11 @@ void Block::Render(bool InLoop) { Details->Render(Target, SetCurrLabel); if (HasFusedContent) { Fused->InnerMap.find(Target)->second->Render(InLoop); + } else if (Details->Type == Branch::Nested) { + // Nest the parent content here, and remove it from showing up afterwards as Next + assert(Parent->Next); + Parent->Next->Render(InLoop); + Parent->Next = NULL; } if (useSwitch && iter != ProcessedBranchesOut.end()) { PrintIndented("break;\n"); @@ -322,12 +331,26 @@ void MultipleShape::RenderLoopPostfix() { void MultipleShape::Render(bool InLoop) { RenderLoopPrefix(); - bool First = true; + + // We know that blocks with the same Id were split from the same source, so their contents are identical and they are logically the same, so re-merge them here + typedef std::map<int, Shape*> IdShapeMap; + IdShapeMap IdMap; for (BlockShapeMap::iterator iter = InnerMap.begin(); iter != InnerMap.end(); iter++) { + int Id = iter->first->Id; + IdShapeMap::iterator Test = IdMap.find(Id); + if (Test != IdMap.end()) { + assert(Shape::IsSimple(iter->second) && Shape::IsSimple(Test->second)); // we can only merge simple blocks, something horrible has gone wrong if we see anything else + continue; + } + IdMap[iter->first->Id] = iter->second; + } + + bool First = true; + for (IdShapeMap::iterator iter = IdMap.begin(); iter != IdMap.end(); iter++) { if (AsmJS) { - PrintIndented("%sif ((label|0) == %d) {\n", First ? "" : "else ", iter->first->Id); + PrintIndented("%sif ((label|0) == %d) {\n", First ? "" : "else ", iter->first); } else { - PrintIndented("%sif (label == %d) {\n", First ? "" : "else ", iter->first->Id); + PrintIndented("%sif (label == %d) {\n", First ? "" : "else ", iter->first); } First = false; Indenter::Indent(); @@ -391,8 +414,8 @@ Relooper::~Relooper() { for (unsigned i = 0; i < Shapes.size(); i++) delete Shapes[i]; } -void Relooper::AddBlock(Block *New) { - New->Id = BlockIdCounter++; +void Relooper::AddBlock(Block *New, int Id) { + New->Id = Id == -1 ? BlockIdCounter++ : Id; Blocks.push_back(New); } @@ -446,8 +469,7 @@ void Relooper::Calculate(Block *Entry) { for (BlockSet::iterator iter = Original->BranchesIn.begin(); iter != Original->BranchesIn.end(); iter++) { Block *Prior = *iter; Block *Split = new Block(Original->Code, Original->BranchVar); - Parent->AddBlock(Split); - PrintDebug(" to %d\n", Split->Id); + Parent->AddBlock(Split, Original->Id); Split->BranchesIn.insert(Prior); Branch *Details = Prior->BranchesOut[Original]; Prior->BranchesOut[Split] = new Branch(Details->Condition, Details->Code); @@ -633,7 +655,7 @@ void Relooper::Calculate(Block *Entry) { Block *Curr = *iter; for (BlockBranchMap::iterator iter = Curr->BranchesOut.begin(); iter != Curr->BranchesOut.end(); iter++) { Block *Target = iter->first; - if (Hoisted.find(Target) == Hoisted.end() && NextEntries.find(Target) == NextEntries.end()) { + if (!contains(Hoisted, Target) && !contains(NextEntries, Target)) { // abort this hoisting abort = true; break; @@ -1060,12 +1082,48 @@ void Relooper::Calculate(Block *Entry) { SHAPE_SWITCH(Root, { if (Simple->Inner->BranchVar) LastLoop = NULL; // a switch clears out the loop (TODO: only for breaks, not continue) - // If there is a next block, we already know at Simple creation time to make direct branches, - // and we can do nothing more. If there is no next however, then Natural is where we will - // go to by doing nothing, so we can potentially optimize some branches to direct. if (Simple->Next) { + if (!Simple->Inner->BranchVar && Simple->Inner->ProcessedBranchesOut.size() == 2) { + // If there is a next block, we already know at Simple creation time to make direct branches, + // and we can do nothing more in general. But, we try to optimize the case of a break and + // a direct: This would normally be if (break?) { break; } .. but if we + // make sure to nest the else, we can save the break, if (!break?) { .. } . This is also + // better because the more canonical nested form is easier to further optimize later. The + // downside is more nesting, which adds to size in builds with whitespace. + // Note that we avoid switches, as it complicates control flow and is not relevant + // for the common case we optimize here. + bool Found = false; + bool Abort = false; + for (BlockBranchMap::iterator iter = Simple->Inner->ProcessedBranchesOut.begin(); iter != Simple->Inner->ProcessedBranchesOut.end(); iter++) { + Block *Target = iter->first; + Branch *Details = iter->second; + if (Details->Type == Branch::Break) { + Found = true; + if (!contains(NaturalBlocks, Target)) Abort = true; + } else if (Details->Type != Branch::Direct) { + Abort = true; + } + } + if (Found && !Abort) { + for (BlockBranchMap::iterator iter = Simple->Inner->ProcessedBranchesOut.begin(); iter != Simple->Inner->ProcessedBranchesOut.end(); iter++) { + Block *Target = iter->first; + Branch *Details = iter->second; + if (Details->Type == Branch::Break) { + Details->Type = Branch::Direct; + if (MultipleShape *Multiple = Shape::IsMultiple(Details->Ancestor)) { + Multiple->NeedLoop--; + } + } else { + assert(Details->Type == Branch::Direct); + Details->Type = Branch::Nested; + } + } + } + } Next = Simple->Next; } else { + // If there is no next then Natural is where we will + // go to by doing nothing, so we can potentially optimize some branches to direct. for (BlockBranchMap::iterator iter = Simple->Inner->ProcessedBranchesOut.begin(); iter != Simple->Inner->ProcessedBranchesOut.end(); iter++) { Block *Target = iter->first; Branch *Details = iter->second; @@ -1123,7 +1181,7 @@ void Relooper::Calculate(Block *Entry) { for (BlockBranchMap::iterator iter = Simple->Inner->ProcessedBranchesOut.begin(); iter != Simple->Inner->ProcessedBranchesOut.end(); iter++) { Block *Target = iter->first; Branch *Details = iter->second; - if (Details->Type != Branch::Direct) { + if (Details->Type == Branch::Break || Details->Type == Branch::Continue) { assert(LoopStack.size() > 0); if (Details->Ancestor != LoopStack.top() && Details->Labeled) { LabeledShape *Labeled = Shape::IsLabeled(Details->Ancestor); @@ -1256,7 +1314,7 @@ VoidIntMap __blockDebugMap__; // maps block pointers in currently running code t extern "C" { -void rl_set_output_buffer(char *buffer, int size) { +RELOOPERDLL_API void rl_set_output_buffer(char *buffer, int size) { #if DEBUG printf("#include \"Relooper.h\"\n"); printf("int main() {\n"); @@ -1266,15 +1324,15 @@ void rl_set_output_buffer(char *buffer, int size) { Relooper::SetOutputBuffer(buffer, size); } -void rl_make_output_buffer(int size) { +RELOOPERDLL_API void rl_make_output_buffer(int size) { Relooper::SetOutputBuffer((char*)malloc(size), size); } -void rl_set_asm_js_mode(int on) { +RELOOPERDLL_API void rl_set_asm_js_mode(int on) { Relooper::SetAsmJSMode(on); } -void *rl_new_block(const char *text, const char *branch_var) { +RELOOPERDLL_API void *rl_new_block(const char *text, const char *branch_var) { Block *ret = new Block(text, branch_var); #if DEBUG printf(" void *b%d = rl_new_block(\"// code %d\");\n", ret->Id, ret->Id); @@ -1284,21 +1342,21 @@ void *rl_new_block(const char *text, const char *branch_var) { return ret; } -void rl_delete_block(void *block) { +RELOOPERDLL_API void rl_delete_block(void *block) { #if DEBUG printf(" rl_delete_block(block_map[%d]);\n", ((Block*)block)->Id); #endif delete (Block*)block; } -void rl_block_add_branch_to(void *from, void *to, const char *condition, const char *code) { +RELOOPERDLL_API void rl_block_add_branch_to(void *from, void *to, const char *condition, const char *code) { #if DEBUG printf(" rl_block_add_branch_to(block_map[%d], block_map[%d], %s%s%s, %s%s%s);\n", ((Block*)from)->Id, ((Block*)to)->Id, condition ? "\"" : "", condition ? condition : "NULL", condition ? "\"" : "", code ? "\"" : "", code ? code : "NULL", code ? "\"" : ""); #endif ((Block*)from)->AddBranchTo((Block*)to, condition, code); } -void *rl_new_relooper() { +RELOOPERDLL_API void *rl_new_relooper() { #if DEBUG printf(" void *block_map[10000];\n"); printf(" void *rl = rl_new_relooper();\n"); @@ -1306,18 +1364,18 @@ void *rl_new_relooper() { return new Relooper; } -void rl_delete_relooper(void *relooper) { +RELOOPERDLL_API void rl_delete_relooper(void *relooper) { delete (Relooper*)relooper; } -void rl_relooper_add_block(void *relooper, void *block) { +RELOOPERDLL_API void rl_relooper_add_block(void *relooper, void *block) { #if DEBUG printf(" rl_relooper_add_block(rl, block_map[%d]);\n", ((Block*)block)->Id); #endif ((Relooper*)relooper)->AddBlock((Block*)block); } -void rl_relooper_calculate(void *relooper, void *entry) { +RELOOPERDLL_API void rl_relooper_calculate(void *relooper, void *entry) { #if DEBUG printf(" rl_relooper_calculate(rl, block_map[%d]);\n", ((Block*)entry)->Id); printf(" rl_relooper_render(rl);\n"); @@ -1329,7 +1387,7 @@ void rl_relooper_calculate(void *relooper, void *entry) { ((Relooper*)relooper)->Calculate((Block*)entry); } -void rl_relooper_render(void *relooper) { +RELOOPERDLL_API void rl_relooper_render(void *relooper) { ((Relooper*)relooper)->Render(); } diff --git a/src/relooper/Relooper.h b/src/relooper/Relooper.h index 6b9394db..152bae0e 100644 --- a/src/relooper/Relooper.h +++ b/src/relooper/Relooper.h @@ -24,9 +24,11 @@ struct Shape; // Info about a branching from one block to another struct Branch { enum FlowType { - Direct = 0, // We will directly reach the right location through other means, no need for continue or break + Direct = 0, // We will directly reach the right location through other means, no need for continue or break Break = 1, - Continue = 2 + Continue = 2, + Nested = 3 // This code is directly reached, but we must be careful to ensure it is nested in an if - it is not reached + // unconditionally, other code paths exist alongside it that we need to make sure do not intertwine }; Shape *Ancestor; // If not NULL, this shape is the relevant one for purposes of getting to the target block. We break or continue on it Branch::FlowType Type; // If Ancestor is not NULL, this says whether to break or continue @@ -57,9 +59,9 @@ struct Block { BlockBranchMap ProcessedBranchesOut; BlockSet ProcessedBranchesIn; Shape *Parent; // The shape we are directly inside - int Id; // A unique identifier, defined when added to relooper + int Id; // A unique identifier, defined when added to relooper. Note that this uniquely identifies a *logical* block - if we split it, the two instances have the same content *and* the same Id const char *Code; // The string representation of the code in this block. Owning pointer (we copy the input) - const char *BranchVar; // If we have more than one branch out, the variable whose value determines where we go + const char *BranchVar; // A variable whose value determines where we go; if this is not NULL, emit a switch on that variable bool IsCheckedMultipleEntry; // If true, we are a multiple entry, so reaching us requires setting the label variable Block(const char *CodeInit, const char *BranchVarInit); @@ -191,7 +193,7 @@ struct Relooper { Relooper(); ~Relooper(); - void AddBlock(Block *New); + void AddBlock(Block *New, int Id=-1); // Calculates the shapes void Calculate(Block *Entry); diff --git a/src/relooper/test.cpp b/src/relooper/test.cpp index b4ce669c..78de9e64 100644 --- a/src/relooper/test.cpp +++ b/src/relooper/test.cpp @@ -312,7 +312,97 @@ int main() { printf("\n\n", "the_var"); r.Render(); - puts(buffer); + puts(r.GetOutputBuffer()); + } + + if (1) { + Relooper::MakeOutputBuffer(10); + + printf("\n\n-- If chain (optimized) --\n\n"); + + Block *b_a = new Block("// block A\n", NULL); + Block *b_b = new Block("// block B\n", NULL); + Block *b_c = new Block("// block C\n", NULL); + + b_a->AddBranchTo(b_b, "a == 10", NULL); + b_a->AddBranchTo(b_c, NULL, NULL); + + b_b->AddBranchTo(b_c, NULL, NULL); + + Relooper r; + r.AddBlock(b_a); + r.AddBlock(b_b); + r.AddBlock(b_c); + + r.Calculate(b_a); + r.Render(); + + puts(r.GetOutputBuffer()); + } + + if (1) { + Relooper::MakeOutputBuffer(10); + + printf("\n\n-- If chain (optimized) --\n\n"); + + Block *b_a = new Block("// block A\n", NULL); + Block *b_b = new Block("// block B\n", NULL); + Block *b_c = new Block("// block C\n", NULL); + Block *b_d = new Block("// block D\n", NULL); + + b_a->AddBranchTo(b_b, "a == 10", NULL); + b_a->AddBranchTo(b_d, NULL, NULL); + + b_b->AddBranchTo(b_c, "b == 10", NULL); + b_b->AddBranchTo(b_d, NULL, NULL); + + b_c->AddBranchTo(b_d, NULL, NULL); + + Relooper r; + r.AddBlock(b_a); + r.AddBlock(b_b); + r.AddBlock(b_c); + r.AddBlock(b_d); + + r.Calculate(b_a); + r.Render(); + + puts(r.GetOutputBuffer()); + } + + if (1) { + Relooper::MakeOutputBuffer(10); + + printf("\n\n-- If chain (optimized, long) --\n\n"); + + Block *b_a = new Block("// block A\n", NULL); + Block *b_b = new Block("// block B\n", NULL); + Block *b_c = new Block("// block C\n", NULL); + Block *b_d = new Block("// block D\n", NULL); + Block *b_e = new Block("// block E\n", NULL); + + b_a->AddBranchTo(b_b, "a == 10", NULL); + b_a->AddBranchTo(b_e, NULL, NULL); + + b_b->AddBranchTo(b_c, "b == 10", NULL); + b_b->AddBranchTo(b_e, NULL, NULL); + + b_c->AddBranchTo(b_d, "c == 10", NULL); + b_c->AddBranchTo(b_e, NULL, NULL); + + b_d->AddBranchTo(b_e, NULL, NULL); + + Relooper r; + r.AddBlock(b_a); + r.AddBlock(b_b); + r.AddBlock(b_c); + r.AddBlock(b_d); + r.AddBlock(b_e); + + r.Calculate(b_a); + r.Render(); + + puts(r.GetOutputBuffer()); } } diff --git a/src/relooper/test.txt b/src/relooper/test.txt index cb02b867..7d79e037 100644 --- a/src/relooper/test.txt +++ b/src/relooper/test.txt @@ -91,7 +91,7 @@ } default: { var $x_1 = $x_0; - label = 8; + label = 7; break L1; } } @@ -106,7 +106,7 @@ } } } - if (label == 8) { + if (label == 7) { // code 7 } // code 4 @@ -324,10 +324,6 @@ label = 1; L0: while(1) { switch(label|0) { - case 3: { - // block C - break; - } case 1: { // block A if (check == 10) { @@ -357,6 +353,49 @@ } break; } + case 3: { + // block C + break; + } + } + } + + + +-- If chain (optimized) -- + + // block A + if (a == 10) { + // block B + } + // block C + + + +-- If chain (optimized) -- + + // block A + if (a == 10) { + // block B + if (b == 10) { + // block C + } + } + // block D + + + +-- If chain (optimized, long) -- + + // block A + if (a == 10) { + // block B + if (b == 10) { + // block C + if (c == 10) { + // block D + } } } + // block E diff --git a/src/runtime.js b/src/runtime.js index cd3afb4b..2ae68279 100644 --- a/src/runtime.js +++ b/src/runtime.js @@ -49,7 +49,7 @@ var RuntimeGenerator = { stackExit: function(initial, force) { if (initial === 0 && SKIP_STACK_IN_SMALL && !force) return ''; var ret = ''; - if (SAFE_HEAP) { + if (SAFE_HEAP && !ASM_JS) { ret += 'var i = sp; while ((i|0) < (STACKTOP|0)) { SAFE_HEAP_CLEAR(i|0); i = (i+1)|0 }'; } return ret += 'STACKTOP=sp'; @@ -185,10 +185,10 @@ var Runtime = { // type can be a native type or a struct (or null, for structs we only look at size here) getAlignSize: function(type, size, vararg) { // we align i64s and doubles on 64-bit boundaries, unlike x86 -#if TARGET_LE32 == 1 +#if TARGET_ASMJS_UNKNOWN_EMSCRIPTEN == 1 if (vararg) return 8; #endif -#if TARGET_LE32 +#if TARGET_ASMJS_UNKNOWN_EMSCRIPTEN if (!vararg && (type == 'i64' || type == 'double')) return 8; if (!type) return Math.min(size, 8); // align structures internally to 64 bits #endif @@ -336,6 +336,9 @@ var Runtime = { #if ASM_JS if (!args.splice) args = Array.prototype.slice.call(args); args.splice(0, 0, ptr); +#if ASSERTIONS + assert(('dynCall_' + sig) in Module, 'bad function pointer type - no table for sig \'' + sig + '\''); +#endif return Module['dynCall_' + sig].apply(null, args); #else return FUNCTION_TABLE[ptr].apply(null, args); @@ -345,6 +348,9 @@ var Runtime = { assert(sig.length == 1); #endif #if ASM_JS +#if ASSERTIONS + assert(('dynCall_' + sig) in Module, 'bad function pointer type - no table for sig \'' + sig + '\''); +#endif return Module['dynCall_' + sig].call(null, ptr); #else return FUNCTION_TABLE[ptr](); @@ -393,7 +399,17 @@ var Runtime = { for (var i = 0; i < numArgs; i++) { args.push(String.fromCharCode(36) + i); // $0, $1 etc } - return Runtime.asmConstCache[code] = eval('(function(' + args.join(',') + '){ ' + Pointer_stringify(code) + ' })'); // new Function does not allow upvars in node + code = Pointer_stringify(code); + if (code[0] === '"') { + // tolerate EM_ASM("..code..") even though EM_ASM(..code..) is correct + if (code.indexOf('"', 1) === code.length-1) { + code = code.substr(1, code.length-2); + } else { + // something invalid happened, e.g. EM_ASM("..code($0)..", input) + abort('invalid EM_ASM input |' + code + '|. Please use EM_ASM(..code..) (no quotes) or EM_ASM({ ..code($0).. }, input) (to input values)'); + } + } + return Runtime.asmConstCache[code] = eval('(function(' + args.join(',') + '){ ' + code + ' })'); // new Function does not allow upvars in node }, warnOnce: function(text) { @@ -468,6 +484,11 @@ var Runtime = { return ret; } this.processJSString = function processJSString(string) { + /* TODO: use TextEncoder when present, + var encoder = new TextEncoder(); + encoder['encoding'] = "utf-8"; + var utf8Array = encoder['encode'](aMsg.data); + */ string = unescape(encodeURIComponent(string)); var ret = []; for (var i = 0; i < string.length; i++) { @@ -477,6 +498,19 @@ var Runtime = { } }, +#if RETAIN_COMPILER_SETTINGS + compilerSettings: {}, +#endif + + getCompilerSetting: function(name) { +#if RETAIN_COMPILER_SETTINGS == 0 + throw 'You must build with -s RETAIN_COMPILER_SETTINGS=1 for Runtime.getCompilerSetting or emscripten_get_compiler_setting to work'; +#else + if (!(name in Runtime.compilerSettings)) return 'invalid compiler setting: ' + name; + return Runtime.compilerSettings[name]; +#endif + }, + #if RUNTIME_DEBUG debug: true, // Switch to false at runtime to disable logging at the right times @@ -555,7 +589,7 @@ function getRuntime() { // Converts a value we have as signed, into an unsigned value. For // example, -1 in int32 would be a very large number as unsigned. -function unSign(value, bits, ignore, sig) { +function unSign(value, bits, ignore) { if (value >= 0) { return value; } @@ -568,7 +602,7 @@ function unSign(value, bits, ignore, sig) { // Converts a value we have as unsigned, into a signed value. For // example, 200 in a uint8 would be a negative number. -function reSign(value, bits, ignore, sig) { +function reSign(value, bits, ignore) { if (value <= 0) { return value; } @@ -602,3 +636,12 @@ function reSign(value, bits, ignore, sig) { // Then 'dynamic' memory for sbrk. Runtime.GLOBAL_BASE = Runtime.alignMemory(1); +if (RETAIN_COMPILER_SETTINGS) { + var blacklist = set('RELOOPER', 'STRUCT_INFO'); + for (var x in this) { + try { + if (x[0] !== '_' && !(x in blacklist) && x == x.toUpperCase() && (typeof this[x] === 'number' || typeof this[x] === 'string' || this.isArray())) Runtime.compilerSettings[x] = this[x]; + } catch(e){} + } +} + diff --git a/src/settings.js b/src/settings.js index 04571e8a..6e644a54 100644 --- a/src/settings.js +++ b/src/settings.js @@ -23,8 +23,8 @@ var QUANTUM_SIZE = 4; // This is the size of an individual field in a structure. // Changing this from the default of 4 is deprecated. var TARGET_X86 = 0; // For i386-pc-linux-gnu -var TARGET_LE32 = 1; // For le32-unknown-nacl. 1 is normal, 2 is for the fastcomp llvm - // backend using pnacl abi simplification +var TARGET_ASMJS_UNKNOWN_EMSCRIPTEN = 1; // For asmjs-unknown-emscripten. 1 is normal, 2 is for the fastcomp llvm + // backend using emscripten-customized abi simplification var CORRECT_SIGNS = 1; // Whether we make sure to convert unsigned values to signed values. // Decreases performance with additional runtime checks. Might not be @@ -48,6 +48,8 @@ var VERBOSE = 0; // When set to 1, will generate more verbose output during comp var INVOKE_RUN = 1; // Whether we will run the main() function. Disable if you embed the generated // code in your own, and will call main() yourself at the right time (which you // can do with Module.callMain(), with an optional parameter of commandline args). +var NO_EXIT_RUNTIME = 0; // If set, the runtime is not quit when main() completes (allowing code to + // run afterwards, for example from the browser main event loop). var INIT_HEAP = 0; // Whether to initialize memory anywhere other than the stack to 0. var TOTAL_STACK = 5*1024*1024; // The total stack size. There is no way to enlarge the stack, so this // value must be large enough for the program's requirements. If @@ -104,6 +106,9 @@ var FORCE_ALIGNED_MEMORY = 0; // If enabled, assumes all reads and writes are fu // for ways to help find places in your code where unaligned reads/writes are done - // you might be able to refactor your codebase to prevent them, which leads to // smaller and faster code, or even the option to turn this flag on. +var WARN_UNALIGNED = 0; // Warn at compile time about instructions that LLVM tells us are not fully aligned. + // This is useful to find places in your code where you might refactor to ensure proper + // alignment. (this option is fastcomp-only) var PRECISE_I64_MATH = 1; // If enabled, i64 addition etc. is emulated - which is slow but precise. If disabled, // we use the 'double trick' which is fast but incurs rounding at high values. // Note that we do not catch 32-bit multiplication by default (which must be done in @@ -121,10 +126,14 @@ var PRECISE_F32 = 0; // 0: Use JS numbers for floating-point values. These are 6 // 1: Model C++ floats precisely, using Math.fround, polyfilling when necessary. This // can be slow if the polyfill is used on heavy float32 computation. // 2: Model C++ floats precisely using Math.fround if available in the JS engine, otherwise - // use an empty polyfill. This will have less of a speed penalty than using the full - // polyfill in cases where engine support is not present. + // use an empty polyfill. This will have much less of a speed penalty than using the full + // polyfill in cases where engine support is not present. In addition, we can + // remove the empty polyfill calls themselves on the client when generating html, + // which should mean that this gives you the best of both worlds of 0 and 1, and is + // therefore recommended. var SIMD = 0; // Whether to emit SIMD code ( https://github.com/johnmccutchan/ecmascript_simd ) +var CLOSURE_COMPILER = 0; // Whether closure compiling is being run on this output var CLOSURE_ANNOTATIONS = 0; // If set, the generated code will be annotated for the closure // compiler. This potentially lets closure optimize the code better. @@ -264,8 +273,8 @@ var DISABLE_EXCEPTION_CATCHING = 0; // Disables generating code to actually catc // TODO: Make this also remove cxa_begin_catch etc., optimize relooper // for it, etc. (perhaps do all of this as preprocessing on .ll?) -var EXCEPTION_CATCHING_WHITELIST = []; // Enables catching exception in listed functions if - // DISABLE_EXCEPTION_CATCHING = 2 set +var EXCEPTION_CATCHING_WHITELIST = []; // Enables catching exception in the listed functions only, if + // DISABLE_EXCEPTION_CATCHING = 2 is set var EXECUTION_TIMEOUT = -1; // Throw an exception after X seconds - useful to debug infinite loops var CHECK_OVERFLOWS = 0; // Add code that checks for overflows in integer math operations. @@ -316,10 +325,25 @@ var EXPORTED_FUNCTIONS = ['_main', '_malloc']; // through LLVM dead code elimination, and also made accessible outside of // the generated code even after running closure compiler (on "Module"). // Note the necessary prefix of "_". + // Note also that this is the full list of exported functions - if you + // have a main() function and want it to run, you must include it in this + // list (as _main is by default in this value, and if you override it + // without keeping it there, you are in effect removing it). var EXPORT_ALL = 0; // If true, we export all the symbols. Note that this does *not* affect LLVM, so it can // still eliminate functions as dead. This just exports them on the Module object. var EXPORT_BINDINGS = 0; // Export all bindings generator functions (prefixed with emscripten_bind_). This // is necessary to use the bindings generator with asm.js +var RETAIN_COMPILER_SETTINGS = 0; // Remembers the values of these settings, and makes them accessible + // through Runtime.getCompilerSetting and emscripten_get_compiler_setting. + // To see what is retained, look for compilerSettings in the generated code. + + +var EMSCRIPTEN_VERSION = ''; // this will contain the emscripten version. you should not modify it. This + // and the following few settings are useful in combination with + // RETAIN_COMPILER_SETTINGS +var OPT_LEVEL = 0; // this will contain the optimization level (-Ox). you should not modify it. +var DEBUG_LEVEL = 0; // this will contain the debug level (-gx). you should not modify it. + // JS library functions (C functions implemented in JS) // that we include by default. If you want to make sure @@ -440,7 +464,7 @@ var HEADLESS = 0; // If 1, will include shim code that tries to 'fake' a browser var BENCHMARK = 0; // If 1, will just time how long main() takes to execute, and not // print out anything at all whatsoever. This is useful for benchmarking. -var ASM_JS = 0; // If 1, generate code in asm.js format. If 2, emits the same code except +var ASM_JS = 1; // If 1, generate code in asm.js format. If 2, emits the same code except // for omitting 'use asm' var PGO = 0; // Enables profile-guided optimization in the form of runtime checks for diff --git a/src/shell.html b/src/shell.html index efb9e91d..cbf8c0d8 100644 --- a/src/shell.html +++ b/src/shell.html @@ -5,37 +5,1218 @@ <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> <title>Emscripten-Generated Code</title> <style> + body { + font-family: arial; + margin: 0; + padding: none; + } + .emscripten { padding-right: 0; margin-left: auto; margin-right: auto; display: block; } - textarea.emscripten { font-family: monospace; width: 80%; } - div.emscripten { text-align: center; } + div.emscripten { text-align: center; } div.emscripten_border { border: 1px solid black; } /* the canvas *must not* have any border or padding, or mouse coords will be wrong */ canvas.emscripten { border: 0px none; } + + #emscripten_logo { + display: inline-block; + margin: 0; + } + + .spinner { + height: 30px; + width: 30px; + margin: 0; + margin-top: 20px; + margin-left: 20px; + display: inline-block; + vertical-align: top; + + -webkit-animation: rotation .8s linear infinite; + -moz-animation: rotation .8s linear infinite; + -o-animation: rotation .8s linear infinite; + animation: rotation 0.8s linear infinite; + + border-left: 5px solid rgb(235, 235, 235); + border-right: 5px solid rgb(235, 235, 235); + border-bottom: 5px solid rgb(235, 235, 235); + border-top: 5px solid rgb(120, 120, 120); + + border-radius: 100%; + background-color: rgb(189, 215, 46); + } + + @-webkit-keyframes rotation { + from {-webkit-transform: rotate(0deg);} + to {-webkit-transform: rotate(360deg);} + } + @-moz-keyframes rotation { + from {-moz-transform: rotate(0deg);} + to {-moz-transform: rotate(360deg);} + } + @-o-keyframes rotation { + from {-o-transform: rotate(0deg);} + to {-o-transform: rotate(360deg);} + } + @keyframes rotation { + from {transform: rotate(0deg);} + to {transform: rotate(360deg);} + } + + #status { + display: inline-block; + vertical-align: top; + margin-top: 30px; + margin-left: 20px; + font-weight: bold; + color: rgb(120, 120, 120); + } + + #progress { + height: 20px; + width: 30px; + } + + #controls { + display: inline-block; + float: right; + vertical-align: top; + margin-top: 30px; + margin-right: 20px; + } + + #output { + width: 100%; + height: 200px; + margin: 0 auto; + margin-top: 10px; + display: block; + background-color: black; + color: white; + font-family: 'Lucida Console', Monaco, monospace; + outline: none; + } </style> </head> <body> - <hr/> + <a href="http://emscripten.org"> + <?xml version="1.0" encoding="UTF-8" standalone="no"?><svg + version="1.1" + id="Layer_1" + x="0px" + y="0px" + width="296px" + height="78px" + viewBox="420 120 100 170" + enable-background="new 0 0 900 400" + xml:space="preserve" + inkscape:version="0.48.4 r9939" + sodipodi:docname="emscripten_powered_by_logo.svg"><metadata + id="metadata345"><rdf:RDF><cc:Work + rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type + rdf:resource="http://purl.org/dc/dcmitype/StillImage" /></cc:Work></rdf:RDF></metadata><defs + id="defs343"><linearGradient + y2="247.6265" + x2="225.1929" + y1="152.499" + x1="225.1929" + gradientUnits="userSpaceOnUse" + id="linearGradient5104"><stop + id="stop5106" + style="stop-color:#C1D72F" + offset="0.3227531" /><stop + id="stop5108" + style="stop-color:#BCD631" + offset="0.45119295" /><stop + id="stop5110" + style="stop-color:#AFD136" + offset="0.64491969" /><stop + id="stop5112" + style="stop-color:#ABD037" + offset="1" /><a:midPointStop + style="stop-color:#C1D72F" + offset="0.0123" /><a:midPointStop + style="stop-color:#C1D72F" + offset="0.3086" /><a:midPointStop + style="stop-color:#ABD037" + offset="1" /></linearGradient><linearGradient + inkscape:collect="always" + xlink:href="#SVGID_2_" + id="linearGradient5120" + x1="397.56918" + y1="128.12726" + x2="397.56918" + y2="166.25996" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(1.103059,0,0,1.103059,-38.997823,3.1312145)" /><filter + inkscape:collect="always" + id="filter5126"><feGaussianBlur + inkscape:collect="always" + stdDeviation="0.56377237" + id="feGaussianBlur5128" /></filter><linearGradient + inkscape:collect="always" + xlink:href="#SVGID_2_" + id="linearGradient5134" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(1.103059,0,0,1.103059,-38.997823,3.1312145)" + x1="397.56918" + y1="128.12726" + x2="397.56918" + y2="166.25996" /></defs><sodipodi:namedview + pagecolor="#ffffff" + bordercolor="#666666" + borderopacity="1" + objecttolerance="10" + gridtolerance="10" + guidetolerance="10" + inkscape:pageopacity="0" + inkscape:pageshadow="2" + inkscape:window-width="1440" + inkscape:window-height="838" + id="namedview341" + showgrid="false" + inkscape:zoom="0.63555556" + inkscape:cx="224.82424" + inkscape:cy="-52.085109" + inkscape:window-x="-8" + inkscape:window-y="-8" + inkscape:window-maximized="1" + inkscape:current-layer="Layer_1" /><g + id="g5130" + transform="matrix(0.91591318,0,0,0.91591318,28.176953,14.143571)"><path + transform="matrix(1.103059,0,0,1.103059,-35.073492,-16.03923)" + id="path5122" + style="fill:#383838;fill-opacity:0.34705882;stroke:none;filter:url(#filter5126)" + d="m 494.39333,173.6323 c 0.57407,0.28703 1.87073,1.00226 2.89426,1.02855 0.55732,0.0143 1.14006,-0.1672 1.60262,-0.4784 1.20466,-0.81046 2.23561,-2.03031 2.72683,-3.39661 0.19424,-0.54027 0.0238,-1.72222 0.0238,-1.72222 l -3.82713,-14.06478 -1.98533,0 0.50231,-2.67891 6.36261,0 2.55939,12.22285 4.78392,-9.68746 -2.00924,0 0,-2.65498 7.19979,0 -11.00301,22.38875 -1.69829,1.91358 -2.29628,1.3395 -2.46371,0.26312 -2.29628,-0.21528 -2.79859,-1.36342 z m -12.0637,-14.56445 c -0.93698,1.88565 -1.70261,4.35262 -0.81842,6.26333 0.36549,0.78976 1.35098,1.19428 2.192,1.41737 0.60934,0.16133 1.29167,0.0999 1.88775,-0.10468 0.48126,-0.1655 0.8829,-0.5224 1.255,-0.8697 0.40341,-0.3768 0.77723,-0.80461 1.03505,-1.29262 0.21864,-0.41395 0.40236,-0.84786 0.49325,-1.30698 0.20667,-1.0485 0.35879,-2.1079 0.33583,-3.17631 -0.0184,-0.87403 -0.0789,-1.87107 -0.47711,-2.64959 -0.26344,-0.51379 -0.77017,-0.71849 -1.33113,-0.85633 -0.42395,-0.10479 -0.81432,-0.0626 -1.21773,0.10517 -0.65479,0.27273 -1.2544,0.5311 -1.82112,0.95764 -0.57331,0.4317 -1.21403,0.86959 -1.53337,1.5127 z m 0.65588,-4.31208 c 0,0 2.19341,-1.80738 3.45549,-2.27082 0.71718,-0.26365 3.45363,-0.65258 4.15,-0.3378 1.47292,0.66633 2.26103,1.57529 2.7222,2.60001 0.46118,1.02472 0.69944,2.59956 0.79701,3.73627 0.13278,1.55027 -0.13682,3.77629 -0.53404,5.74843 -0.30079,1.49256 -1.01883,2.74423 -1.83478,3.92156 -1.06526,1.5373 -1.82382,2.15116 -3.66756,2.46594 -0.98864,0.16889 -1.93845,0.46787 -3.25466,0.0928 -1.4384,-0.40963 -2.35273,-0.81244 -3.39599,-1.63337 -0.72524,-0.57054 -1.16043,-1.54043 -1.16043,-1.54043 l 0,2.82636 -4.8903,0 3.39872,-23.01602 -1.92242,-0.85888 0.0403,-2.38127 7.25847,0.0534 z m -23.77803,2.20447 c 0.29175,1.49273 0.0813,4.83252 -0.86111,6.69751 -0.3062,0.60617 -0.94813,1.32967 -1.55479,1.6983 -1.01515,0.61713 -2.21688,1.21322 -3.3966,1.07639 -0.47944,-0.0541 -0.97036,-0.34348 -1.24383,-0.74151 -0.47686,-0.69328 -0.43621,-1.55032 -0.45448,-2.39198 -0.024,-1.06873 0.13137,-2.23775 0.38272,-3.277 0.18705,-0.7744 0.4229,-1.58254 0.86111,-2.24844 0.39037,-0.59323 0.92628,-1.12617 1.55478,-1.45909 0.54854,-0.29014 1.19695,-0.38467 1.81791,-0.40664 0.63637,-0.0231 1.3031,0.0385 1.88966,0.28704 0.3875,0.16453 0.92361,0.3524 1.00463,0.76542 z m 1.29312,-9.69052 -0.64254,6.12262 c 0,0 -1.68393,-0.96858 -2.605,-1.25148 -0.73032,-0.22434 -1.50312,-0.36654 -2.26624,-0.33838 -0.97069,0.0345 -1.91182,0.22099 -2.81751,0.57088 -0.9185,0.35497 -1.78344,0.94565 -2.49338,1.62792 -0.88025,0.84538 -1.51404,1.90455 -2.02977,3.0106 -0.39653,0.84993 -0.69517,1.75284 -0.87975,2.67232 -0.22875,1.14241 -0.44415,2.38719 -0.43937,3.55197 0.01,1.44865 0.0623,2.89489 0.54092,4.26214 0.25525,0.72907 0.71643,1.40578 1.28572,1.9283 0.56835,0.52207 1.29566,0.87604 2.02935,1.11621 0.41072,0.13491 0.85346,0.17274 1.28579,0.16935 1.00285,-0.01 2.03715,-0.0883 2.97671,-0.43999 0.66497,-0.2489 1.21759,-0.73399 1.79298,-1.1502 0.75304,-0.54475 2.16476,-1.86006 2.16476,-1.86006 l 0,1.62374 -0.5751,0 0,1.48807 6.86709,0 0,-2.84135 -1.92841,0 3.21374,-23.57782 -7.37422,0 0,2.33412 z m -93.60062,7.55781 2.33363,15.57933 6.23084,0 4.04243,-11.34169 1.62654,11.34169 5.88425,0 7.05633,-16.38872 0,-2.0141 -6.1713,0 0,2.82349 1.88966,0 -4.04243,10.16973 -0.74151,0 -1.29167,-12.55773 -5.38194,0 -4.7361,12.50989 -1.55478,-12.94538 -6.86496,0 0,2.82349 z m -12.15,0.72146 c -0.56264,0.0892 -1.03524,0.17358 -1.53086,0.45447 -0.737,0.41808 -1.46132,0.95771 -1.91357,1.67437 -0.44123,0.70048 -0.53204,1.57581 -0.66975,2.39196 -0.1751,1.04003 -0.20064,2.10306 -0.19136,3.15741 0.01,0.81614 -0.0138,1.66577 0.35879,2.39197 0.1904,0.37315 0.52874,0.80945 0.88503,1.02855 0.56015,0.34453 1.06632,0.55494 1.72222,0.598 0.72597,0.0483 1.48801,-0.18852 2.10493,-0.57408 0.59422,-0.37072 1.03334,-0.97401 1.38735,-1.5787 0.46117,-0.78744 0.70905,-1.69257 0.90895,-2.58334 0.20377,-0.90704 0.33579,-1.84565 0.28703,-2.77468 -0.0491,-0.92714 -0.18211,-1.88434 -0.57407,-2.72684 -0.2728,-0.58681 -0.70954,-1.00753 -1.29166,-1.29165 -0.44403,-0.21628 -0.99455,-0.24402 -1.48303,-0.16744 z m -6.62442,-0.73581 c 0.65404,-0.6664 1.4072,-1.25479 2.23273,-1.69161 1.0305,-0.54505 2.16429,-0.92749 3.31518,-1.11604 1.51307,-0.24806 3.09342,-0.2847 4.60036,0 0.88055,0.16632 1.78322,0.44742 2.50307,0.98113 0.77409,0.57312 1.35279,1.40936 1.79291,2.26639 0.42901,0.83457 0.6828,1.77223 0.77798,2.70605 0.16564,1.61985 0.024,3.29135 -0.37201,4.87103 -0.33328,1.33759 -0.88436,2.64754 -1.65745,3.78889 -0.67549,0.99679 -1.52894,1.91262 -2.53721,2.5709 -0.89957,0.58746 -1.9718,0.87641 -3.01035,1.15006 -0.87153,0.22963 -1.77166,0.4095 -2.67235,0.40576 -1.21068,-0.01 -2.47998,-0.0817 -3.58589,-0.57511 -1.09854,-0.48896 -1.89728,-1.32739 -2.60455,-2.30013 -0.61123,-0.83995 -1.02561,-1.59975 -1.31932,-2.87516 -0.2125,-0.9233 -0.40006,-2.19912 -0.37215,-3.14592 0.0335,-1.16537 0.3568,-2.74121 0.83416,-3.80434 0.52547,-1.17098 1.17609,-2.3161 2.07489,-3.2319 z m 94.95184,13.82318 c -2.20516,1.01761 -4.61429,1.69636 -7.02343,1.69636 -5.32726,0 -7.22678,-3.12145 -7.22678,-7.22678 0,-7.1251 4.54685,-11.19645 10.0772,-11.19645 3.7324,0 5.56453,1.69625 5.56453,4.47856 0,4.85189 -5.12329,6.27735 -10.41633,6.82001 0.10168,1.73076 0.81446,3.32485 3.3592,3.32485 1.2218,0 2.88401,-0.37315 4.91982,-1.22099 z m -3.22292,-11.77374 c 0,-0.81423 -0.57695,-1.28891 -1.62876,-1.28891 -1.89988,0 -3.46041,1.66212 -3.96978,4.34287 1.45897,-0.20368 5.59854,-0.91613 5.59854,-3.05396 z m -30.33408,11.77374 c -2.2054,1.01761 -4.61457,1.69636 -7.02371,1.69636 -5.32653,0 -7.22671,-3.12145 -7.22671,-7.22678 0,-7.1251 4.54679,-11.19645 10.07785,-11.19645 3.73175,0 5.56382,1.69625 5.56382,4.47856 0,4.85189 -5.12273,6.27735 -10.41568,6.82001 0.10142,1.73076 0.81422,3.32485 3.35884,3.32485 1.22158,0 2.8842,-0.37315 4.91994,-1.22099 z m -3.22305,-11.77374 c 0,-0.81423 -0.57638,-1.28891 -1.62883,-1.28891 -1.89959,0 -3.46023,1.66212 -3.96971,4.34287 1.4591,-0.20368 5.59854,-0.91613 5.59854,-3.05396 z m -82.36051,20.5268 -0.0679,-0.13571 0.98406,-5.66614 2.10303,-15.16698 c 0.0687,-0.40664 -0.0332,-0.61046 -0.30522,-0.71214 l -1.66259,-0.61111 0.37379,-2.57855 6.78556,0 -0.40663,2.71427 0.10142,0.0335 c 2.0016,-1.86631 4.10566,-3.08743 6.24306,-3.08743 2.91821,0 4.95366,1.86577 4.95366,6.78561 0,4.68241 -1.83206,11.6379 -8.14271,11.6379 -2.20534,0 -3.42694,-0.84825 -4.68256,-1.73039 l -0.74621,5.08917 c -0.0341,0.37361 0.0326,0.50898 0.47457,0.54273 l 3.42697,0.33969 -0.37385,2.5447 -9.0589,0 z m 6.78613,-12.04485 c 0.84787,0.71258 1.96788,1.32305 3.22348,1.32305 2.74798,0 3.76601,-3.86811 3.76601,-6.85368 0,-2.002 -0.47476,-3.32542 -1.76432,-3.32542 -1.35696,0 -3.08763,1.4591 -4.30913,2.54506 z m 81.08934,4.85147 0.33969,-2.54464 1.56064,-0.2038 c 0.47498,-0.0683 0.5429,-0.1695 0.61084,-0.67837 l 1.42466,-10.34864 c 0.0335,-0.37315 -0.0335,-0.61046 -0.33914,-0.71214 l -1.69691,-0.61111 0.37365,-2.57855 6.71797,0 -0.44097,3.05395 0.10191,0.0679 c 1.32326,-1.89982 3.22359,-3.46042 5.39485,-3.46042 0.7463,0 2.0359,0.13582 2.61295,0.30538 l -0.84863,6.17508 -3.96972,-0.13582 -0.10157,-1.76443 c -0.0335,-0.30537 -0.10223,-0.40701 -0.37391,-0.40701 -0.64452,0 -1.69636,0.78027 -2.64651,1.76455 l -1.18674,8.61817 c -0.0687,0.54303 -0.0334,0.64474 0.47477,0.67874 l 3.22351,0.27142 -0.37384,2.51081 -10.8575,0 z" + inkscape:connector-curvature="0" + sodipodi:nodetypes="cssscccccccccccccccccssssssssccssscssssscsssccccccccsssssssssccsccsssssssssscsscccccccccccccccccccccccccccccccsssscsssssscscsssssssscsssssssssscsssscsccsscscsssscsccsscsccccccccccsssccccccccssscccccccccccccsccccsccccccc" /><path + sodipodi:nodetypes="cssscccccccccccccccccssssssssccssscssssscsssccccccccsssssssssccsccsssssssssscsscccccccccccccccccccccccccccccccsssscsssssscscsssssssscsssssssssscsssscsccsscscsssscsccsscsccccccccccsssccccccccssscccccccccccccsccccsccccccc" + inkscape:connector-curvature="0" + d="m 509.55935,174.26011 c 0.63327,0.31663 2.06355,1.10555 3.19256,1.13455 0.61476,0.0158 1.25757,-0.18443 1.76781,-0.5277 1.3288,-0.89397 2.46618,-2.23946 3.00784,-3.74661 0.21419,-0.59598 0.0258,-1.89972 0.0258,-1.89972 l -4.22153,-15.51428 -2.18993,0 0.55406,-2.95501 7.01835,0 2.82313,13.48255 5.27696,-10.68586 -2.21631,0 0,-2.92858 7.94179,0 -12.13698,24.69605 -1.87332,2.11078 -2.5329,1.4776 -2.71762,0.29022 -2.53295,-0.23748 -3.08699,-1.50392 z m -13.30698,-16.06545 c -1.0335,2.08005 -1.87803,4.80122 -0.90274,6.90883 0.4032,0.87116 1.49018,1.31738 2.4179,1.56347 0.67214,0.17793 1.42477,0.1102 2.08233,-0.11548 0.53084,-0.1826 0.97383,-0.5762 1.38432,-0.9593 0.44502,-0.4157 0.85733,-0.8875 1.14176,-1.42582 0.24113,-0.45665 0.44375,-0.93526 0.54404,-1.44168 0.22797,-1.1566 0.3958,-2.3252 0.37043,-3.50371 -0.0204,-0.96413 -0.0869,-2.06387 -0.52631,-2.92259 -0.29054,-0.56679 -0.84946,-0.79259 -1.46826,-0.94463 -0.46761,-0.11559 -0.89829,-0.0686 -1.34322,0.11597 -0.72226,0.30083 -1.38368,0.5859 -2.00879,1.05634 -0.63242,0.4762 -1.33915,0.9593 -1.69146,1.6686 z m 0.72346,-4.75648 c 0,0 2.41951,-1.99358 3.81169,-2.50482 0.79109,-0.29085 3.80953,-0.71977 4.57766,-0.3726 1.6247,0.73503 2.49408,1.73759 3.00274,2.86791 0.50868,1.13043 0.77154,2.86756 0.87911,4.12137 0.14648,1.71007 -0.15092,4.16549 -0.58904,6.34083 -0.33179,1.64636 -1.12383,3.02703 -2.02388,4.32576 -1.17506,1.6957 -2.01178,2.37286 -4.04556,2.72004 -1.09051,0.18629 -2.13814,0.51607 -3.59006,0.10268 -1.5866,-0.45183 -2.59522,-0.89615 -3.74599,-1.8017 -0.79994,-0.62933 -1.28003,-1.6992 -1.28003,-1.6992 l 0,3.11766 -5.39426,0 3.74898,-25.38802 -2.12052,-0.94738 0.0443,-2.62669 8.00657,0.0587 z m -26.22853,2.43167 c 0.32185,1.64663 0.0893,5.33062 -0.9498,7.38781 -0.33781,0.66857 -1.04588,1.46667 -1.7151,1.8733 -1.11975,0.68073 -2.44527,1.33822 -3.7466,1.18729 -0.52883,-0.0601 -1.07036,-0.37888 -1.37203,-0.81791 -0.52601,-0.76478 -0.48121,-1.71012 -0.50128,-2.63848 -0.0263,-1.17893 0.14487,-2.46835 0.42212,-3.6147 0.20635,-0.8543 0.4665,-1.74564 0.94981,-2.48024 0.43067,-0.65433 1.02178,-1.24217 1.71508,-1.60939 0.60504,-0.32004 1.32025,-0.42437 2.00521,-0.44854 0.70197,-0.0251 1.4374,0.0425 2.08446,0.31654 0.4274,0.18153 1.01882,0.3888 1.10813,0.84432 z m 1.42642,-10.68922 -0.70874,6.75362 c 0,0 -1.85753,-1.06838 -2.8735,-1.38048 -0.80562,-0.24744 -1.65802,-0.40424 -2.49984,-0.37318 -1.07069,0.0382 -2.10882,0.24369 -3.1078,0.62968 -1.01321,0.39157 -1.96724,1.04315 -2.75039,1.79572 -0.97095,0.93248 -1.67003,2.10085 -2.23897,3.3208 -0.43738,0.93753 -0.76677,1.93354 -0.9704,2.94777 -0.2523,1.26016 -0.4899,2.63324 -0.48461,3.91802 0.011,1.59795 0.0683,3.19329 0.59661,4.70144 0.28155,0.80417 0.79028,1.55058 1.41822,2.127 0.62695,0.57587 1.4292,0.96634 2.23856,1.23121 0.45301,0.14881 0.94135,0.19054 1.41828,0.18685 1.10615,-0.011 2.24705,-0.0973 3.28346,-0.48539 0.73352,-0.2745 1.34304,-0.80959 1.97773,-1.2687 0.83064,-0.60085 2.38786,-2.05176 2.38786,-2.05176 l 0,1.79104 -0.63429,0 0,1.64147 7.57478,0 0,-3.13415 -2.12721,0 3.54494,-26.00772 -8.13411,0 0,2.57462 z m -103.24702,8.33671 2.57413,17.18493 6.87304,0 4.45903,-12.51049 1.79414,12.51049 6.49065,0 7.78353,-18.07772 0,-2.2217 -6.8073,0 0,3.11449 2.08446,0 -4.45903,11.21783 -0.8179,0 -1.42488,-13.85193 -5.93654,0 -5.2242,13.79919 -1.71497,-14.27958 -7.57246,0 0,3.11449 z m -13.4021,0.79586 c -0.62064,0.0982 -1.14194,0.19148 -1.68866,0.50127 -0.813,0.46118 -1.61192,1.05641 -2.11077,1.84697 -0.48673,0.77268 -0.58683,1.73821 -0.73875,2.63846 -0.1932,1.14723 -0.22134,2.31976 -0.21116,3.48281 0.011,0.90024 -0.0148,1.83747 0.39579,2.63847 0.21,0.41165 0.58324,0.89285 0.97623,1.13455 0.61796,0.38003 1.17622,0.61214 1.89972,0.6596 0.80077,0.0533 1.64141,-0.20792 2.32189,-0.63318 0.65546,-0.40892 1.13978,-1.07441 1.53029,-1.7414 0.50878,-0.86864 0.78215,-1.86707 1.00265,-2.84964 0.22477,-1.00044 0.37039,-2.03585 0.31663,-3.06058 -0.0541,-1.02274 -0.20091,-2.07854 -0.63327,-3.00784 -0.3009,-0.64731 -0.78264,-1.11143 -1.42476,-1.42485 -0.48983,-0.23858 -1.09705,-0.26912 -1.63583,-0.18464 z m -7.30711,-0.81171 c 0.72143,-0.735 1.55219,-1.38409 2.46282,-1.86591 1.1367,-0.60125 2.38729,-1.02309 3.65678,-1.23104 1.66908,-0.27366 3.41222,-0.314 5.07446,0 0.97135,0.18342 1.96702,0.49352 2.76107,1.08223 0.85389,0.63222 1.49219,1.55466 1.97771,2.49999 0.47321,0.92057 0.7531,1.95483 0.85808,2.98495 0.18274,1.78675 0.0263,3.63055 -0.41031,5.37303 -0.36757,1.47539 -0.97545,2.92034 -1.82825,4.17929 -0.74509,1.09959 -1.68654,2.10982 -2.79871,2.8359 -0.99227,0.64796 -2.175,0.96671 -3.32055,1.26856 -0.96139,0.25333 -1.95426,0.4517 -2.94774,0.44756 -1.33549,-0.011 -2.73559,-0.0897 -3.9555,-0.63431 -1.21174,-0.53936 -2.09278,-1.46419 -2.87295,-2.53723 -0.67423,-0.92645 -1.13131,-1.76457 -1.45532,-3.17146 -0.2344,-1.0184 -0.44126,-2.42572 -0.41044,-3.47012 0.0365,-1.28547 0.39349,-3.02371 0.92005,-4.19644 0.57967,-1.29168 1.29729,-2.5548 2.2888,-3.565 z m 104.73744,15.24778 c -2.43247,1.12251 -5.0899,1.87126 -7.74734,1.87126 -5.87626,0 -7.97147,-3.44315 -7.97147,-7.97158 0,-7.8594 5.0154,-12.35035 11.11569,-12.35035 4.11711,0 6.13803,1.87105 6.13803,4.94016 0,5.35189 -5.65129,6.92425 -11.48983,7.52281 0.11219,1.90916 0.89836,3.66755 3.7054,3.66755 1.3477,0 3.18121,-0.41165 5.42682,-1.34689 z m -3.55513,-12.98704 c 0,-0.89823 -0.63635,-1.42181 -1.79655,-1.42181 -2.09568,0 -3.81712,1.83342 -4.37899,4.79047 1.60937,-0.22468 6.17554,-1.01053 6.17554,-3.36866 z m -33.46028,12.98704 c -2.4327,1.12251 -5.09006,1.87126 -7.74751,1.87126 -5.87553,0 -7.97151,-3.44315 -7.97151,-7.97158 0,-7.8594 5.01539,-12.35035 11.11645,-12.35035 4.11635,0 6.13722,1.87105 6.13722,4.94016 0,5.35189 -5.65062,6.92425 -11.48908,7.52281 0.11182,1.90916 0.89812,3.66755 3.70494,3.66755 1.34748,0 3.1815,-0.41165 5.42704,-1.34689 z m -3.55514,-12.98704 c 0,-0.89823 -0.63578,-1.42181 -1.79674,-1.42181 -2.09539,0 -3.81683,1.83342 -4.37881,4.79047 1.60951,-0.22468 6.17555,-1.01053 6.17555,-3.36866 z m -90.84852,22.6422 -0.0749,-0.14971 1.08546,-6.25004 2.31984,-16.73008 c 0.0757,-0.44854 -0.0367,-0.67336 -0.33673,-0.78554 l -1.83388,-0.67411 0.41228,-2.84425 7.48486,0 -0.44853,2.99397 0.11182,0.0371 c 2.2079,-2.05871 4.52887,-3.40563 6.88646,-3.40563 3.21901,0 5.46427,2.05807 5.46427,7.48491 0,5.16501 -2.02094,12.8373 -8.98192,12.8373 -2.43264,0 -3.78014,-0.93565 -5.16516,-1.90869 l -0.82311,5.61357 c -0.0376,0.41212 0.0356,0.56148 0.52347,0.59873 l 3.78017,0.37469 -0.41234,2.8069 -9.9925,0 z m 7.48553,-13.28615 c 0.93528,0.78598 2.17068,1.45946 3.55568,1.45946 3.03118,0 4.15411,-4.26682 4.15411,-7.56009 0,-2.2083 -0.52366,-3.66812 -1.94612,-3.66812 -1.49686,0 -3.40583,1.6095 -4.75323,2.80736 z m 89.44624,5.35147 0.37469,-2.80694 1.72154,-0.2248 c 0.52388,-0.0753 0.5988,-0.1869 0.67374,-0.74827 l 1.57152,-11.41514 c 0.0365,-0.41155 -0.0368,-0.67336 -0.3741,-0.78554 l -1.87181,-0.67411 0.41215,-2.84425 7.41037,0 -0.48647,3.36865 0.11241,0.0749 c 1.45966,-2.09562 3.55581,-3.81702 5.95085,-3.81702 0.8232,0 2.2457,0.14982 2.88225,0.33688 l -0.93613,6.81148 -4.37882,-0.14982 -0.11196,-1.94633 c -0.0371,-0.33677 -0.11284,-0.44891 -0.41252,-0.44891 -0.71092,0 -1.87116,0.86067 -2.91921,1.94635 l -1.30904,9.50637 c -0.0757,0.59903 -0.0368,0.71124 0.52367,0.74874 l 3.55571,0.29932 -0.41234,2.76961 -11.9765,0 z" + style="fill:url(#linearGradient5134);fill-opacity:1;stroke:none" + id="path5080" /></g><path + fill="#E2E2E2" + d="M256.023,135.437H196.36c-16.432,0-29.8,13.368-29.8,29.8v73.527c0,16.432,13.368,29.8,29.8,29.8h59.663 c16.433,0,29.801-13.368,29.801-29.8v-73.527C285.824,148.805,272.456,135.437,256.023,135.437z M191.561,165.236 c0-2.646,2.153-4.8,4.8-4.8h59.663c2.647,0,4.801,2.153,4.801,4.8v73.527c0,2.646-2.153,4.8-4.801,4.8H196.36 c-2.646,0-4.8-2.153-4.8-4.8V165.236z" + id="path3" /><path + d="m 531.664,250.155 h 18.498 l -2.809,18.064 h 5.59 37.586 l 2.6,-17.718 c 4.98,-1.091 9.133,-3.455 12.512,-6.693 3.084,4.075 8.566,7.37 18.252,7.37 6.338,0 12.775,-1.807 17.174,-3.687 4.254,2.399 9.463,3.687 15.459,3.687 3.088,0 6.236,-0.355 9.426,-1.023 h 67.135 l 3.354,-24.827 -5.445,-0.764 1.879,-13.356 c 0.371,-2.386 0.449,-4.66 0.449,-6.156 l -0.008,-0.375 c -0.457,-12.191 -8.139,-19.765 -20.045,-19.765 -2.404,0 -4.623,0.314 -6.676,0.852 h -34.189 l -0.035,0.244 c -2.527,-0.701 -5.41,-1.096 -8.686,-1.096 -3.801,0 -7.406,0.555 -10.76,1.598 l 0.105,-0.746 h -12.467 l 1.826,-12.951 H 615.08 l -1.846,7.658 c -1.373,5.704 -2.213,5.793 -4.453,6.03 l -4.508,0.477 c -3.049,-1.424 -6.357,-2.065 -9.602,-2.065 -2.135,0 -4.275,0.284 -6.416,0.852 h -19.291 c 0.502,-1.772 0.775,-3.674 0.775,-5.678 0,-9.601 -6.846,-16.305 -16.646,-16.305 -11.055,0 -18.775,7.721 -18.775,18.776 0,0.951 0.082,1.869 0.219,2.764 -2.135,-0.288 -4.277,-0.409 -5.553,-0.409 -2.053,0 -4.072,0.288 -6.045,0.852 h -31.342 c -2.74,-0.553 -5.641,-0.852 -8.537,-0.852 -7.138,0 -13.492,1.674 -18.808,4.723 l -3.451,-1.461 c -3.711,-1.571 -11.232,-3.262 -18.979,-3.262 -8.933,0 -16.383,2.56 -21.576,7.016 -3.265,-4.473 -8.523,-7.016 -15.228,-7.016 -4.822,0 -9.021,1.477 -12.572,3.44 -2.996,-2.204 -6.796,-3.44 -11.115,-3.44 -2.327,0 -4.48,0.315 -6.476,0.852 h -33.963 l -0.035,0.245 c -2.526,-0.702 -5.41,-1.097 -8.687,-1.097 -20.458,0 -35.307,16.031 -35.307,38.117 0,17.363 10.785,28.149 28.148,28.149 3.087,0 6.236,-0.356 9.426,-1.023 h 88.816 c 3.706,0.676 7.669,1.023 11.154,1.023 8.907,0 16.278,-2.375 21.51,-6.593 4.872,4.252 11.585,6.593 19.728,6.593 3.053,0 6.206,-0.368 9.286,-1.023 h 44.664 2.069 z" + id="path5" + inkscape:connector-curvature="0" + style="fill:#e2e2e2" /><path + fill="#F5F5F5" + d="M255.023,133.437H195.36c-16.432,0-29.8,13.368-29.8,29.8v73.527c0,16.432,13.368,29.8,29.8,29.8h59.663 c16.433,0,29.801-13.368,29.801-29.8v-73.527C284.824,146.805,271.456,133.437,255.023,133.437z M190.561,163.236 c0-2.646,2.153-4.8,4.8-4.8h59.663c2.647,0,4.801,2.153,4.801,4.8v73.527c0,2.646-2.153,4.8-4.801,4.8H195.36 c-2.646,0-4.8-2.153-4.8-4.8V163.236z" + id="path7" /><g + id="g9"><g + id="g11"><path + fill="#FBFDF8" + d="M195.361,251.626c-8.161,0-14.8-6.64-14.8-14.8v-73.527c0-8.161,6.639-14.8,14.8-14.8h59.663 c8.161,0,14.8,6.639,14.8,14.8v73.527c0,8.16-6.639,14.8-14.8,14.8H195.361z" + id="path13" /><path + fill="#F0F4E1" + d="M255.024,152.499c5.964,0,10.8,4.835,10.8,10.8v73.527c0,5.965-4.835,10.8-10.8,10.8h-59.663 c-5.964,0-10.8-4.835-10.8-10.8v-73.527c0-5.964,4.835-10.8,10.8-10.8H255.024 M255.024,144.499h-59.663 c-10.366,0-18.8,8.434-18.8,18.8v73.527c0,10.366,8.434,18.8,18.8,18.8h59.663c10.366,0,18.8-8.434,18.8-18.8v-73.527 C273.824,152.933,265.391,144.499,255.024,144.499L255.024,144.499z" + id="path15" /></g><defs + id="defs17"><filter + id="Adobe_OpacityMaskFilter" + filterUnits="userSpaceOnUse" + x="176.562" + y="144.499" + width="97.263" + height="111.127"><feColorMatrix + type="matrix" + values="-1 0 0 0 1 0 -1 0 0 1 0 0 -1 0 1 0 0 0 1 0" + color-interpolation-filters="sRGB" + result="source" + id="feColorMatrix20" /></filter></defs><mask + maskUnits="userSpaceOnUse" + x="176.562" + y="144.499" + width="97.263" + height="111.127" + id="SVGID_1_"><g + filter="url(#Adobe_OpacityMaskFilter)" + id="g23"><image + overflow="visible" + width="422" + height="480" + xlink:href="data:image/jpeg;base64,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 7fGT6fI+nyPp8j6fI+nyPp8j6fI+nyPp8j6fI+nyPp8D7x8fJtxp1m7Tp5jbw/MfWIjbXTk5SHsH j/sBQ63ZK2AdthqO8vXbUZWrJ0V/oJ7ZB7Sa+ofJMIkS6IySyJRLIkSyJEsiRLIkSyJEsiRLIkSy JVLYiRLYicEr8xfwSemN0kjy8PIdkfxQp0xWEAPYPH/YCh1uyVsAAz08ome2si37qZkumaULspIu 2aRkuyki7KSLspIuyki7KSLspIuyki7KSLtilC6qSLtilC6fNNFu5qz8k7wcI+vkAAHsHj/sBWoQ AAAAAAAAAAAAAAAAAAAAAAAAAHpAf//aAAgBAgABBQD/ACi//9oACAEDAAEFAP8AKL//2gAIAQEA AQUA6w6rz/LM+776rO++qzvvqs776rO++qzvvqs776rO++qzvvqs776rO++qzvvqs776rO++qzvv qs776rO++qzvvqs776rO++qzvvqs776rO++qzvvqs776rO++qzvvqs776rO++qzvvqs776rO++qz vvqs776rO++qzvvqs776rO++qzvvqs776rO++qzvvqs776rO++qzvvqs776rO++qzvvqs776rO++ qzvvqs776rMfLPYHlHyRFUbivuDeUv1FOSVKJkRwE4CcAOAHADgBwA4AcAOAHADgBwA4AcAOAHAD gBwA4AcAOAHADgBwA4AcAOAnARciFyRUK8ndQcgyGxaaqf4fYHlHwRFUjZe68RsqbpGoKIUQkKYY kISEYJDAmBMCYEwJgTAmBMCYEwJgTAmBMCYEwJgTAmBMCYEwJgTAmCQWELCKoSFcNByEhIyxusk5 VVQV0VUL8vYHlGltupyqDlaIMREQaijcUoilMUSKgkZDDIYZDDIYZDDIYZDDIYZDDIYZDDIYZDDI YZDDIYZDDIYZDDIYZDDIYZDDIYZDDIYZDDILGQWKVRSuKORR2KPRCZltDiSYrjFXx9geUaG26nKs vy9KEjxhmONRxuOUMFLAjIjJdF0XRdIXSF0hdF0XRdF0XRdF0XRdF0XRdF0XRdIXSF0hdIXRdCsi sisFTBXHHY49GH4xMhU10y4tTFfw9geUCJ9rlcL6SNHGGBlgbZKGilsShDZQ+kPr/wBX0fSGygtC CtoVNDjI6wPsElgzCGjlLrdTden2B5QZfGvnYbCIkdkYZGmihsSn61ItJXQOtD7JIZJTBm0X6+Hs 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 DNdYwcM11jBwzXWMHDNdYwcM11jBwzXWMHDNdYwcM11jBwzXWMHDP+z/AEd/2MzUhXbvz7H6XVO/ Y7vFJSkpSUpKUlKSlJSkpSUpKUlKSlJSkpSUpKUlKSlJSkpSUpKUlKSlJSkpSUpKUlKSlJSkp+le p3d6ndOn4OZqQrt349EOqp0QT9PVfzGDBgwYMGDBgwYMGDBgwYMGDBgwYMGDBgwYMGDBgwYMGC9X RVh9/wAjo8nRfnmakK7d+H+LqdVEefTqonYYMGDBgwYMGDBgwYMGDBgwYMGDBgwYMGDBgwYMGDBg wYMGDBeqd/uKip2+/wAszUhXbsqOutURVT9SidhgwYMlZ/XsGDBgvYVFQVOnb6fHM1IV27J0QR95 O6idhOwyaWC9hU6d/oK6v0+GZqQrt2RFVOyCdhBJrUU/cRO6N+GZqQrt06CL07qIJNiijydGjzsu ZqQrt0dd/MRBBJsUUU/y+8uZqQrt06/YQQSbFFFOv2WXM1IV26KIJNyij0uZqQrt0e8iCTcoo/Lm akK7dHvIggk2KKKPy5mpCu3R7yIJNyij8uZqQrt0e8iCTcoo/LmakK7dHvIgk3KKPy5mpCu3R7yI JNyij8uZqQrt0e8iCTaooo/LmakK7dHvIgk3KKPy5mpCu3R7yIJNyij8uZqQrt0e8iCCTaoo/Lma kK7dHvIggk2qKPy5mpCu3R7yIIJNqij8uZqQrt0e8iCCTaoo/LmakK7dHvIggk2qKPy5mpCu3R7y IIJNqij8uZqQrt0e8iCCTaoo/LmakK7dHvIggk2qKPy5mpCu3R7yIIJNqij8uZqQrt0e8iCCTaoo /LmakK7dHvIggk2qKPy5mpCu3R7yIIJNqij8uZqQrt0e8iCCTaoo/LmakK7dHvIggk2qKPy5mpCu 3R7yIIJNqij8uZqQrt0e8iCCTaoo/LmakK7dHvIggk2qKPy5mpCu3R7yIIJNqij8uZqQrt0e8iCC Taoo/LmakK7dHvIggk2qKPy5mpCu3R7yIIJNqij8uZqQrt0e8iCTcoo/LmakK7dHvIgk2qKKPy5m pCu3R7yIJNyij8uZqQrt0e8iCTcoo/LmakK7dHvIgk3KKPy5mpCu3R7yIJNyij8uZqQrt0e8iCCT aoo/LmakK7dHvIgk3KKPy5mpCu3R4QSblFHpczUhXboqfcQQSbVFFT7y5mpCu3RPzEEEmxRRRHZc zUhXbojyfRR1RBJsUUUX7JLmakK7dk/bVfAgk2KKL37qwVV+suZqQrt2RHk+giook2L3F7i9GJ8M zUhXbsqItFRFRRO40aNmVo0aL3FhuL5+OZqQrt34I69REVFGjRo0aNGjf6po0aNGjRo0XuK5DXv9 zqrV+OZqQrt349UXt9hEVeijRo0aNGjRo0aNGjRo0aNGjRo0aNGjRo0aNGjRo0aNGjRo0Xq9/YVH V6OnVflmakK7d+XVOw3qh0e7FM7PJ7KQ0aNGjRo0aNGjRo0aNGjRo0aNGjRo0aNGjRpSKaH6V6nR 3sh1eXr+BmakK7d/C7KqFJfZTX2U19lNfZTX2U19lN72U3vZTe9lN72U3vZTe9lN72U3vZTe9lN7 2U3vZTe9lN72U3vZTe9lN72U3vZTe9lN72U19lNfZTX2U19lNfZSX2d3l/Ej/wAj/wCf+7/jD6/y f5/7tBOnX+L/AMfr+5tPKm08qbTyptPKm08qbTyptPKm08qbTyptPKm08qbTyptPKm08qbTyptPK m08qbTyptPKm08qbTyptPKm08qbTyptPKm08qbTyptPKm08qbTyptPKm08qbTyptPKm08qbTyptP Km08qbTyptPKm08qbTyptPKm08qbTyptPKm08qbTyptPKm08qbTyptPKn/jan//Z" + transform="matrix(0.24 0 0 0.24 174.5615 142.499)" + id="image25"></image></g></mask><g + opacity="0.09" + mask="url(#SVGID_1_)" + a:adobe-blending-mode="multiply" + a:adobe-opacity-share="1" + id="g27"><path + fill="#1D2915" + a:adobe-blending-mode="normal" + a:adobe-opacity-share="0" + d="M195.361,251.626 c-8.161,0-14.8-6.64-14.8-14.8v-73.527c0-8.161,6.639-14.8,14.8-14.8h59.663c8.161,0,14.8,6.639,14.8,14.8v73.527 c0,8.16-6.639,14.8-14.8,14.8H195.361z" + id="path29" /><path + fill="#1D2915" + a:adobe-blending-mode="normal" + a:adobe-opacity-share="0" + d="M255.024,152.499 c5.964,0,10.8,4.835,10.8,10.8v73.527c0,5.965-4.835,10.8-10.8,10.8h-59.663c-5.964,0-10.8-4.835-10.8-10.8v-73.527 c0-5.964,4.835-10.8,10.8-10.8H255.024 M255.024,144.499h-59.663c-10.366,0-18.8,8.434-18.8,18.8v73.527 c0,10.366,8.434,18.8,18.8,18.8h59.663c10.366,0,18.8-8.434,18.8-18.8v-73.527C273.824,152.933,265.391,144.499,255.024,144.499 L255.024,144.499z" + id="path31" /></g></g><g + id="g33"><g + id="g35"><linearGradient + id="SVGID_2_" + gradientUnits="userSpaceOnUse" + x1="225.1929" + y1="152.499" + x2="225.1929" + y2="247.6265"><stop + offset="0.0123" + style="stop-color:#C1D72F" + id="stop38" /><stop + offset="0.1394" + style="stop-color:#BCD631" + id="stop40" /><stop + offset="0.5859" + style="stop-color:#AFD136" + id="stop42" /><stop + offset="1" + style="stop-color:#ABD037" + id="stop44" /><a:midPointStop + offset="0.0123" + style="stop-color:#C1D72F" /><a:midPointStop + offset="0.3086" + style="stop-color:#C1D72F" /><a:midPointStop + offset="1" + style="stop-color:#ABD037" /></linearGradient><path + d="M184.562,236.826c0,5.965,4.835,10.8,10.8,10.8h59.663c5.964,0,10.8-4.835,10.8-10.8v-73.527 c0-5.964-4.835-10.8-10.8-10.8h-59.663c-5.964,0-10.8,4.835-10.8,10.8V236.826z" + id="path46" + fill="url(#SVGID_2_)" /></g><defs + id="defs48"><filter + id="Adobe_OpacityMaskFilter_1_" + filterUnits="userSpaceOnUse" + x="184.562" + y="152.499" + width="81.263" + height="95.127"><feColorMatrix + type="matrix" + values="-1 0 0 0 1 0 -1 0 0 1 0 0 -1 0 1 0 0 0 1 0" + color-interpolation-filters="sRGB" + result="source" + id="feColorMatrix51" /></filter></defs><mask + maskUnits="userSpaceOnUse" + x="184.562" + y="152.499" + width="81.263" + height="95.127" + id="SVGID_3_"><g + filter="url(#Adobe_OpacityMaskFilter_1_)" + id="g54"><image + overflow="visible" + width="356" + height="414" + xlink:href="data:image/jpeg;base64,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 zFhO7bfhcyUttpzKJlCjZiwndtvwuZKW205lEyhRsxYTu234XMlLbacyiZQo2YsJ3bb8LmSlttOZ RMoUbMWE7tt+FzJS22nMomUKNmLCd22/C5kpbbTmUTKFGzFhO7bfhcyUttpzKJlCjZiwndtvwuZK W205lEyhRsxYTu234XMlLbacyiZQo2YsJ3bb8LmSlttOZRMoUbMWE7tt+FzJS22nMomUKNmLCd22 /C5kpbbTmUTKFGzFhO7bfhcyUttpzKJlCjZiwndtvwuZKW205lEyhRsxYTu234XMlLbacyiZQo2Y sJ3bb8LmSlttOZRMoUbMWE7tt+FzJS22nMomUKNmLCd22/C5kpbbTmUTKFGzFhO7bfhcyUttpzKJ lCjZiwndtvwuZKW205lEyhRsxYTu234XMlLbacyiZQo2YsJ3bb8LmSlttOZRMoUbMWE7tt+FzJS2 2nMomUKNmLCd22/C5kpbbTmUTKFGzFhO7bfhcyUttpzCZQo38SwndtvwuZKW20dTVfvQQ+cmUXiI xFhxUsJ3bb8LmSlttGVPp8/C/go1fkTjky8RyqsB9T6KvD8CwndtvwuZKW23BKNR3wqftX+qCcRO JwyLiLxF4i0Ka8V/cuFhO7bfhcyUttuCKi/CpBRtOs74cnBF/qJ+oiRIkSJEiRIkSJEiRIkSJEiR IkSJEiRIkSJEiRIkReIv6hadFfl31d9EFc5flViuFhO7bfhcyUttv2ERrvzNT/VT4qIrf+TqIdVD qodVDqpqdVNTqpqdVNTqpqdVNTqpqdVNTqpqdVNTqpqdVNTqpqdVNTqpqdVNTqpqdVNTqpqdVNTq pqdVNTqpqdVNTqpqdVDqodVDqC/xorl0F/O74av+qfYsJ3bb8LmSlttyywndtvzD/9k=" + transform="matrix(0.24 0 0 0.24 182.5615 150.499)" + id="image56"></image></g></mask><g + opacity="0.35" + mask="url(#SVGID_3_)" + a:adobe-opacity-share="1" + id="g58"><path + a:adobe-opacity-share="0" + d="M184.562,236.826c0,5.965,4.835,10.8,10.8,10.8h59.663 c5.964,0,10.8-4.835,10.8-10.8v-73.527c0-5.964-4.835-10.8-10.8-10.8h-59.663c-5.964,0-10.8,4.835-10.8,10.8V236.826z" + id="path60" + fill="#1D2915" /></g></g><linearGradient + id="SVGID_4_" + gradientUnits="userSpaceOnUse" + x1="226.1924" + y1="159.7139" + x2="226.1924" + y2="200"><stop + offset="0.0123" + style="stop-color:#FFFFFF" + id="stop63" /><stop + offset="0.3788" + style="stop-color:#F8FBF3" + id="stop65" /><stop + offset="1" + style="stop-color:#F2F7E8" + id="stop67" /><a:midPointStop + offset="0.0123" + style="stop-color:#FFFFFF" /><a:midPointStop + offset="0.4383" + style="stop-color:#FFFFFF" /><a:midPointStop + offset="1" + style="stop-color:#F2F7E8" /></linearGradient><polygon + fill="url(#SVGID_4_)" + points="221.189,159.714 214.142,180.951 224.048,180.951 214.142,200 238.243,173.61 227.655,173.61 236.978,159.714 " + id="polygon69" /><g + id="g71"><g + id="g73"><g + id="g75"><image + overflow="visible" + opacity="0.75" + a:adobe-blending-mode="multiply" + a:adobe-opacity-share="1" + width="392" + height="242" + xlink:href="data:image/png;base64,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 SUVORK5CYII=" + transform="matrix(0.24 0 0 0.24 179.2061 198.1514)" + id="image77"></image><g + id="g79"><radialGradient + id="SVGID_5_" + cx="225.1929" + cy="226.1387" + r="30.8299" + gradientTransform="matrix(1 0 0 0.75 0 56.5347)" + gradientUnits="userSpaceOnUse"><stop + offset="0.0123" + style="stop-color:#FFFFFF" + id="stop82" /><stop + offset="0.4828" + style="stop-color:#FDFEFB" + id="stop84" /><stop + offset="0.7611" + style="stop-color:#F8FBF3" + id="stop86" /><stop + offset="0.989" + style="stop-color:#F2F8E8" + id="stop88" /><stop + offset="1" + style="stop-color:#F2F7E8" + id="stop90" /><a:midPointStop + offset="0.0123" + style="stop-color:#FFFFFF" /><a:midPointStop + offset="0.8025" + style="stop-color:#FFFFFF" /><a:midPointStop + offset="1" + style="stop-color:#F2F7E8" /></radialGradient><path + fill="url(#SVGID_5_)" + d="M186.706,235.825c0,5.965,4.835,10.801,10.799,10.801h55.374c5.965,0,10.801-4.836,10.801-10.801 v-19.373c0-5.965-4.836-10.801-10.801-10.801h-55.374c-5.964,0-10.799,4.836-10.799,10.801V235.825z" + id="path92" /><path + fill="none" + stroke="#EDF5E5" + stroke-width="5" + stroke-miterlimit="10" + d="M186.706,235.825 c0,5.965,4.835,10.801,10.799,10.801h55.374c5.965,0,10.801-4.836,10.801-10.801v-19.373c0-5.965-4.836-10.801-10.801-10.801 h-55.374c-5.964,0-10.799,4.836-10.799,10.801V235.825z" + id="path94" /></g></g><path + opacity="0.74" + fill="#FFFFFF" + a:adobe-blending-mode="lighten" + d="M263.623,229.595c0.037-0.364,0.057-0.734,0.057-1.107 v-13.375c0-5.965-4.836-10.799-10.801-10.799h-55.374c-5.964,0-10.799,4.834-10.799,10.799v7.324 c7.545-1.012,15.699-1.566,24.213-1.566C231.959,220.87,250.812,224.252,263.623,229.595z" + id="path96" /><linearGradient + id="SVGID_6_" + gradientUnits="userSpaceOnUse" + x1="225.1929" + y1="204.3135" + x2="225.1929" + y2="246.626"><stop + offset="0.0123" + style="stop-color:#FFFFFF;stop-opacity:0" + id="stop99" /><stop + offset="0.0141" + style="stop-color:#FDFDFC;stop-opacity:2.231669e-04" + id="stop101" /><stop + offset="0.1344" + style="stop-color:#BEBEAF;stop-opacity:0.0148" + id="stop103" /><stop + offset="0.2565" + style="stop-color:#94957C;stop-opacity:0.0297" + id="stop105" /><stop + offset="0.3796" + style="stop-color:#747759;stop-opacity:0.0446" + id="stop107" /><stop + offset="0.5029" + style="stop-color:#5D633F;stop-opacity:0.0596" + id="stop109" /><stop + offset="0.6263" + style="stop-color:#4D552E;stop-opacity:0.0746" + id="stop111" /><stop + offset="0.75" + style="stop-color:#414B23;stop-opacity:0.0896" + id="stop113" /><stop + offset="0.8742" + style="stop-color:#3B461E;stop-opacity:0.1047" + id="stop115" /><stop + offset="1" + style="stop-color:#38441C;stop-opacity:0.12" + id="stop117" /><a:midPointStop + offset="0.0123" + style="stop-color:#FFFFFF;stop-opacity:0" /><a:midPointStop + offset="0.2901" + style="stop-color:#FFFFFF;stop-opacity:0" /><a:midPointStop + offset="1" + style="stop-color:#38441C;stop-opacity:0.12" /></linearGradient><path + fill="url(#SVGID_6_)" + a:adobe-blending-mode="darken" + d="M263.68,221.954v13.871c0,5.965-4.836,10.801-10.801,10.801 h-55.374c-5.964,0-10.799-4.836-10.799-10.801v-13.871l0.038-7.704c0,0,0.923-9.937,11.173-9.937h54.962 c0,0,10.063,0.328,10.801,10.799V221.954z" + id="path119" /></g><g + id="g121"><g + id="g123"><image + overflow="visible" + opacity="0.25" + a:adobe-blending-mode="multiply" + a:adobe-opacity-share="1" + width="30" + height="30" + xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACEAAAAhCAYAAABX5MJvAAAACXBIWXMAAC4jAAAuIwF4pT92AAAA GXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAuJJREFUeNrsl9trE0EUxjO7m5vW tKFN1RqLCmqlIvjgkz5I/cOFIqLggw9KsRHxUo1IdEtactG9+A1+A8dxNrsxK/rgwI9lt5ueb875 ZuZspfJ//Bhqjvc0AfCIHClIQEzSMkUoBqyCJbAKWrxXQoBmBL6AQzChmGQREWbmNQY/DS6Aa6AL mtZvdcDPoEcOQEgxUV5mVMYzH5wCZ8FFcJ0CLoN1UHeIGII34AV4BvbBW4qbzsqKctzruq+ALXAL 3ABXwAafNyjS9sQ3cAwG4BXYA0/AU/AejLOE+I4MtME22AH3wE2wyedNivSFQT3eB/y79kwHnGE2 v4IjinCaNrBEtJiBu2SLs686VkRWGRt8/wTL5jFwxIxMbSGB+Ac1qtcluEMBbWslFDV7QBFdlmBE bwxZtthVDn1dpgF3WIIOhakF9iCf2ajQK32W5hcRJgvnmYHb9ECzQAnyhif8o7PxkWImsiQeRSyJ fWCjJAGy5G2usKtgzc6wx5dWxT6wYhm2jKNBm/UcV90m/aLsdLVoonX+QJV8RvmcXNflNVOOKktQ Fz4p+6AMrBg/GUeeFWUHd51HyuXevz7+GRELNSRzjMwYnmhI5Laa/gEBYxEjskVE7Ih67AeOi3ZE BYc55j+xxzjgpBMpImZL1mNDMuDxm5aYBT2x1+wx+vZJ6lt94kl2Ux1uWl4JWZhy9g/AQ/DOPjt8 q0ULuLebhiRYYO8wPUTIdm+X1zDrKE/FKjH95TL3eP83MiIF7FHAY2ZkYpfadxhoRE80WJ66EKIK BE9YAiPgPkW8dPUSFUfDGnMpHVmKvQJCEoofcsamBLs0fOgSUMnomo2QQ66UAbMTi4+hmOk2mGZW B39OE+rgj5iBcNb3h5qxk9boDb1SLrEh2c75+NlnCfT1A4OP8nZiVeAT0IhZY0Ni+gHP8oEpQ59Z HHP2uRtfkeUnxTj7AWHqMU0ZiRVX2ld5kZ4jnSewHN8FGACSOOKkAlOGAAAAAABJRU5ErkJggg==" + transform="matrix(0.24 0 0 0.24 199.0298 216.5547)" + id="image125"></image><g + id="g127"><radialGradient + id="SVGID_7_" + cx="202.6289" + cy="219.7041" + r="2.9995" + gradientTransform="matrix(1 0 0 0.75 0 54.926)" + gradientUnits="userSpaceOnUse"><stop + offset="0.0123" + style="stop-color:#FFFFFF" + id="stop130" /><stop + offset="0.4235" + style="stop-color:#FAFCF6" + id="stop132" /><stop + offset="1" + style="stop-color:#F2F7E8" + id="stop134" /><a:midPointStop + offset="0.0123" + style="stop-color:#FFFFFF" /><a:midPointStop + offset="0.6235" + style="stop-color:#FFFFFF" /><a:midPointStop + offset="1" + style="stop-color:#F2F7E8" /></radialGradient><circle + fill="url(#SVGID_7_)" + cx="202.629" + cy="219.704" + r="2.999" + id="circle136" /></g></g><g + id="g138"><image + overflow="visible" + opacity="0.25" + a:adobe-blending-mode="multiply" + a:adobe-opacity-share="1" + width="30" + height="30" + xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACYAAAAhCAYAAAC1ONkWAAAACXBIWXMAAC4jAAAuIwF4pT92AAAA GXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAtFJREFUeNrsmP1LFEEYx292Ts3V 9ujFrCiwFyPShH4I+imoiPqbhYKIoKigN0W8SulNIrOU63S921u/A9+BYdm9mbndg4IGPiynuzOf eea52WeuVvvf/Joo8VwA6rxm+0lBD3R5TYctpu6XYBRE4DiYzMhpqRbYBDtgHyQ+gsIzQmMUOg3O gzkwzciZTUXqO1gCH8E3CsauERSOUiOgAc6AC2ABXAYXwZECsV/gPVgBb8AH8AVsg45NTjhKTYGr 4Aa4AmYZqYjLmpdj+4ySilwTLIOn4C34YZOTjlLXwH1wC8xzKSMureQym0g+O85ITxP1uU3hPS6r t5j63zFK3QM3uYwNQ0g45KUSPMSJNLjsLS71blHUZJ9Ox5lDSuo2k32SHYsBvslKcAIc5jJvUK7r I1ZntK6Du8yryBJhW9P73hi3jg2ym7ek0hKtO0z4E5xx2RYYOany7DPYyotaUPCwyoVL3KemKpLS TX+h5jhGI88jT0x9/U9yrzoHwoL7ykQtZN8LHGvURUxy05xhntWH8I7WOTzDsaSLmOAMQl6DIYgF mTGEi5iWEyWqD9dtpHCMoPaXtn9KrHSR59CsYxSJ6SKv1e9FW6L1MmM4iXWMIu8ri7u04mjF7HuJ Y3VcxLqciaqfVlnYJRWKJexzlWNs5r2SZMGMNOp1cRYc5atEVBCtPVazD8AzHzHdQUKZUyzywoIT kY9Uh9XrC4o1WTimPmI9ouv9iAXfIHKm1GtKvSyKlq2C1Una5sMTLBRHPN4MOvIxpV6BRfCEJU/s W8Ganf4xzoaCf5dGaS36JHnMKnWNUg/BY35uD1rza7ku5bY4658cMDHkEt6nUZP4TQG1dI/Ic/CJ /SVVHHglS2J94pnluXLecuB9x3Nlk5+3jUlV9hOBMAQjCpn1lMikgFrCdQrtGEKp62CDlCtaMLQc eNu+QmV/7XGp2cyN2rsdCDAAoyXZx8WJpTUAAAAASUVORK5CYII=" + transform="matrix(0.24 0 0 0.24 213.9448 216.5547)" + id="image140"></image><g + id="g142"><radialGradient + id="SVGID_8_" + cx="217.5439" + cy="219.7041" + r="2.9995" + gradientTransform="matrix(1 0 0 0.75 0 54.926)" + gradientUnits="userSpaceOnUse"><stop + offset="0.0123" + style="stop-color:#FFFFFF" + id="stop145" /><stop + offset="0.4235" + style="stop-color:#FAFCF6" + id="stop147" /><stop + offset="1" + style="stop-color:#F2F7E8" + id="stop149" /><a:midPointStop + offset="0.0123" + style="stop-color:#FFFFFF" /><a:midPointStop + offset="0.6235" + style="stop-color:#FFFFFF" /><a:midPointStop + offset="1" + style="stop-color:#F2F7E8" /></radialGradient><circle + fill="url(#SVGID_8_)" + cx="217.544" + cy="219.704" + r="2.999" + id="circle151" /></g></g><g + id="g153"><image + overflow="visible" + opacity="0.25" + a:adobe-blending-mode="multiply" + a:adobe-opacity-share="1" + width="30" + height="30" + xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACYAAAAhCAYAAAC1ONkWAAAACXBIWXMAAC4jAAAuIwF4pT92AAAA GXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAttJREFUeNrsmO9r00Acxptc1m6d Fn9M125sU4RVpyjiSwXB/9wXulciiE4dhpUMpwzFUa02XdP4HDwHR7hcLk0mCh58WOnI9548973k uTYa/0e54c15nQ8E8TJ1UpKQ2Z8QJgUtgDa4CC6AliZOiYrBCfgGfoLTsgK9OQStgE2wDa6DDv+v hhQwBAdgH0TgS1mBLsIEBV0F18BNcJvC1sCyQdgIHFHYG/AODMAxBSZVhQXgHJ15AO5T2Aa4TMHC 0GMJBXwFhxT2Erygkz/AtGhim1NS1A3wCDwGO+AKBS3QKS+nrlp6eQProKe5G4LvNucCS0+1uXQP wRNwj6JaFkH6SgitNxeJ0BwNueSzMsIC3ulduiX/roJmpp9cWkVQ1CrrqB17ws+TPGdM3y3Rftnk fTpVVlS2ZpN1+qy7znl8V2HSrUvcdbKnutryVRk+63RZd5vzBC7ClFvyMXALbHEDiJreNGpDbbH+ Wp5rJmEd7sQ+n13NCq8uU881WbfPeTquwtq0u1ezW1nXepyn7SJM9dgSCc4oPBTO4Rus9jKJwatZ lGeZy+rYXzH+GWEpXxEq5Kl8VecwBcnURVjMgCcZn5GwsTZH7CJMD3khL5zWLGzKuiHnGZpe5CZh I6bO9wx7v+bN7YYxY70j1o/yEoZpKSdMmntMoMq1tIYlVG7ts/4x50tddmXCEDfgxRETZ1JRWMI6 EesObGFRWCyfaa+oDl8jQd4DscApJUr21S54Dj7wu1JBUW2Ct1rybDDRntditUtPnWor8Aw8Zd2h rXdFQdGYd6WfbPTYnOeeEiSv/cTDyC5FvbL1luspSSXPFUYUmaHu8KS0yfjdMpySYp6QIop6TZdC njEnRTvdpVc8Lt0yBW4wS+04HHj3+Fg4pKARnUxdJnVNBL7hSNal4OxPBFLAZ/CRzumn8NR1wrKR xdfy1KLlwDvmw3RaRlDVX3s8h8dGWiUE/BZgAMf82R9IYLF+AAAAAElFTkSuQmCC" + transform="matrix(0.24 0 0 0.24 228.8599 216.5547)" + id="image155"></image><g + id="g157"><radialGradient + id="SVGID_9_" + cx="232.459" + cy="219.7041" + r="2.9995" + gradientTransform="matrix(1 0 0 0.75 0 54.926)" + gradientUnits="userSpaceOnUse"><stop + offset="0.0123" + style="stop-color:#FFFFFF" + id="stop160" /><stop + offset="0.4235" + style="stop-color:#FAFCF6" + id="stop162" /><stop + offset="1" + style="stop-color:#F2F7E8" + id="stop164" /><a:midPointStop + offset="0.0123" + style="stop-color:#FFFFFF" /><a:midPointStop + offset="0.6235" + style="stop-color:#FFFFFF" /><a:midPointStop + offset="1" + style="stop-color:#F2F7E8" /></radialGradient><circle + fill="url(#SVGID_9_)" + cx="232.459" + cy="219.704" + r="2.999" + id="circle166" /></g></g><g + id="g168"><image + overflow="visible" + opacity="0.25" + a:adobe-blending-mode="multiply" + a:adobe-opacity-share="1" + width="30" + height="30" + xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACEAAAAhCAYAAABX5MJvAAAACXBIWXMAAC4jAAAuIwF4pT92AAAA GXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAt9JREFUeNrsl91rE0EUxTOzm69a 05YmVWsUFdRKRfDBJ32Q+ocLRUTBBx+UYiOitlqRaEos2UT3w3P1jIzrbHZNVuiDAz9CNru5Z+69 M3O2Uvk/fg414zOCDzSxRwJiEJGkTBGKAatgEayCFr8rS4AwAp/BIRhTTDyPCDPzGoOfAhfANdAF zdT/SMBPoEf2wYBiQldmVAEBHjgJzoCL4DoFXAZroO4QMQRvwAvwDOyCtxQ3SWdF5QiQui+DDXAL 3ABXwDqvNygy3RPfwBHog1dgBzwBT8E7ENhCvJwMrIBNsAXugZvgPK83KdKzGlTzu8/fpWc64DSz +RV8oYhfTetPEdFiBu6SDc6+6lgRWZNo8P4Flk0zcMiMSGkSP+MPalQvJbhDASuplVB0RfkU0WUJ RuyNIcsWucoh15bYgFssQYfC1Bz7kcdsVNgrByzNHyJMFs4xA7fZA80CJcgb2uofycYHihlrh4hF ax9YL0mAGT7LKivsKmjLpLXjplVrH1ie0ryzlkWa9SxXnWR5QTv6ocUmWuMDqlLu8Di5ruk1Vzmq LEGdD5QtQln7yI8YespZUXbwzBi6cgzGsRTx14ZkxvFbDJeI9Laa/AMBgRUjTIsI6Yh69ANH0xzR DMMc8x/pMcTwjNIiIlqyHg1Jn8dvUmIWZGKv6THk/Jh4GWqFE3RTHW5auoQsTDj7B+Ah2JOzI8vU RNxQ2pYh8efYO4yHGNDubfNzkHWUJ9YqMf5yiZ7AmyEjtoAdCnjMjIj5TbycBhqxJxosT90SogoE j1kCI+A+Rbw0XmKaxzQlCXjz2GpOXUBITPFDztiUYJsNPzAC8kQklpBDrpQ+sxNZL0MR020wZlaC P2cTSvBHzMAg/f6hCu6qNfaGrJRLNCSbOS8/uyyBfL5n8JFrJy7a7Solpk1DYrynTvWBKcMBsxhw 9nEZL8S2GNtzuJo6YFOG1oor7a28iOdI8gLb47sAAwCDFN6m03jgxgAAAABJRU5ErkJggg==" + transform="matrix(0.24 0 0 0.24 243.7749 216.5547)" + id="image170"></image><g + id="g172"><radialGradient + id="SVGID_10_" + cx="247.374" + cy="219.7041" + r="2.9995" + gradientTransform="matrix(1 0 0 0.75 0 54.926)" + gradientUnits="userSpaceOnUse"><stop + offset="0.0123" + style="stop-color:#FFFFFF" + id="stop175" /><stop + offset="0.4235" + style="stop-color:#FAFCF6" + id="stop177" /><stop + offset="1" + style="stop-color:#F2F7E8" + id="stop179" /><a:midPointStop + offset="0.0123" + style="stop-color:#FFFFFF" /><a:midPointStop + offset="0.6235" + style="stop-color:#FFFFFF" /><a:midPointStop + offset="1" + style="stop-color:#F2F7E8" /></radialGradient><circle + fill="url(#SVGID_10_)" + cx="247.374" + cy="219.704" + r="2.999" + id="circle181" /></g></g><g + id="g183"><image + overflow="visible" + opacity="0.25" + a:adobe-blending-mode="multiply" + a:adobe-opacity-share="1" + width="30" + height="30" + xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACEAAAAhCAYAAABX5MJvAAAACXBIWXMAAC4jAAAuIwF4pT92AAAA GXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAn9JREFUeNrsl+lrE0EYxvdKjSZW YxEPxBsVrNdHQTxA/KMFBRGPDwoVVIpoq3jUeJUG25qk2fVZ+A28WTabxG4lHzrwgxw78z7zzMw7 73reBDR/jOcCUREhn21LRCy6osfn0kT4BN0h9oiDoiGmTN8Efoum+CHWEBRvRoSbeZXgR8QZcVEc F7syfTcI/kq8Fgviu1jlv3hcET627xXHxFlxWZwXJ8RMxgmPIKkTn8UbMSdeinfiq1hnmUYS4QTs F5fENQSc4rfUgahgT7TFivgk5sVj8VQs4kqukGiAgCviDiKOijr/BUOWL7t/9uGaVyTEighYggsI uI79NQYfdY9FPF8x/WL2xiJLk9hOoelcJehtcYslqI8hYNDGrvP9G5t1PbtRQ+NIg/W/KWaxNNpk DnK5JZ35TzbuCq70ibAu3BBXxWHW1i8hGbpc0+akNLNuBDAtTopz4kBJAuxEC8cPzIlI88BpOoQl Xw1TuDtLsqvZkxbwUA2FjZxEVJYbdROj4mWOpVuzCMoWYGPlxggm4SrfFmFFJOTzDUi2KFY8KIYT sUoSWRadLRDSM0XPMgVPn4guOT0tSN6KVtHd/w8tYWJfqDHeM+m+jBkTeIEaoEmKTUp0oXD80Kjt kU4PkVSqJWTOhIDpFf5APBFLOOPliYhxZgYR00MKmVEEuKWeQ8Q8ruRe5Xb3po7s5CqvDSjnxhVw XzzjFu3k5XTbuuziNYLvZolCk+KHBU8n8QcBL8Rd8VB8yCto8kTEDNBCTIdBg4wQvyD4L6rsdOb3 xKNhhW44IKm4wZaghSAnoIdrHWhz/m3wlOfiI86OXPJPzMvPxLwG/tcX4u3m2l8BBgBQ/dU5d1Za tAAAAABJRU5ErkJggg==" + transform="matrix(0.24 0 0 0.24 199.0298 230.2217)" + id="image185"></image><g + id="g187"><radialGradient + id="SVGID_11_" + cx="202.6289" + cy="233.3711" + r="2.999" + gradientTransform="matrix(1 0 0 0.75 0 58.3428)" + gradientUnits="userSpaceOnUse"><stop + offset="0.0123" + style="stop-color:#FFFFFF" + id="stop190" /><stop + offset="0.4235" + style="stop-color:#FAFCF6" + id="stop192" /><stop + offset="1" + style="stop-color:#F2F7E8" + id="stop194" /><a:midPointStop + offset="0.0123" + style="stop-color:#FFFFFF" /><a:midPointStop + offset="0.6235" + style="stop-color:#FFFFFF" /><a:midPointStop + offset="1" + style="stop-color:#F2F7E8" /></radialGradient><circle + fill="url(#SVGID_11_)" + cx="202.629" + cy="233.37" + r="2.999" + id="circle196" /></g></g><g + id="g198"><image + overflow="visible" + opacity="0.25" + a:adobe-blending-mode="multiply" + a:adobe-opacity-share="1" + width="30" + height="30" + xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACYAAAAhCAYAAAC1ONkWAAAACXBIWXMAAC4jAAAuIwF4pT92AAAA GXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAmlJREFUeNrsmM9r1EAUx3cz2XW1 il2wLLagIh5aeilUBC967EX/XA/1It5aUaiC2JNY/EWp0lZkG3c3id+Bz0AIaXayibKHDnzIJfPm s29eZl/Sas3paNeYZ0SHaz5OKmIx5pr+azF7fyh6oi+WxXURZGJZiUScim/iWERiUkWwXTFDlxG6 I1bFBnKd3P1jpPbEvviE4JlvBtueUl1xQ9wVa2ITsdtkzOTmxGTsALE34oP4KH6I0TS5tofUJXFT PBCPxToZ67OlZTUWkSmbsffipXglvos/ZXLGI1N2qx6Kp+KRuFcgVYRh/oJYEgNxle08EUNqsbJY SDAnZa8r1FlYoQzc02sFF5n/W/wskzMlAReopydkaoVtDWocL1bqGtv8Bblx0YSgJFt9xNbJXLfG uZctjQEx11gj9M2YDXCFiVviflmAGeQCYtlt/MxTOvbJmEFkFZqSyu9GNr7xEXNP4gbnVK/mFhZl rUdsd0B3fcQCDs3lcw7PJobJrRH4iE37g24qa6VrBK05HRdiTYjVbvI8xtQ1isSyTd4pE5secW6N xEdslGnyDmhd0oazFRF7j7VGPmIxPdQ+HNMWNzUmBfFj3+7C1YDtMG7RT3UaONMS+jErtC1e05tV EksyPdkAyToHbsqW2e51R7ygs42qNooJaU/oPF2TF8wgl2SkdsVz8Y5sJVXFUvryX6TfNXld5HwE U37cWUbqGdfDsto1njVxQjscQ8jc8+SckO25jqipHTK1i+Ro1peR7FM6pKH7StAhC7uam7CQI+J+ J7RNTb0lU7Vf3+b6hXeuPxH8948qF6Pq+CvAAGGezDColMK7AAAAAElFTkSuQmCC" + transform="matrix(0.24 0 0 0.24 213.9448 230.2217)" + id="image200"></image><g + id="g202"><radialGradient + id="SVGID_12_" + cx="217.5439" + cy="233.3711" + r="2.999" + gradientTransform="matrix(1 0 0 0.75 0 58.3428)" + gradientUnits="userSpaceOnUse"><stop + offset="0.0123" + style="stop-color:#FFFFFF" + id="stop205" /><stop + offset="0.4235" + style="stop-color:#FAFCF6" + id="stop207" /><stop + offset="1" + style="stop-color:#F2F7E8" + id="stop209" /><a:midPointStop + offset="0.0123" + style="stop-color:#FFFFFF" /><a:midPointStop + offset="0.6235" + style="stop-color:#FFFFFF" /><a:midPointStop + offset="1" + style="stop-color:#F2F7E8" /></radialGradient><circle + fill="url(#SVGID_12_)" + cx="217.544" + cy="233.37" + r="2.999" + id="circle211" /></g></g><g + id="g213"><image + overflow="visible" + opacity="0.25" + a:adobe-blending-mode="multiply" + a:adobe-opacity-share="1" + width="30" + height="30" + xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACYAAAAhCAYAAAC1ONkWAAAACXBIWXMAAC4jAAAuIwF4pT92AAAA GXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAlRJREFUeNrsmM9LG0EUx5PdjTTW otDYWmoOQm2M/YGXnrxI/3IvnnoRW7TagocotqjQtLYp5pffgc/CECc7u+uGpuDAB2Y32Tff9+bN 7Jstlaa0lXM+E4gIAsd/BqIHpj+cpLBY0Ix4JBbhwYg4I+SvOIdf4jqrwHIOQUvihXgtVsWCQ9gP 8UV8El/Ft6wCyyl+DxH0XLwUb8UbxD0V1RE7ZtCO+I6oj2JPHIlTBPZ94sqe3yIi0hCb4h1Reibm iOK4HDPRuRJnRO+D2BGHRLSXJC7yRGpeNMV7sYXAeQSFCU6FRHIGB56IGveMI/uIGxu5JGFzTN0W wpqIijIsmpDFYZ55hah4xRpxP7MIM4NWWHEbTGGDKY1ybi8VKyVM/l2wYjtM+S1xrvww9x6KulgX K0QqvOOeGafGCnbrjOPScOtmnPA1Hm7Sr+TcjF2Rq2F3nb4zNVzCZsWyWMOr2XFe5WgB9urYX+ba KywkF1bJh8d4WWSrYLdhbdBhmohVrddNtYApLOUZI0jxgp6EMO8YQWlK272wuwobFlHkeVqqMVzC OlaR15mQMO8Yo8L6VpFnypNL0S1YWBe7h4wTVxneiP0RJ+KzaHE9KEjUAHst7J9wnWoqe7z9TVly QL9bwJQOsXOB3X36vbTVhfHqN16Zh49F2xXujK2PnWPsthhnkLYeiz0ziblrVZ55CkV7Ftrk1Q52 z5NmIkowdsUBIrIqz7SltR2la0vUNhxhP3PNP7RCf4CouPIs4jDS9p2U/svj21QfeKf6E8E/+ahy 37K2GwEGAJb/2mQI89WQAAAAAElFTkSuQmCC" + transform="matrix(0.24 0 0 0.24 228.8599 230.2217)" + id="image215"></image><g + id="g217"><radialGradient + id="SVGID_13_" + cx="232.459" + cy="233.3711" + r="2.999" + gradientTransform="matrix(1 0 0 0.75 0 58.3428)" + gradientUnits="userSpaceOnUse"><stop + offset="0.0123" + style="stop-color:#FFFFFF" + id="stop220" /><stop + offset="0.4235" + style="stop-color:#FAFCF6" + id="stop222" /><stop + offset="1" + style="stop-color:#F2F7E8" + id="stop224" /><a:midPointStop + offset="0.0123" + style="stop-color:#FFFFFF" /><a:midPointStop + offset="0.6235" + style="stop-color:#FFFFFF" /><a:midPointStop + offset="1" + style="stop-color:#F2F7E8" /></radialGradient><circle + fill="url(#SVGID_13_)" + cx="232.459" + cy="233.37" + r="2.999" + id="circle226" /></g></g><g + id="g228"><image + overflow="visible" + opacity="0.25" + a:adobe-blending-mode="multiply" + a:adobe-opacity-share="1" + width="30" + height="30" + xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACEAAAAhCAYAAABX5MJvAAAACXBIWXMAAC4jAAAuIwF4pT92AAAA GXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAnpJREFUeNrsl2lrE1EUhmdLjSZ2 sYgL4lqsYN0+CuIC4o8WFERcPii0YEsQtYpL3cVgW7PMjO+F55ZhmMlMzATyoRceSDKZc957zplz zzjOBCz3P+/xRE34fE6uWESiJ0I+VybCxekeMSMOizkxlbATwx/xRXwXWwiKRhFhd17H+TFxVlwU J8W+lJ0+zlfFmngjvolNrkXDinAJ+6w4IRbFZXFenBLzqUg4ODGR+CheimXxQrwWn8U2aSolwgo4 KC6Jawg4w28mAsGAmuiI3+KDaInH4qlYJyo7QoISAq6IO4g4Lppc8wrSl66fA0TNSQvJE+GRggsI uE74GxgvW8gB/68l7ouojXVSE/s5N9dxelvcIgXNIQTkFXaT718pViMiyjIa8OiZ/N8US4Q0GLEf 2d5iauYHhWtqpu8PiMINcVUcJbduBY3R9poOT4rpJdteRi1Mi9PinDhUkQC7/Cz7aRH2iTB9YIEb /IqPiSmiu0Sza3gZf2qgcC6jEVUVjWbCR83LOR8CqFpAMu07PrxJOMp3ReSJiOnnfYjH5DdK+sgS sUkT+SW6YxASJoYe46OXFtGjp5uB5JVop8/+EVfMxj4xY7w1m/YywtRmGmqhtlNhNMIs+36O2pB2 fYSmUq+gc8ZsyBzhD8QTsWEikyciomjnETFdMMiUEWBTvYyIFlGJ/ILqNRHZy1HeyBnnhhVwXzzj FO06BSHuUcVbON9Piuy7hlvCudnEXwSsiLvioXhnB5oiEREG2ojpYtRLCXEHOP/JlG12fk88yhp0 /RJNxRrbgDaCrICQqHWhw/OfdG54Lt4T2dIj/8S8/EzMa+DYX4h3l13/BBgABM7SO70ZkkMAAAAA SUVORK5CYII=" + transform="matrix(0.24 0 0 0.24 243.7749 230.2217)" + id="image230"></image><g + id="g232"><radialGradient + id="SVGID_14_" + cx="247.374" + cy="233.3711" + r="2.999" + gradientTransform="matrix(1 0 0 0.75 0 58.3428)" + gradientUnits="userSpaceOnUse"><stop + offset="0.0123" + style="stop-color:#FFFFFF" + id="stop235" /><stop + offset="0.4235" + style="stop-color:#FAFCF6" + id="stop237" /><stop + offset="1" + style="stop-color:#F2F7E8" + id="stop239" /><a:midPointStop + offset="0.0123" + style="stop-color:#FFFFFF" /><a:midPointStop + offset="0.6235" + style="stop-color:#FFFFFF" /><a:midPointStop + offset="1" + style="stop-color:#F2F7E8" /></radialGradient><circle + fill="url(#SVGID_14_)" + cx="247.374" + cy="233.37" + r="2.999" + id="circle241" /></g></g></g></g><path + d="m 529.664,248.155 h 18.498 l -2.809,18.064 h 5.59 37.586 l 2.6,-17.718 c 4.98,-1.091 9.133,-3.455 12.512,-6.693 3.084,4.075 8.566,7.37 18.252,7.37 6.338,0 12.775,-1.807 17.174,-3.687 4.254,2.399 9.463,3.687 15.459,3.687 3.088,0 6.236,-0.355 9.426,-1.023 h 67.135 l 3.354,-24.827 -5.445,-0.764 1.879,-13.356 c 0.371,-2.386 0.449,-4.66 0.449,-6.156 l -0.008,-0.375 c -0.457,-12.191 -8.139,-19.765 -20.045,-19.765 -2.404,0 -4.623,0.314 -6.676,0.852 h -34.189 l -0.035,0.244 c -2.527,-0.701 -5.41,-1.096 -8.686,-1.096 -3.801,0 -7.406,0.555 -10.76,1.598 l 0.105,-0.746 h -12.467 l 1.826,-12.951 H 613.08 l -1.846,7.658 c -1.373,5.704 -2.213,5.793 -4.453,6.03 l -4.508,0.477 c -3.049,-1.424 -6.357,-2.065 -9.602,-2.065 -2.135,0 -4.275,0.284 -6.416,0.852 h -19.291 c 0.502,-1.772 0.775,-3.674 0.775,-5.678 0,-9.601 -6.846,-16.305 -16.646,-16.305 -11.055,0 -18.775,7.721 -18.775,18.776 0,0.951 0.082,1.869 0.219,2.764 -2.135,-0.288 -4.277,-0.409 -5.553,-0.409 -2.053,0 -4.072,0.288 -6.045,0.852 h -31.342 c -2.74,-0.553 -5.641,-0.852 -8.537,-0.852 -7.138,0 -13.492,1.674 -18.808,4.723 l -3.451,-1.461 c -3.711,-1.571 -11.232,-3.262 -18.979,-3.262 -8.933,0 -16.383,2.56 -21.576,7.016 -3.265,-4.473 -8.523,-7.016 -15.228,-7.016 -4.822,0 -9.021,1.477 -12.572,3.44 -2.996,-2.204 -6.796,-3.44 -11.115,-3.44 -2.327,0 -4.48,0.315 -6.476,0.852 h -33.963 l -0.035,0.245 c -2.526,-0.702 -5.41,-1.097 -8.687,-1.097 -20.458,0 -35.307,16.031 -35.307,38.117 0,17.363 10.785,28.149 28.148,28.149 3.087,0 6.236,-0.356 9.426,-1.023 h 88.816 c 3.706,0.676 7.669,1.023 11.154,1.023 8.907,0 16.278,-2.375 21.51,-6.593 4.872,4.252 11.585,6.593 19.728,6.593 3.053,0 6.206,-0.368 9.286,-1.023 h 44.664 2.069 z" + id="path243" + inkscape:connector-curvature="0" + style="fill:#f5f5f5" /><g + id="g245" + transform="translate(0,16)"><g + id="g247"><path + d="m 340.308,218.463 c -5.538,2.556 -11.588,4.26 -17.638,4.26 -13.377,0 -18.148,-7.839 -18.148,-18.148 0,-17.893 11.418,-28.117 25.307,-28.117 9.372,0 13.973,4.26 13.973,11.247 0,12.184 -12.865,15.763 -26.157,17.126 0.255,4.346 2.045,8.35 8.435,8.35 3.068,0 7.243,-0.937 12.355,-3.067 l 1.873,8.349 z m -8.095,-29.567 c 0,-2.045 -1.448,-3.237 -4.09,-3.237 -4.771,0 -8.69,4.175 -9.969,10.906 3.664,-0.511 14.059,-2.3 14.059,-7.669 z" + id="path249" + inkscape:connector-curvature="0" + style="fill:#383838" /><path + d="m 394.07,221.7 -0.171,-0.255 1.789,-10.055 2.642,-18.063 c 0.512,-3.749 0.341,-5.623 -1.96,-5.623 -2.642,0 -5.794,2.727 -9.372,5.879 l -2.727,19.512 c -0.171,1.363 -0.171,1.534 1.022,1.704 l 4.26,0.597 -0.852,6.305 h -18.404 l -0.171,-0.341 1.875,-10.82 2.471,-17.212 c 0.512,-3.237 0.682,-5.453 -1.789,-5.453 -3.238,0 -7.413,3.664 -9.714,5.709 l -2.642,19.512 c -0.17,1.363 -0.17,1.534 1.108,1.704 l 4.26,0.597 -0.852,6.305 h -23.347 l 0.853,-6.39 3.749,-0.512 c 1.107,-0.17 1.363,-0.426 1.533,-1.704 l 3.579,-25.987 c 0.17,-0.938 0,-1.534 -0.767,-1.789 l -4.176,-1.534 0.938,-6.476 h 16.871 l -0.938,6.987 0.256,0.085 c 4.43,-3.749 9.116,-7.924 15.592,-7.924 4.687,0 7.839,2.641 8.18,7.753 l 0.256,0.086 c 4.175,-3.664 9.202,-7.839 15.252,-7.839 6.22,0 8.775,3.152 8.946,9.202 0,1.618 -0.171,3.493 -0.426,5.538 l -3.067,21.897 c -0.171,1.363 -0.171,1.534 1.107,1.704 l 4.175,0.597 -0.852,6.305 H 394.07 z" + id="path251" + inkscape:connector-curvature="0" + style="fill:#383838" /><path + d="m 443.995,190.771 -0.17,-4.431 c 0,-0.682 -0.085,-1.108 -1.022,-1.363 -1.022,-0.256 -2.642,-0.427 -4.771,-0.427 -3.579,0 -6.391,1.108 -6.391,4.09 0,2.727 2.982,3.749 6.731,5.027 6.05,2.045 13.888,4.431 13.888,13.463 0,11.076 -9.372,15.592 -20.193,15.592 -8.009,0 -14.91,-1.959 -16.273,-2.981 l 1.618,-12.355 8.691,0.512 0.255,4.941 c 0,0.597 0.171,1.108 0.938,1.363 1.278,0.427 3.238,0.768 6.05,0.768 4.687,0 7.327,-1.79 7.327,-4.687 0,-3.408 -3.152,-4.175 -8.009,-5.624 -6.135,-1.874 -12.78,-4.26 -12.78,-13.206 0,-10.48 9.116,-14.996 19.597,-14.996 6.646,0 12.866,1.533 15.081,2.471 l -1.704,12.354 -8.863,-0.511 z" + id="path253" + inkscape:connector-curvature="0" + style="fill:#383838" /><path + d="m 489.748,218.548 c -4.175,2.386 -10.395,4.175 -16.444,4.175 -13.036,0 -18.575,-7.583 -18.575,-18.574 0,-18.83 11.588,-27.691 25.988,-27.691 6.475,0 11.843,1.874 14.229,3.578 l -1.874,13.377 -8.691,-0.426 -0.255,-5.794 c 0,-0.597 -0.086,-0.938 -0.597,-1.192 -1.022,-0.427 -2.557,-0.597 -4.175,-0.597 -5.624,0 -11.418,4.601 -11.418,17.382 0,7.839 3.493,10.395 8.436,10.395 4.346,0 8.436,-1.448 11.247,-2.556 l 2.129,7.923 z" + id="path255" + inkscape:connector-curvature="0" + style="fill:#383838" /><path + d="m 491.364,221.7 0.853,-6.39 3.919,-0.512 c 1.193,-0.17 1.363,-0.426 1.534,-1.704 l 3.578,-25.987 c 0.086,-0.938 -0.085,-1.534 -0.852,-1.789 l -4.261,-1.534 0.938,-6.476 h 16.87 l -1.107,7.669 0.256,0.17 c 3.323,-4.771 8.095,-8.69 13.548,-8.69 1.874,0 5.112,0.341 6.561,0.767 l -2.13,15.507 -9.969,-0.341 -0.256,-4.431 c -0.086,-0.767 -0.256,-1.022 -0.938,-1.022 -1.619,0 -4.26,1.96 -6.646,4.431 l -2.981,21.643 c -0.171,1.363 -0.085,1.619 1.192,1.704 l 8.095,0.682 -0.938,6.305 h -27.266 z" + id="path257" + inkscape:connector-curvature="0" + style="fill:#383838" /><path + d="m 536.094,221.7 -0.17,-0.426 2.045,-11.503 3.152,-22.749 c 0.17,-0.938 -0.086,-1.534 -0.853,-1.79 l -4.175,-1.448 0.852,-6.476 h 18.149 l -5.027,35.786 c -0.171,1.363 -0.085,1.534 1.192,1.704 l 4.09,0.597 -0.852,6.305 h -18.403 z m 5.879,-57.598 c 0,-5.453 3.238,-8.775 8.776,-8.775 4.175,0 6.646,2.215 6.646,6.305 0,5.368 -3.322,8.861 -8.861,8.861 -4.176,-0.001 -6.561,-2.387 -6.561,-6.391 z" + id="path259" + inkscape:connector-curvature="0" + style="fill:#383838" /><path + d="m 556.796,239.764 -0.17,-0.341 2.471,-14.229 5.282,-38.087 c 0.171,-1.022 -0.085,-1.534 -0.767,-1.789 l -4.175,-1.534 0.938,-6.476 h 17.041 l -1.022,6.816 0.255,0.085 c 5.027,-4.686 10.311,-7.753 15.678,-7.753 7.328,0 12.44,4.686 12.44,17.041 0,11.758 -4.601,29.225 -20.449,29.225 -5.538,0 -8.605,-2.13 -11.759,-4.345 l -1.874,12.78 c -0.085,0.938 0.085,1.278 1.192,1.363 l 8.606,0.853 -0.938,6.39 h -22.749 z m 17.041,-30.247 c 2.13,1.789 4.942,3.322 8.095,3.322 6.901,0 9.458,-9.713 9.458,-17.211 0,-5.027 -1.193,-8.351 -4.431,-8.351 -3.408,0 -7.754,3.664 -10.821,6.391 l -2.301,15.849 z" + id="path261" + inkscape:connector-curvature="0" + style="fill:#383838" /><path + d="m 635.777,219.4 c -3.749,1.789 -9.458,3.322 -14.229,3.322 -8.521,0 -12.099,-2.981 -12.099,-9.969 0,-1.107 0.085,-2.386 0.256,-3.749 l 3.066,-22.323 c 0.086,-0.512 0.086,-0.853 -0.511,-0.853 h -5.879 l 1.107,-7.839 c 7.242,-0.767 10.906,-4.431 13.122,-13.633 h 7.924 l -1.704,12.1 c -0.085,0.596 -0.085,0.852 0.597,0.852 h 11.758 l -1.193,8.521 h -12.439 l -2.812,20.364 c -0.171,1.107 -0.256,1.96 -0.256,2.727 0,2.982 1.278,4.26 4.942,4.26 2.385,0 4.771,-0.596 6.816,-1.363 l 1.534,7.583 z" + id="path263" + inkscape:connector-curvature="0" + style="fill:#383838" /><path + d="m 671.817,218.463 c -5.538,2.556 -11.588,4.26 -17.638,4.26 -13.377,0 -18.148,-7.839 -18.148,-18.148 0,-17.893 11.418,-28.117 25.307,-28.117 9.372,0 13.973,4.26 13.973,11.247 0,12.184 -12.865,15.763 -26.157,17.126 0.255,4.346 2.045,8.35 8.435,8.35 3.068,0 7.243,-0.937 12.355,-3.067 l 1.873,8.349 z m -8.094,-29.567 c 0,-2.045 -1.448,-3.237 -4.09,-3.237 -4.771,0 -8.69,4.175 -9.969,10.906 3.664,-0.511 14.059,-2.3 14.059,-7.669 z" + id="path265" + inkscape:connector-curvature="0" + style="fill:#383838" /><path + d="m 703.596,221.7 -0.17,-0.255 1.874,-10.396 2.471,-17.723 c 0.512,-3.578 0.341,-5.879 -2.215,-5.879 -3.664,0 -8.18,3.578 -11.077,6.135 l -2.641,19.512 c -0.171,1.363 -0.171,1.534 1.107,1.704 l 4.26,0.597 -0.852,6.305 h -23.347 l 0.853,-6.39 3.749,-0.512 c 1.107,-0.17 1.363,-0.426 1.533,-1.704 l 3.579,-25.987 c 0.17,-0.938 0,-1.534 -0.768,-1.789 l -4.175,-1.534 0.938,-6.476 h 16.87 l -0.937,6.987 0.255,0.085 c 4.771,-4.09 9.373,-7.924 16.02,-7.924 6.475,0 9.798,3.322 10.054,10.139 0,1.363 -0.085,3.067 -0.341,4.687 l -3.067,21.812 c -0.171,1.363 -0.171,1.534 1.022,1.704 l 4.26,0.597 L 722,221.7 h -18.404 z" + id="path267" + inkscape:connector-curvature="0" + style="fill:#383838" /></g><g + id="g269"><linearGradient + id="SVGID_15_" + gradientUnits="userSpaceOnUse" + x1="324.1611" + y1="239.7637" + x2="324.1611" + y2="155.3275"><stop + offset="0" + style="stop-color:#000000" + id="stop272" /><stop + offset="1" + style="stop-color:#000000;stop-opacity:0" + id="stop274" /><a:midPointStop + offset="0" + style="stop-color:#000000" /><a:midPointStop + offset="0.6933" + style="stop-color:#000000" /><a:midPointStop + offset="1" + style="stop-color:#000000;stop-opacity:0" /></linearGradient><path + d="m 340.308,218.463 c -5.538,2.556 -11.588,4.26 -17.638,4.26 -13.377,0 -18.148,-7.839 -18.148,-18.148 0,-17.893 11.418,-28.117 25.307,-28.117 9.372,0 13.973,4.26 13.973,11.247 0,12.184 -12.865,15.763 -26.157,17.126 0.255,4.346 2.045,8.35 8.435,8.35 3.068,0 7.243,-0.937 12.355,-3.067 l 1.873,8.349 z m -8.095,-29.567 c 0,-2.045 -1.448,-3.237 -4.09,-3.237 -4.771,0 -8.69,4.175 -9.969,10.906 3.664,-0.511 14.059,-2.3 14.059,-7.669 z" + id="path276" + style="fill:url(#SVGID_15_)" + inkscape:connector-curvature="0" /><linearGradient + id="SVGID_16_" + gradientUnits="userSpaceOnUse" + x1="377.45459" + y1="239.7637" + x2="377.45459" + y2="155.3277"><stop + offset="0" + style="stop-color:#000000" + id="stop279" /><stop + offset="1" + style="stop-color:#000000;stop-opacity:0" + id="stop281" /><a:midPointStop + offset="0" + style="stop-color:#000000" /><a:midPointStop + offset="0.6933" + style="stop-color:#000000" /><a:midPointStop + offset="1" + style="stop-color:#000000;stop-opacity:0" /></linearGradient><path + d="m 394.07,221.7 -0.171,-0.255 1.789,-10.055 2.642,-18.063 c 0.512,-3.749 0.341,-5.623 -1.96,-5.623 -2.642,0 -5.794,2.727 -9.372,5.879 l -2.727,19.512 c -0.171,1.363 -0.171,1.534 1.022,1.704 l 4.26,0.597 -0.852,6.305 h -18.404 l -0.171,-0.341 1.875,-10.82 2.471,-17.212 c 0.512,-3.237 0.682,-5.453 -1.789,-5.453 -3.238,0 -7.413,3.664 -9.714,5.709 l -2.642,19.512 c -0.17,1.363 -0.17,1.534 1.108,1.704 l 4.26,0.597 -0.852,6.305 h -23.347 l 0.853,-6.39 3.749,-0.512 c 1.107,-0.17 1.363,-0.426 1.533,-1.704 l 3.579,-25.987 c 0.17,-0.938 0,-1.534 -0.767,-1.789 l -4.176,-1.534 0.938,-6.476 h 16.871 l -0.938,6.987 0.256,0.085 c 4.43,-3.749 9.116,-7.924 15.592,-7.924 4.687,0 7.839,2.641 8.18,7.753 l 0.256,0.086 c 4.175,-3.664 9.202,-7.839 15.252,-7.839 6.22,0 8.775,3.152 8.946,9.202 0,1.618 -0.171,3.493 -0.426,5.538 l -3.067,21.897 c -0.171,1.363 -0.171,1.534 1.107,1.704 l 4.175,0.597 -0.852,6.305 H 394.07 z" + id="path283" + style="fill:url(#SVGID_16_)" + inkscape:connector-curvature="0" /><linearGradient + id="SVGID_17_" + gradientUnits="userSpaceOnUse" + x1="435.17719" + y1="239.7637" + x2="435.17719" + y2="155.3275"><stop + offset="0" + style="stop-color:#000000" + id="stop286" /><stop + offset="1" + style="stop-color:#000000;stop-opacity:0" + id="stop288" /><a:midPointStop + offset="0" + style="stop-color:#000000" /><a:midPointStop + offset="0.6933" + style="stop-color:#000000" /><a:midPointStop + offset="1" + style="stop-color:#000000;stop-opacity:0" /></linearGradient><path + d="m 443.995,190.771 -0.17,-4.431 c 0,-0.682 -0.085,-1.108 -1.022,-1.363 -1.022,-0.256 -2.642,-0.427 -4.771,-0.427 -3.579,0 -6.391,1.108 -6.391,4.09 0,2.727 2.982,3.749 6.731,5.027 6.05,2.045 13.888,4.431 13.888,13.463 0,11.076 -9.372,15.592 -20.193,15.592 -8.009,0 -14.91,-1.959 -16.273,-2.981 l 1.618,-12.355 8.691,0.512 0.255,4.941 c 0,0.597 0.171,1.108 0.938,1.363 1.278,0.427 3.238,0.768 6.05,0.768 4.687,0 7.327,-1.79 7.327,-4.687 0,-3.408 -3.152,-4.175 -8.009,-5.624 -6.135,-1.874 -12.78,-4.26 -12.78,-13.206 0,-10.48 9.116,-14.996 19.597,-14.996 6.646,0 12.866,1.533 15.081,2.471 l -1.704,12.354 -8.863,-0.511 z" + id="path290" + style="fill:url(#SVGID_17_)" + inkscape:connector-curvature="0" /><linearGradient + id="SVGID_18_" + gradientUnits="userSpaceOnUse" + x1="474.83691" + y1="239.7637" + x2="474.83691" + y2="155.3275"><stop + offset="0" + style="stop-color:#000000" + id="stop293" /><stop + offset="1" + style="stop-color:#000000;stop-opacity:0" + id="stop295" /><a:midPointStop + offset="0" + style="stop-color:#000000" /><a:midPointStop + offset="0.6933" + style="stop-color:#000000" /><a:midPointStop + offset="1" + style="stop-color:#000000;stop-opacity:0" /></linearGradient><path + d="m 489.748,218.548 c -4.175,2.386 -10.395,4.175 -16.444,4.175 -13.036,0 -18.575,-7.583 -18.575,-18.574 0,-18.83 11.588,-27.691 25.988,-27.691 6.475,0 11.843,1.874 14.229,3.578 l -1.874,13.377 -8.691,-0.426 -0.255,-5.794 c 0,-0.597 -0.086,-0.938 -0.597,-1.192 -1.022,-0.427 -2.557,-0.597 -4.175,-0.597 -5.624,0 -11.418,4.601 -11.418,17.382 0,7.839 3.493,10.395 8.436,10.395 4.346,0 8.436,-1.448 11.247,-2.556 l 2.129,7.923 z" + id="path297" + style="fill:url(#SVGID_18_)" + inkscape:connector-curvature="0" /><linearGradient + id="SVGID_19_" + gradientUnits="userSpaceOnUse" + x1="512.28223" + y1="239.7637" + x2="512.28223" + y2="155.3277"><stop + offset="0" + style="stop-color:#000000" + id="stop300" /><stop + offset="1" + style="stop-color:#000000;stop-opacity:0" + id="stop302" /><a:midPointStop + offset="0" + style="stop-color:#000000" /><a:midPointStop + offset="0.6933" + style="stop-color:#000000" /><a:midPointStop + offset="1" + style="stop-color:#000000;stop-opacity:0" /></linearGradient><path + d="m 491.364,221.7 0.853,-6.39 3.919,-0.512 c 1.193,-0.17 1.363,-0.426 1.534,-1.704 l 3.578,-25.987 c 0.086,-0.938 -0.085,-1.534 -0.852,-1.789 l -4.261,-1.534 0.938,-6.476 h 16.87 l -1.107,7.669 0.256,0.17 c 3.323,-4.771 8.095,-8.69 13.548,-8.69 1.874,0 5.112,0.341 6.561,0.767 l -2.13,15.507 -9.969,-0.341 -0.256,-4.431 c -0.086,-0.767 -0.256,-1.022 -0.938,-1.022 -1.619,0 -4.26,1.96 -6.646,4.431 l -2.981,21.643 c -0.171,1.363 -0.085,1.619 1.192,1.704 l 8.095,0.682 -0.938,6.305 h -27.266 z" + id="path304" + style="fill:url(#SVGID_19_)" + inkscape:connector-curvature="0" /><linearGradient + id="SVGID_20_" + gradientUnits="userSpaceOnUse" + x1="546.65918" + y1="239.7637" + x2="546.65918" + y2="155.32719"><stop + offset="0" + style="stop-color:#000000" + id="stop307" /><stop + offset="1" + style="stop-color:#000000;stop-opacity:0" + id="stop309" /><a:midPointStop + offset="0" + style="stop-color:#000000" /><a:midPointStop + offset="0.6933" + style="stop-color:#000000" /><a:midPointStop + offset="1" + style="stop-color:#000000;stop-opacity:0" /></linearGradient><path + d="m 536.094,221.7 -0.17,-0.426 2.045,-11.503 3.152,-22.749 c 0.17,-0.938 -0.086,-1.534 -0.853,-1.79 l -4.175,-1.448 0.852,-6.476 h 18.149 l -5.027,35.786 c -0.171,1.363 -0.085,1.534 1.192,1.704 l 4.09,0.597 -0.852,6.305 h -18.403 z m 5.879,-57.598 c 0,-5.453 3.238,-8.775 8.776,-8.775 4.175,0 6.646,2.215 6.646,6.305 0,5.368 -3.322,8.861 -8.861,8.861 -4.176,-0.001 -6.561,-2.387 -6.561,-6.391 z" + id="path311" + style="fill:url(#SVGID_20_)" + inkscape:connector-curvature="0" /><linearGradient + id="SVGID_21_" + gradientUnits="userSpaceOnUse" + x1="580.69629" + y1="239.7637" + x2="580.69629" + y2="155.32719"><stop + offset="0" + style="stop-color:#000000" + id="stop314" /><stop + offset="1" + style="stop-color:#000000;stop-opacity:0" + id="stop316" /><a:midPointStop + offset="0" + style="stop-color:#000000" /><a:midPointStop + offset="0.6933" + style="stop-color:#000000" /><a:midPointStop + offset="1" + style="stop-color:#000000;stop-opacity:0" /></linearGradient><path + d="m 556.796,239.764 -0.17,-0.341 2.471,-14.229 5.282,-38.087 c 0.171,-1.022 -0.085,-1.534 -0.767,-1.789 l -4.175,-1.534 0.938,-6.476 h 17.041 l -1.022,6.816 0.255,0.085 c 5.027,-4.686 10.311,-7.753 15.678,-7.753 7.328,0 12.44,4.686 12.44,17.041 0,11.758 -4.601,29.225 -20.449,29.225 -5.538,0 -8.605,-2.13 -11.759,-4.345 l -1.874,12.78 c -0.085,0.938 0.085,1.278 1.192,1.363 l 8.606,0.853 -0.938,6.39 h -22.749 z m 17.041,-30.247 c 2.13,1.789 4.942,3.322 8.095,3.322 6.901,0 9.458,-9.713 9.458,-17.211 0,-5.027 -1.193,-8.351 -4.431,-8.351 -3.408,0 -7.754,3.664 -10.821,6.391 l -2.301,15.849 z" + id="path318" + style="fill:url(#SVGID_21_)" + inkscape:connector-curvature="0" /><linearGradient + id="SVGID_22_" + gradientUnits="userSpaceOnUse" + x1="622.7832" + y1="239.7637" + x2="622.7832" + y2="155.3268"><stop + offset="0" + style="stop-color:#000000" + id="stop321" /><stop + offset="1" + style="stop-color:#000000;stop-opacity:0" + id="stop323" /><a:midPointStop + offset="0" + style="stop-color:#000000" /><a:midPointStop + offset="0.6933" + style="stop-color:#000000" /><a:midPointStop + offset="1" + style="stop-color:#000000;stop-opacity:0" /></linearGradient><path + d="m 635.777,219.4 c -3.749,1.789 -9.458,3.322 -14.229,3.322 -8.521,0 -12.099,-2.981 -12.099,-9.969 0,-1.107 0.085,-2.386 0.256,-3.749 l 3.066,-22.323 c 0.086,-0.512 0.086,-0.853 -0.511,-0.853 h -5.879 l 1.107,-7.839 c 7.242,-0.767 10.906,-4.431 13.122,-13.633 h 7.924 l -1.704,12.1 c -0.085,0.596 -0.085,0.852 0.597,0.852 h 11.758 l -1.193,8.521 h -12.439 l -2.812,20.364 c -0.171,1.107 -0.256,1.96 -0.256,2.727 0,2.982 1.278,4.26 4.942,4.26 2.385,0 4.771,-0.596 6.816,-1.363 l 1.534,7.583 z" + id="path325" + style="fill:url(#SVGID_22_)" + inkscape:connector-curvature="0" /><linearGradient + id="SVGID_23_" + gradientUnits="userSpaceOnUse" + x1="655.6709" + y1="239.7637" + x2="655.6709" + y2="155.3275"><stop + offset="0" + style="stop-color:#000000" + id="stop328" /><stop + offset="1" + style="stop-color:#000000;stop-opacity:0" + id="stop330" /><a:midPointStop + offset="0" + style="stop-color:#000000" /><a:midPointStop + offset="0.6933" + style="stop-color:#000000" /><a:midPointStop + offset="1" + style="stop-color:#000000;stop-opacity:0" /></linearGradient><path + d="m 671.817,218.463 c -5.538,2.556 -11.588,4.26 -17.638,4.26 -13.377,0 -18.148,-7.839 -18.148,-18.148 0,-17.893 11.418,-28.117 25.307,-28.117 9.372,0 13.973,4.26 13.973,11.247 0,12.184 -12.865,15.763 -26.157,17.126 0.255,4.346 2.045,8.35 8.435,8.35 3.068,0 7.243,-0.937 12.355,-3.067 l 1.873,8.349 z m -8.094,-29.567 c 0,-2.045 -1.448,-3.237 -4.09,-3.237 -4.771,0 -8.69,4.175 -9.969,10.906 3.664,-0.511 14.059,-2.3 14.059,-7.669 z" + id="path332" + style="fill:url(#SVGID_23_)" + inkscape:connector-curvature="0" /><linearGradient + id="SVGID_24_" + gradientUnits="userSpaceOnUse" + x1="697.92969" + y1="239.7637" + x2="697.92969" + y2="155.3277"><stop + offset="0" + style="stop-color:#000000" + id="stop335" /><stop + offset="1" + style="stop-color:#000000;stop-opacity:0" + id="stop337" /><a:midPointStop + offset="0" + style="stop-color:#000000" /><a:midPointStop + offset="0.6933" + style="stop-color:#000000" /><a:midPointStop + offset="1" + style="stop-color:#000000;stop-opacity:0" /></linearGradient><path + d="m 703.596,221.7 -0.17,-0.255 1.874,-10.396 2.471,-17.723 c 0.512,-3.578 0.341,-5.879 -2.215,-5.879 -3.664,0 -8.18,3.578 -11.077,6.135 l -2.641,19.512 c -0.171,1.363 -0.171,1.534 1.107,1.704 l 4.26,0.597 -0.852,6.305 h -23.347 l 0.853,-6.39 3.749,-0.512 c 1.107,-0.17 1.363,-0.426 1.533,-1.704 l 3.579,-25.987 c 0.17,-0.938 0,-1.534 -0.768,-1.789 l -4.175,-1.534 0.938,-6.476 h 16.87 l -0.937,6.987 0.255,0.085 c 4.771,-4.09 9.373,-7.924 16.02,-7.924 6.475,0 9.798,3.322 10.054,10.139 0,1.363 -0.085,3.067 -0.341,4.687 l -3.067,21.812 c -0.171,1.363 -0.171,1.534 1.022,1.704 l 4.26,0.597 L 722,221.7 h -18.404 z" + id="path339" + style="fill:url(#SVGID_24_)" + inkscape:connector-curvature="0" /></g></g><g + id="g4141" + transform="matrix(0.81856441,0,0,0.81856441,79.234731,-94.128741)"><g + id="g4143"></g><g + id="g4165"><linearGradient + y2="155.3275" + x2="324.1611" + y1="239.7637" + x1="324.1611" + gradientUnits="userSpaceOnUse" + id="linearGradient4167"><stop + id="stop4169" + style="stop-color:#000000" + offset="0" /><stop + id="stop4171" + style="stop-color:#000000;stop-opacity:0" + offset="1" /><a:midPointStop + style="stop-color:#000000" + offset="0" /><a:midPointStop + style="stop-color:#000000" + offset="0.6933" /><a:midPointStop + style="stop-color:#000000;stop-opacity:0" + offset="1" /></linearGradient><linearGradient + y2="155.3277" + x2="377.45459" + y1="239.7637" + x1="377.45459" + gradientUnits="userSpaceOnUse" + id="linearGradient4175"><stop + id="stop4177" + style="stop-color:#000000" + offset="0" /><stop + id="stop4179" + style="stop-color:#000000;stop-opacity:0" + offset="1" /><a:midPointStop + style="stop-color:#000000" + offset="0" /><a:midPointStop + style="stop-color:#000000" + offset="0.6933" /><a:midPointStop + style="stop-color:#000000;stop-opacity:0" + offset="1" /></linearGradient><linearGradient + y2="155.3275" + x2="435.17719" + y1="239.7637" + x1="435.17719" + gradientUnits="userSpaceOnUse" + id="linearGradient4183"><stop + id="stop4185" + style="stop-color:#000000" + offset="0" /><stop + id="stop4187" + style="stop-color:#000000;stop-opacity:0" + offset="1" /><a:midPointStop + style="stop-color:#000000" + offset="0" /><a:midPointStop + style="stop-color:#000000" + offset="0.6933" /><a:midPointStop + style="stop-color:#000000;stop-opacity:0" + offset="1" /></linearGradient><linearGradient + y2="155.3275" + x2="474.83691" + y1="239.7637" + x1="474.83691" + gradientUnits="userSpaceOnUse" + id="linearGradient4191"><stop + id="stop4193" + style="stop-color:#000000" + offset="0" /><stop + id="stop4195" + style="stop-color:#000000;stop-opacity:0" + offset="1" /><a:midPointStop + style="stop-color:#000000" + offset="0" /><a:midPointStop + style="stop-color:#000000" + offset="0.6933" /><a:midPointStop + style="stop-color:#000000;stop-opacity:0" + offset="1" /></linearGradient><linearGradient + y2="155.3277" + x2="512.28223" + y1="239.7637" + x1="512.28223" + gradientUnits="userSpaceOnUse" + id="linearGradient4199"><stop + id="stop4201" + style="stop-color:#000000" + offset="0" /><stop + id="stop4203" + style="stop-color:#000000;stop-opacity:0" + offset="1" /><a:midPointStop + style="stop-color:#000000" + offset="0" /><a:midPointStop + style="stop-color:#000000" + offset="0.6933" /><a:midPointStop + style="stop-color:#000000;stop-opacity:0" + offset="1" /></linearGradient><linearGradient + y2="155.32719" + x2="546.65918" + y1="239.7637" + x1="546.65918" + gradientUnits="userSpaceOnUse" + id="linearGradient4207"><stop + id="stop4209" + style="stop-color:#000000" + offset="0" /><stop + id="stop4211" + style="stop-color:#000000;stop-opacity:0" + offset="1" /><a:midPointStop + style="stop-color:#000000" + offset="0" /><a:midPointStop + style="stop-color:#000000" + offset="0.6933" /><a:midPointStop + style="stop-color:#000000;stop-opacity:0" + offset="1" /></linearGradient><linearGradient + y2="155.32719" + x2="580.69629" + y1="239.7637" + x1="580.69629" + gradientUnits="userSpaceOnUse" + id="linearGradient4215"><stop + id="stop4217" + style="stop-color:#000000" + offset="0" /><stop + id="stop4219" + style="stop-color:#000000;stop-opacity:0" + offset="1" /><a:midPointStop + style="stop-color:#000000" + offset="0" /><a:midPointStop + style="stop-color:#000000" + offset="0.6933" /><a:midPointStop + style="stop-color:#000000;stop-opacity:0" + offset="1" /></linearGradient><linearGradient + y2="155.3268" + x2="622.7832" + y1="239.7637" + x1="622.7832" + gradientUnits="userSpaceOnUse" + id="linearGradient4223"><stop + id="stop4225" + style="stop-color:#000000" + offset="0" /><stop + id="stop4227" + style="stop-color:#000000;stop-opacity:0" + offset="1" /><a:midPointStop + style="stop-color:#000000" + offset="0" /><a:midPointStop + style="stop-color:#000000" + offset="0.6933" /><a:midPointStop + style="stop-color:#000000;stop-opacity:0" + offset="1" /></linearGradient><linearGradient + y2="155.3275" + x2="655.6709" + y1="239.7637" + x1="655.6709" + gradientUnits="userSpaceOnUse" + id="linearGradient4231"><stop + id="stop4233" + style="stop-color:#000000" + offset="0" /><stop + id="stop4235" + style="stop-color:#000000;stop-opacity:0" + offset="1" /><a:midPointStop + style="stop-color:#000000" + offset="0" /><a:midPointStop + style="stop-color:#000000" + offset="0.6933" /><a:midPointStop + style="stop-color:#000000;stop-opacity:0" + offset="1" /></linearGradient><linearGradient + y2="155.3277" + x2="697.92969" + y1="239.7637" + x1="697.92969" + gradientUnits="userSpaceOnUse" + id="linearGradient4239"><stop + id="stop4241" + style="stop-color:#000000" + offset="0" /><stop + id="stop4243" + style="stop-color:#000000;stop-opacity:0" + offset="1" /><a:midPointStop + style="stop-color:#000000" + offset="0" /><a:midPointStop + style="stop-color:#000000" + offset="0.6933" /><a:midPointStop + style="stop-color:#000000;stop-opacity:0" + offset="1" /></linearGradient></g></g></svg> + </a> + + <div class="spinner" id='spinner'></div> <div class="emscripten" id="status">Downloading...</div> + +<span id='controls'> + <span><input type="checkbox" id="resize">Resize canvas</span> + <span><input type="checkbox" id="pointerLock" checked>Lock/hide mouse pointer </span> + <span><input type="button" value="Fullscreen" onclick="Module.requestFullScreen(document.getElementById('pointerLock').checked, + document.getElementById('resize').checked)"> + </span> +</span> + <div class="emscripten"> - <progress value="0" max="100" id="progress" hidden=1></progress> + <progress value="0" max="100" id="progress" hidden=1></progress> </div> + + <div class="emscripten_border"> <canvas class="emscripten" id="canvas" oncontextmenu="event.preventDefault()"></canvas> </div> - <hr/> - <div class="emscripten"> - <input type="checkbox" id="resize">Resize canvas - <input type="checkbox" id="pointerLock" checked>Lock/hide mouse pointer - - <input type="button" value="Fullscreen" onclick="Module.requestFullScreen(document.getElementById('pointerLock').checked, - document.getElementById('resize').checked)"> - </div> - - <hr/> - <textarea class="emscripten" id="output" rows="8"></textarea> - <hr> + <textarea id="output" rows="8"></textarea> + <script type='text/javascript'> - // connect to canvas + var statusElement = document.getElementById('status'); + var progressElement = document.getElementById('progress'); + var spinnerElement = document.getElementById('spinner'); + var Module = { preRun: [], postRun: [], @@ -68,17 +1249,17 @@ var m = text.match(/([^(]+)\((\d+(\.\d+)?)\/(\d+)\)/); var now = Date.now(); if (m && now - Date.now() < 30) return; // if this is a progress update, skip it if too soon - var statusElement = document.getElementById('status'); - var progressElement = document.getElementById('progress'); if (m) { text = m[1]; progressElement.value = parseInt(m[2])*100; progressElement.max = parseInt(m[4])*100; progressElement.hidden = false; + spinnerElement.hidden = false; } else { progressElement.value = null; progressElement.max = null; progressElement.hidden = true; + if (!text) spinnerElement.style.display = 'none'; } statusElement.innerHTML = text; }, diff --git a/src/shell.js b/src/shell.js index b41fbb51..e1c0eb54 100644 --- a/src/shell.js +++ b/src/shell.js @@ -14,7 +14,11 @@ // before the code. Then that object will be used in the code, and you // can continue to use Module afterwards as well. var Module; +#if CLOSURE_COMPILER if (!Module) Module = eval('(function() { try { return {{{ EXPORT_NAME }}} || {} } catch(e) { return {} } })()'); +#else +if (!Module) Module = (typeof {{{ EXPORT_NAME }}} !== 'undefined' ? {{{ EXPORT_NAME }}} : null) || {}; +#endif // Sometimes an existing Module object exists with properties // meant to overwrite the default module functionality. Here @@ -38,10 +42,10 @@ var ENVIRONMENT_IS_SHELL = !ENVIRONMENT_IS_WEB && !ENVIRONMENT_IS_NODE && !ENVIR if (ENVIRONMENT_IS_NODE) { // Expose functionality in the same simple way that the shells work // Note that we pollute the global namespace here, otherwise we break in node - Module['print'] = function print(x) { + if (!Module['print']) Module['print'] = function print(x) { process['stdout'].write(x + '\n'); }; - Module['printErr'] = function printErr(x) { + if (!Module['printErr']) Module['printErr'] = function printErr(x) { process['stderr'].write(x + '\n'); }; @@ -71,7 +75,7 @@ if (ENVIRONMENT_IS_NODE) { module['exports'] = Module; } else if (ENVIRONMENT_IS_SHELL) { - Module['print'] = print; + if (!Module['print']) Module['print'] = print; if (typeof printErr != 'undefined') Module['printErr'] = printErr; // not present in v8 or older sm if (typeof read != 'undefined') { @@ -107,16 +111,16 @@ else if (ENVIRONMENT_IS_WEB || ENVIRONMENT_IS_WORKER) { } if (typeof console !== 'undefined') { - Module['print'] = function print(x) { + if (!Module['print']) Module['print'] = function print(x) { console.log(x); }; - Module['printErr'] = function printErr(x) { + if (!Module['printErr']) Module['printErr'] = function printErr(x) { console.log(x); }; } else { // Probably a worker, and without console.log. We can do very little here... var TRY_USE_DUMP = false; - Module['print'] = (TRY_USE_DUMP && (typeof(dump) !== "undefined") ? (function(x) { + if (!Module['print']) Module['print'] = (TRY_USE_DUMP && (typeof(dump) !== "undefined") ? (function(x) { dump(x); }) : (function(x) { // self.postMessage(x); // enable this if you want stdout to be sent as messages @@ -124,7 +128,7 @@ else if (ENVIRONMENT_IS_WEB || ENVIRONMENT_IS_WORKER) { } if (ENVIRONMENT_IS_WEB) { - this['{{{ EXPORT_NAME }}}'] = Module; + window['{{{ EXPORT_NAME }}}'] = Module; } else { Module['load'] = importScripts; } diff --git a/src/shell_minimal.html b/src/shell_minimal.html new file mode 100644 index 00000000..7a3a8d08 --- /dev/null +++ b/src/shell_minimal.html @@ -0,0 +1,132 @@ +<!doctype html> +<html lang="en-us"> + <head> + <meta charset="utf-8"> + <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> + <title>Emscripten-Generated Code</title> + <style> + .emscripten { padding-right: 0; margin-left: auto; margin-right: auto; display: block; } + textarea.emscripten { font-family: monospace; width: 80%; } + div.emscripten { text-align: center; } + div.emscripten_border { border: 1px solid black; } + /* the canvas *must not* have any border or padding, or mouse coords will be wrong */ + canvas.emscripten { border: 0px none; } + + .spinner { + height: 50px; + width: 50px; + margin: 0px auto; + -webkit-animation: rotation .8s linear infinite; + -moz-animation: rotation .8s linear infinite; + -o-animation: rotation .8s linear infinite; + animation: rotation 0.8s linear infinite; + border-left: 10px solid rgb(0,150,240); + border-right: 10px solid rgb(0,150,240); + border-bottom: 10px solid rgb(0,150,240); + border-top: 10px solid rgb(100,0,200); + border-radius: 100%; + background-color: rgb(200,100,250); + } + @-webkit-keyframes rotation { + from {-webkit-transform: rotate(0deg);} + to {-webkit-transform: rotate(360deg);} + } + @-moz-keyframes rotation { + from {-moz-transform: rotate(0deg);} + to {-moz-transform: rotate(360deg);} + } + @-o-keyframes rotation { + from {-o-transform: rotate(0deg);} + to {-o-transform: rotate(360deg);} + } + @keyframes rotation { + from {transform: rotate(0deg);} + to {transform: rotate(360deg);} + } + + </style> + </head> + <body> + <hr/> + <figure style="overflow:visible;" id="spinner"><div class="spinner"></div><center style="margin-top:0.5em"><strong>emscripten</strong></center></figure> + <div class="emscripten" id="status">Downloading...</div> + <div class="emscripten"> + <progress value="0" max="100" id="progress" hidden=1></progress> + </div> + <div class="emscripten_border"> + <canvas class="emscripten" id="canvas" oncontextmenu="event.preventDefault()"></canvas> + </div> + <hr/> + <div class="emscripten"> + <input type="checkbox" id="resize">Resize canvas + <input type="checkbox" id="pointerLock" checked>Lock/hide mouse pointer + + <input type="button" value="Fullscreen" onclick="Module.requestFullScreen(document.getElementById('pointerLock').checked, + document.getElementById('resize').checked)"> + </div> + + <hr/> + <textarea class="emscripten" id="output" rows="8"></textarea> + <hr> + <script type='text/javascript'> + var statusElement = document.getElementById('status'); + var progressElement = document.getElementById('progress'); + var spinnerElement = document.getElementById('spinner'); + + var Module = { + preRun: [], + postRun: [], + print: (function() { + var element = document.getElementById('output'); + element.value = ''; // clear browser cache + return function(text) { + text = Array.prototype.slice.call(arguments).join(' '); + // These replacements are necessary if you render to raw HTML + //text = text.replace(/&/g, "&"); + //text = text.replace(/</g, "<"); + //text = text.replace(/>/g, ">"); + //text = text.replace('\n', '<br>', 'g'); + element.value += text + "\n"; + element.scrollTop = element.scrollHeight; // focus on bottom + }; + })(), + printErr: function(text) { + text = Array.prototype.slice.call(arguments).join(' '); + if (0) { // XXX disabled for safety typeof dump == 'function') { + dump(text + '\n'); // fast, straight to the real console + } else { + console.log(text); + } + }, + canvas: document.getElementById('canvas'), + setStatus: function(text) { + if (!Module.setStatus.last) Module.setStatus.last = { time: Date.now(), text: '' }; + if (text === Module.setStatus.text) return; + var m = text.match(/([^(]+)\((\d+(\.\d+)?)\/(\d+)\)/); + var now = Date.now(); + if (m && now - Date.now() < 30) return; // if this is a progress update, skip it if too soon + if (m) { + text = m[1]; + progressElement.value = parseInt(m[2])*100; + progressElement.max = parseInt(m[4])*100; + progressElement.hidden = false; + spinnerElement.hidden = false; + } else { + progressElement.value = null; + progressElement.max = null; + progressElement.hidden = true; + if (!text) spinnerElement.hidden = true; + } + statusElement.innerHTML = text; + }, + totalDependencies: 0, + monitorRunDependencies: function(left) { + this.totalDependencies = Math.max(this.totalDependencies, left); + Module.setStatus(left ? 'Preparing... (' + (this.totalDependencies-left) + '/' + this.totalDependencies + ')' : 'All downloads complete.'); + } + }; + Module.setStatus('Downloading...'); + </script> + {{{ SCRIPT }}} + </body> +</html> diff --git a/src/struct_info.json b/src/struct_info.json index a22851b6..2a2b4c64 100644 --- a/src/struct_info.json +++ b/src/struct_info.json @@ -141,7 +141,9 @@ }, { "file": "libc/stdlib.h", - "defines": [], + "defines": [ + "RAND_MAX" + ], "structs": { // NOTE: The hash sign at the end of this name is a hint to the processor that it mustn't prefix "struct " to the name to reference this struct. // It will be stripped away when writing the compiled JSON file. You can just refer to it as C_STRUCTS.div_t when using it in the JS code. @@ -1073,5 +1075,192 @@ "UUID_TYPE_DCE_RANDOM" ], "structs": {} + }, + // =========================================== + // emscripten html5 library + // =========================================== + { + "file": "emscripten/html5.h", + "defines": [ + "EMSCRIPTEN_EVENT_KEYPRESS", + "EMSCRIPTEN_EVENT_KEYDOWN", + "EMSCRIPTEN_EVENT_KEYUP", + "EMSCRIPTEN_EVENT_CLICK", + "EMSCRIPTEN_EVENT_MOUSEDOWN", + "EMSCRIPTEN_EVENT_MOUSEUP", + "EMSCRIPTEN_EVENT_DBLCLICK", + "EMSCRIPTEN_EVENT_MOUSEMOVE", + "EMSCRIPTEN_EVENT_WHEEL", + "EMSCRIPTEN_EVENT_RESIZE", + "EMSCRIPTEN_EVENT_SCROLL", + "EMSCRIPTEN_EVENT_BLUR", + "EMSCRIPTEN_EVENT_FOCUS", + "EMSCRIPTEN_EVENT_FOCUSIN", + "EMSCRIPTEN_EVENT_FOCUSOUT", + "EMSCRIPTEN_EVENT_DEVICEORIENTATION", + "EMSCRIPTEN_EVENT_DEVICEMOTION", + "EMSCRIPTEN_EVENT_ORIENTATIONCHANGE", + "EMSCRIPTEN_EVENT_FULLSCREENCHANGE", + "EMSCRIPTEN_EVENT_POINTERLOCKCHANGE", + "EMSCRIPTEN_EVENT_VISIBILITYCHANGE", + "EMSCRIPTEN_EVENT_TOUCHSTART", + "EMSCRIPTEN_EVENT_TOUCHEND", + "EMSCRIPTEN_EVENT_TOUCHMOVE", + "EMSCRIPTEN_EVENT_TOUCHCANCEL", + "EMSCRIPTEN_EVENT_GAMEPADCONNECTED", + "EMSCRIPTEN_EVENT_GAMEPADDISCONNECTED", + "EMSCRIPTEN_EVENT_BEFOREUNLOAD", + "EMSCRIPTEN_EVENT_BATTERYCHARGINGCHANGE", + "EMSCRIPTEN_EVENT_BATTERYLEVELCHANGE", + "EMSCRIPTEN_EVENT_WEBGLCONTEXTLOST", + "EMSCRIPTEN_EVENT_WEBGLCONTEXTRESTORED", + + "EMSCRIPTEN_RESULT_SUCCESS", + "EMSCRIPTEN_RESULT_DEFERRED", + "EMSCRIPTEN_RESULT_FAILED_NOT_DEFERRED", + "EMSCRIPTEN_RESULT_INVALID_TARGET", + "EMSCRIPTEN_RESULT_UNKNOWN_TARGET", + "EMSCRIPTEN_RESULT_INVALID_PARAM", + "EMSCRIPTEN_RESULT_NOT_SUPPORTED", + "EMSCRIPTEN_RESULT_FAILED", + "EMSCRIPTEN_RESULT_NO_DATA" + ], + "structs": { + "EmscriptenKeyboardEvent": [ + "key", + "code", + "location", + "ctrlKey", + "shiftKey", + "altKey", + "metaKey", + "repeat", + "locale", + "charValue", + "charCode", + "keyCode", + "which" + ], + "EmscriptenMouseEvent": [ + "timestamp", + "screenX", + "screenY", + "clientX", + "clientY", + "ctrlKey", + "shiftKey", + "altKey", + "metaKey", + "button", + "buttons", + "movementX", + "movementY", + "canvasX", + "canvasY" + ], + "EmscriptenWheelEvent": [ + "mouse", + "deltaX", + "deltaY", + "deltaZ", + "deltaMode" + ], + "EmscriptenUiEvent": [ + "detail", + "documentBodyClientWidth", + "documentBodyClientHeight", + "windowInnerWidth", + "windowInnerHeight", + "windowOuterWidth", + "windowOuterHeight", + "scrollTop", + "scrollLeft" + ], + "EmscriptenFocusEvent": [ + "nodeName", + "id" + ], + "EmscriptenDeviceOrientationEvent": [ + "timestamp", + "alpha", + "beta", + "gamma", + "absolute" + ], + "EmscriptenDeviceMotionEvent": [ + "timestamp", + "accelerationX", + "accelerationY", + "accelerationZ", + "accelerationIncludingGravityX", + "accelerationIncludingGravityY", + "accelerationIncludingGravityZ", + "rotationRateAlpha", + "rotationRateBeta", + "rotationRateGamma" + ], + "EmscriptenOrientationChangeEvent": [ + "orientationIndex", + "orientationAngle" + ], + "EmscriptenFullscreenChangeEvent": [ + "isFullscreen", + "fullscreenEnabled", + "nodeName", + "id", + "elementWidth", + "elementHeight", + "screenWidth", + "screenHeight" + ], + "EmscriptenPointerlockChangeEvent": [ + "isActive", + "nodeName", + "id" + ], + "EmscriptenVisibilityChangeEvent": [ + "hidden", + "visibilityState" + ], + "EmscriptenTouchPoint": [ + "identifier", + "screenX", + "screenY", + "clientX", + "clientY", + "pageX", + "pageY", + "isChanged", + "onTarget", + "canvasX", + "canvasY" + ], + "EmscriptenTouchEvent": [ + "numTouches", + "ctrlKey", + "shiftKey", + "altKey", + "metaKey", + "touches" + ], + "EmscriptenGamepadEvent": [ + "timestamp", + "axis", + "analogButton", + "digitalButton", + "connected", + "index", + "numAxes", + "numButtons", + "id", + "mapping" + ], + "EmscriptenBatteryEvent": [ + "chargingTime", + "dischargingTime", + "level", + "charging" + ] + } } -] + ] diff --git a/src/utility.js b/src/utility.js index 178c596b..54cc2d69 100644 --- a/src/utility.js +++ b/src/utility.js @@ -200,11 +200,11 @@ function dprint() { printErr(text); } -var PROF_ORIGIN = Date.now(); -var PROF_TIME = PROF_ORIGIN; +var _PROF_ORIGIN = Date.now(); +var _PROF_TIME = _PROF_ORIGIN; function PROF(pass) { if (!pass) { - dprint("Profiling: " + ((Date.now() - PROF_TIME)/1000) + ' seconds, total: ' + ((Date.now() - PROF_ORIGIN)/1000)); + dprint("Profiling: " + ((Date.now() - _PROF_TIME)/1000) + ' seconds, total: ' + ((Date.now() - _PROF_ORIGIN)/1000)); } PROF_TIME = Date.now(); } |