summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/deps_info.json7
-rw-r--r--src/embind/emval.js36
-rw-r--r--src/headlessCanvas.js1
-rw-r--r--src/jsifier.js72
-rw-r--r--src/library.js320
-rw-r--r--src/library_browser.js22
-rw-r--r--src/library_egl.js2
-rw-r--r--src/library_fs.js213
-rw-r--r--src/library_gl.js198
-rw-r--r--src/library_glfw.js8
-rw-r--r--src/library_glut.js2
-rw-r--r--src/library_html5.js19
-rw-r--r--src/library_idbfs.js324
-rw-r--r--src/library_openal.js195
-rw-r--r--src/library_sdl.js160
-rw-r--r--src/parseTools.js4
-rw-r--r--src/postamble.js4
-rw-r--r--src/preamble.js9
-rw-r--r--src/runtime.js4
-rw-r--r--src/settings.js16
-rw-r--r--src/struct_info.json4
21 files changed, 1028 insertions, 592 deletions
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/headlessCanvas.js b/src/headlessCanvas.js
index 6b0f9d47..4bd17a7b 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;
}
},
diff --git a/src/jsifier.js b/src/jsifier.js
index f4819584..35846d39 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 += ';';
@@ -387,6 +395,13 @@ 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 (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 +463,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 +493,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 +502,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);
}
}
@@ -1223,7 +1227,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;
@@ -1844,7 +1848,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 && !SIDE_MODULE) {
+ 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) {
diff --git a/src/library.js b/src/library.js
index bca47944..f69b52e5 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);
}
@@ -134,7 +135,7 @@ LibraryManager.library = {
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;
@@ -1641,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;
@@ -2289,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) {
@@ -2322,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'],
@@ -2350,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);
@@ -2375,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;
@@ -2393,7 +2396,7 @@ LibraryManager.library = {
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_;
@@ -2417,8 +2420,7 @@ 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;
+ return FS.getStreamFromPtr(stream).fd;
},
ftrylockfile: function() {
// int ftrylockfile(FILE *file);
@@ -2460,19 +2462,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);
+ 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 {
@@ -2488,11 +2491,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) {
@@ -2517,7 +2521,7 @@ 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;
@@ -2527,7 +2531,7 @@ LibraryManager.library = {
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;
@@ -2541,7 +2545,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;
@@ -2553,15 +2557,16 @@ 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;
},
@@ -2570,7 +2575,7 @@ LibraryManager.library = {
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;
@@ -2589,7 +2594,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;
@@ -2602,15 +2607,16 @@ LibraryManager.library = {
}
},
ftello: '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 {
@@ -2673,7 +2679,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) {
@@ -2730,7 +2736,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;
}
@@ -2755,7 +2761,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;
}
@@ -2898,7 +2904,7 @@ LibraryManager.library = {
// ==========================================================================
mmap__deps: ['$FS'],
- mmap: function(start, num, prot, flags, stream, offset) {
+ 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.
@@ -2909,13 +2915,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);
@@ -3229,22 +3235,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) {
@@ -3435,13 +3453,25 @@ LibraryManager.library = {
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() {
@@ -3485,11 +3515,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) {
@@ -3729,76 +3766,6 @@ LibraryManager.library = {
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++) {
@@ -4000,7 +3967,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',
@@ -4011,65 +3981,104 @@ 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: function() {
// http://refspecs.freestandards.org/LSB_3.0.0/LSB-Core-generic/LSB-Core-generic/baselib---ctype-b-loc.html
@@ -4415,7 +4424,7 @@ LibraryManager.library = {
// 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__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;
@@ -4609,6 +4618,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;
@@ -5887,7 +5898,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) {
@@ -6129,7 +6143,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);
@@ -7342,20 +7359,10 @@ 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
@@ -7510,6 +7517,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;
}
@@ -8636,6 +8644,8 @@ LibraryManager.library = {
return 0;
},
+ mkport: function() { throw 'TODO' },
+
// ==========================================================================
// select.h
// ==========================================================================
diff --git a/src/library_browser.js b/src/library_browser.js
index 458a8dd2..50a8fc6f 100644
--- a/src/library_browser.js
+++ b/src/library_browser.js
@@ -13,6 +13,7 @@ mergeInto(LibraryManager.library, {
$Browser: {
mainLoop: {
scheduler: null,
+ method: '',
shouldPause: false,
paused: false,
queue: [],
@@ -445,6 +446,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,
@@ -674,6 +679,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 +700,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;
@@ -784,6 +793,11 @@ mergeInto(LibraryManager.library, {
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']();
}
@@ -814,11 +828,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();
@@ -946,6 +962,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;
diff --git a/src/library_egl.js b/src/library_egl.js
index 11cf8951..69dd266d 100644
--- a/src/library_egl.js
+++ b/src/library_egl.js
@@ -555,7 +555,7 @@ var LibraryEGL = {
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..e97ba588 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);
@@ -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);
@@ -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();
diff --git a/src/library_gl.js b/src/library_gl.js
index 0a30292a..e3442a8a 100644
--- a/src/library_gl.js
+++ b/src/library_gl.js
@@ -210,6 +210,7 @@ var LibraryGL = {
}
},
+#if LEGACY_GL_EMULATION
// Find a token in a shader source string
findToken: function(source, token) {
function isIdentChar(ch) {
@@ -238,6 +239,7 @@ var LibraryGL = {
} while (true);
return false;
},
+#endif
getSource: function(shader, count, string, length) {
var source = '';
@@ -255,6 +257,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 */) {
@@ -270,6 +273,7 @@ var LibraryGL = {
#endif
}
}
+#endif
return source;
},
@@ -879,12 +883,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',
@@ -1544,9 +1548,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) {
@@ -1560,7 +1562,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') }}};
}
@@ -1849,7 +1854,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();
@@ -1874,7 +1879,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) {
@@ -1895,7 +1900,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)) {
@@ -1905,7 +1910,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];
@@ -1916,7 +1921,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
@@ -1985,7 +1990,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
@@ -2009,7 +2014,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,
@@ -2026,7 +2031,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);
@@ -2140,7 +2145,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)) {
@@ -2155,14 +2160,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);
@@ -2174,7 +2179,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]');
@@ -2195,7 +2200,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.
@@ -2206,12 +2211,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');
}
@@ -2219,7 +2224,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) {
@@ -2234,7 +2239,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
@@ -2257,7 +2262,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;
}
@@ -2265,21 +2270,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];
@@ -2311,6 +2316,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]) {
@@ -2321,8 +2327,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
@@ -2343,7 +2351,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]) {
@@ -2354,6 +2364,7 @@ var LibraryGL = {
Module.printErr('WARNING: getObjectParameteriv received invalid id: ' + id);
}
},
+ glGetInfoLogARB: 'glGetInfoLog',
glBindProgram__sig: 'vii',
glBindProgram: function(type, id) {
@@ -2361,6 +2372,7 @@ var LibraryGL = {
assert(id == 0);
#endif
},
+ glBindProgramARB: 'glBindProgram',
glGetPointerv: function(name, p) {
var attribute;
@@ -4108,7 +4120,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;
@@ -4124,7 +4136,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;
@@ -4164,36 +4176,36 @@ 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);
@@ -4208,7 +4220,7 @@ var LibraryGL = {
};
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.
@@ -4707,6 +4719,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);
@@ -4820,6 +4833,7 @@ var LibraryGL = {
if (GLEmulation.currentVao && GLEmulation.currentVao.id == id) GLEmulation.currentVao = null;
}
},
+ glBindVertexArray__deps: ['glBindBuffer', 'glEnableVertexAttribArray', 'glVertexAttribPointer', 'glEnableClientState'],
glBindVertexArray__sig: 'vi',
glBindVertexArray: function(vao) {
// undo vao-related things, wipe the slate clean, both for vao of 0 or an actual vao
@@ -5027,39 +5041,11 @@ var LibraryGL = {
#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.'; },
+ glGenVertexArrays: function(){ throw 'Legacy GL function (glGenVertexArrays) 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
@@ -5370,53 +5356,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..b54205ad 100644
--- a/src/library_glfw.js
+++ b/src/library_glfw.js
@@ -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..167e5272 100644
--- a/src/library_glut.js
+++ b/src/library_glut.js
@@ -229,7 +229,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) {
diff --git a/src/library_html5.js b/src/library_html5.js
index 703f9a74..a38c2390 100644
--- a/src/library_html5.js
+++ b/src/library_html5.js
@@ -709,7 +709,9 @@ var LibraryJSEvents = {
var eventHandler = {
target: JSEvents.findEventTarget(target),
- allowsDeferredCalls: true,
+ 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,
@@ -980,18 +982,23 @@ var LibraryJSEvents = {
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) {
- window.screen.lockOrientation(orientations);
+ succeeded = window.screen.lockOrientation(orientations);
} else if (window.screen.mozLockOrientation) {
- window.screen.mozLockOrientation(orientations);
+ succeeded = window.screen.mozLockOrientation(orientations);
} else if (window.screen.webkitLockOrientation) {
- window.screen.webkitLockOrientation(orientations);
+ succeeded = window.screen.webkitLockOrientation(orientations);
} else if (window.screen.msLockOrientation) {
- window.screen.msLockOrientation(orientations);
+ succeeded = window.screen.msLockOrientation(orientations);
} else {
return {{{ cDefine('EMSCRIPTEN_RESULT_NOT_SUPPORTED') }}};
}
- return {{{ cDefine('EMSCRIPTEN_RESULT_SUCCESS') }}};
+ if (succeeded) {
+ return {{{ cDefine('EMSCRIPTEN_RESULT_SUCCESS') }}};
+ } else {
+ return {{{ cDefine('EMSCRIPTEN_RESULT_FAILED') }}};
+ }
},
emscripten_unlock_orientation: function() {
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_openal.js b/src/library_openal.js
index 67481824..ac49fe95 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,
@@ -235,6 +239,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
@@ -334,6 +374,18 @@ var LibraryOpenAL = {
}
},
+ alIsSource: function(sourceId) {
+ if (!AL.currentContext) {
+ return false;
+ }
+
+ if (!AL.currentContext.src[sourceId - 1]) {
+ return false;
+ } else {
+ return true;
+ }
+ },
+
alSourcei__deps: ['updateSource'],
alSourcei: function(source, param, value) {
if (!AL.currentContext) {
@@ -691,6 +743,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;
@@ -715,6 +768,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) {
@@ -1128,15 +1216,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) == "function" ||
+ typeof(webkitAudioContext) == "function") {
+ ret = 'Device';
+ } else {
+ return 0;
+ }
+ break;
+ case 0x1005 /* ALC_DEVICE_SPECIFIER */:
+ if (typeof(AudioContext) == "function" ||
+ typeof(webkitAudioContext) == "function") {
+ 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 80734d95..cadc3aee 100644
--- a/src/library_sdl.js
+++ b/src/library_sdl.js
@@ -214,27 +214,33 @@ 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
+ 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') }}};// SDL_PIXELFORMAT_RGBA8888
+ {{{ 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') }}};
@@ -245,8 +251,7 @@ var LibrarySDL = {
{{{ 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 +271,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 +342,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 +412,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,
@@ -701,6 +706,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) {
@@ -986,6 +1014,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 +1078,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 +1254,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);
},
@@ -1723,6 +1762,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 +1778,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;
@@ -1773,18 +1794,18 @@ var LibrarySDL = {
SDL.audio.soundSource[SDL.audio.nextSoundSource]['start'](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 +1857,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']) {
+ 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 +2519,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 +2713,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 +2750,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/parseTools.js b/src/parseTools.js
index bad080b7..fe56580e 100644
--- a/src/parseTools.js
+++ b/src/parseTools.js
@@ -162,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] == '%';
}
@@ -172,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
diff --git a/src/postamble.js b/src/postamble.js
index 90a86474..382d3117 100644
--- a/src/postamble.js
+++ b/src/postamble.js
@@ -202,6 +202,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 9a65b092..5038e9c4 100644
--- a/src/preamble.js
+++ b/src/preamble.js
@@ -188,6 +188,8 @@ function SAFE_HEAP_STORE(dest, value, bytes, isFloat) {
#endif
assert(dest > 0, 'segmentation fault');
assert(dest % bytes === 0);
+ assert(dest < Math.max(DYNAMICTOP, STATICTOP));
+ assert(DYNAMICTOP <= TOTAL_MEMORY);
setValue(dest, value, getSafeHeapType(bytes, isFloat), 1);
}
@@ -197,6 +199,8 @@ function SAFE_HEAP_LOAD(dest, bytes, isFloat, unsigned) {
#endif
assert(dest > 0, 'segmentation fault');
assert(dest % bytes === 0);
+ assert(dest < Math.max(DYNAMICTOP, STATICTOP));
+ 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);
@@ -1023,6 +1027,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/runtime.js b/src/runtime.js
index 4fcca56b..a9265e70 100644
--- a/src/runtime.js
+++ b/src/runtime.js
@@ -565,7 +565,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;
}
@@ -578,7 +578,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;
}
diff --git a/src/settings.js b/src/settings.js
index 720fb53f..1db91dca 100644
--- a/src/settings.js
+++ b/src/settings.js
@@ -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,8 +126,11 @@ 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_ANNOTATIONS = 0; // If set, the generated code will be annotated for the closure
@@ -251,8 +259,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.
diff --git a/src/struct_info.json b/src/struct_info.json
index 32261c0a..2aeffc9c 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.