blob: d9fd3ee5d1013673f007dd97452b957f48798bee [file] [log] [blame] [edit]
//"use strict";
// Utilities for browser environments
mergeInto(LibraryManager.library, {
$Browser__postset: 'Module["requestFullScreen"] = function(lockPointer, resizeCanvas) { Browser.requestFullScreen(lockPointer, resizeCanvas) };\n' + // exports
'Module["requestAnimationFrame"] = function(func) { Browser.requestAnimationFrame(func) };\n' +
'Module["pauseMainLoop"] = function() { Browser.mainLoop.pause() };\n' +
'Module["resumeMainLoop"] = function() { Browser.mainLoop.resume() };\n' +
'Module["getUserMedia"] = function() { Browser.getUserMedia() }',
$Browser: {
mainLoop: {
scheduler: null,
shouldPause: false,
paused: false,
queue: [],
pause: function() {
Browser.mainLoop.shouldPause = true;
},
resume: function() {
if (Browser.mainLoop.paused) {
Browser.mainLoop.paused = false;
Browser.mainLoop.scheduler();
}
Browser.mainLoop.shouldPause = false;
},
updateStatus: function() {
if (Module['setStatus']) {
var message = Module['statusMessage'] || 'Please wait...';
var remaining = Browser.mainLoop.remainingBlockers;
var expected = Browser.mainLoop.expectedBlockers;
if (remaining) {
if (remaining < expected) {
Module['setStatus'](message + ' (' + (expected - remaining) + '/' + expected + ')');
} else {
Module['setStatus'](message);
}
} else {
Module['setStatus']('');
}
}
}
},
isFullScreen: false,
pointerLock: false,
moduleContextCreatedCallbacks: [],
workers: [],
init: function() {
if (!Module["preloadPlugins"]) Module["preloadPlugins"] = []; // needs to exist even in workers
if (Browser.initted || ENVIRONMENT_IS_WORKER) return;
Browser.initted = true;
try {
new Blob();
Browser.hasBlobConstructor = true;
} catch(e) {
Browser.hasBlobConstructor = false;
console.log("warning: no blob constructor, cannot create blobs with mimetypes");
}
Browser.BlobBuilder = typeof MozBlobBuilder != "undefined" ? MozBlobBuilder : (typeof WebKitBlobBuilder != "undefined" ? WebKitBlobBuilder : (!Browser.hasBlobConstructor ? console.log("warning: no BlobBuilder") : null));
Browser.URLObject = typeof window != "undefined" ? (window.URL ? window.URL : window.webkitURL) : console.log("warning: cannot create object URLs");
// Support for plugins that can process preloaded files. You can add more of these to
// your app by creating and appending to Module.preloadPlugins.
//
// Each plugin is asked if it can handle a file based on the file's name. If it can,
// it is given the file's raw data. When it is done, it calls a callback with the file's
// (possibly modified) data. For example, a plugin might decompress a file, or it
// might create some side data structure for use later (like an Image element, etc.).
function getMimetype(name) {
return {
'jpg': 'image/jpeg',
'jpeg': 'image/jpeg',
'png': 'image/png',
'bmp': 'image/bmp',
'ogg': 'audio/ogg',
'wav': 'audio/wav',
'mp3': 'audio/mpeg'
}[name.substr(name.lastIndexOf('.')+1)];
}
var imagePlugin = {};
imagePlugin['canHandle'] = function(name) {
return !Module.noImageDecoding && /\.(jpg|jpeg|png|bmp)$/.exec(name);
};
imagePlugin['handle'] = function(byteArray, name, onload, onerror) {
var b = null;
if (Browser.hasBlobConstructor) {
try {
b = new Blob([byteArray], { type: getMimetype(name) });
} catch(e) {
Runtime.warnOnce('Blob constructor present but fails: ' + e + '; falling back to blob builder');
}
}
if (!b) {
var bb = new Browser.BlobBuilder();
bb.append((new Uint8Array(byteArray)).buffer); // we need to pass a buffer, and must copy the array to get the right data range
b = bb.getBlob();
}
var url = Browser.URLObject.createObjectURL(b);
#if ASSERTIONS
assert(typeof url == 'string', 'createObjectURL must return a url as a string');
#endif
var img = new Image();
img.onload = function() {
assert(img.complete, 'Image ' + name + ' could not be decoded');
var canvas = document.createElement('canvas');
canvas.width = img.width;
canvas.height = img.height;
var ctx = canvas.getContext('2d');
ctx.drawImage(img, 0, 0);
Module["preloadedImages"][name] = canvas;
Browser.URLObject.revokeObjectURL(url);
if (onload) onload(byteArray);
};
img.onerror = function(event) {
console.log('Image ' + url + ' could not be decoded');
if (onerror) onerror();
};
img.src = url;
};
Module['preloadPlugins'].push(imagePlugin);
var audioPlugin = {};
audioPlugin['canHandle'] = function(name) {
return !Module.noAudioDecoding && name.substr(-4) in { '.ogg': 1, '.wav': 1, '.mp3': 1 };
};
audioPlugin['handle'] = function(byteArray, name, onload, onerror) {
var done = false;
function finish(audio) {
if (done) return;
done = true;
Module["preloadedAudios"][name] = audio;
if (onload) onload(byteArray);
}
function fail() {
if (done) return;
done = true;
Module["preloadedAudios"][name] = new Audio(); // empty shim
if (onerror) onerror();
}
if (Browser.hasBlobConstructor) {
try {
var b = new Blob([byteArray], { type: getMimetype(name) });
} catch(e) {
return fail();
}
var url = Browser.URLObject.createObjectURL(b); // XXX we never revoke this!
#if ASSERTIONS
assert(typeof url == 'string', 'createObjectURL must return a url as a string');
#endif
var audio = new Audio();
audio.addEventListener('canplaythrough', function() { finish(audio) }, false); // use addEventListener due to chromium bug 124926
audio.onerror = function(event) {
if (done) return;
console.log('warning: browser could not fully decode audio ' + name + ', trying slower base64 approach');
function encode64(data) {
var BASE = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
var PAD = '=';
var ret = '';
var leftchar = 0;
var leftbits = 0;
for (var i = 0; i < data.length; i++) {
leftchar = (leftchar << 8) | data[i];
leftbits += 8;
while (leftbits >= 6) {
var curr = (leftchar >> (leftbits-6)) & 0x3f;
leftbits -= 6;
ret += BASE[curr];
}
}
if (leftbits == 2) {
ret += BASE[(leftchar&3) << 4];
ret += PAD + PAD;
} else if (leftbits == 4) {
ret += BASE[(leftchar&0xf) << 2];
ret += PAD;
}
return ret;
}
audio.src = 'data:audio/x-' + name.substr(-3) + ';base64,' + encode64(byteArray);
finish(audio); // we don't wait for confirmation this worked - but it's worth trying
};
audio.src = url;
// workaround for chrome bug 124926 - we do not always get oncanplaythrough or onerror
Browser.safeSetTimeout(function() {
finish(audio); // try to use it even though it is not necessarily ready to play
}, 10000);
} else {
return fail();
}
};
Module['preloadPlugins'].push(audioPlugin);
// Canvas event setup
var canvas = Module['canvas'];
canvas.requestPointerLock = canvas['requestPointerLock'] ||
canvas['mozRequestPointerLock'] ||
canvas['webkitRequestPointerLock'];
canvas.exitPointerLock = document['exitPointerLock'] ||
document['mozExitPointerLock'] ||
document['webkitExitPointerLock'] ||
function(){}; // no-op if function does not exist
canvas.exitPointerLock = canvas.exitPointerLock.bind(document);
function pointerLockChange() {
Browser.pointerLock = document['pointerLockElement'] === canvas ||
document['mozPointerLockElement'] === canvas ||
document['webkitPointerLockElement'] === canvas;
}
document.addEventListener('pointerlockchange', pointerLockChange, false);
document.addEventListener('mozpointerlockchange', pointerLockChange, false);
document.addEventListener('webkitpointerlockchange', pointerLockChange, false);
if (Module['elementPointerLock']) {
canvas.addEventListener("click", function(ev) {
if (!Browser.pointerLock && canvas.requestPointerLock) {
canvas.requestPointerLock();
ev.preventDefault();
}
}, false);
}
},
createContext: function(canvas, useWebGL, setInModule) {
#if !USE_TYPED_ARRAYS
if (useWebGL) {
Module.print('(USE_TYPED_ARRAYS needs to be enabled for WebGL)');
return null;
}
#endif
var ctx;
try {
if (useWebGL) {
ctx = canvas.getContext('experimental-webgl', {
#if GL_TESTING
preserveDrawingBuffer: true,
#endif
alpha: false
});
} else {
ctx = canvas.getContext('2d');
}
if (!ctx) throw ':(';
} catch (e) {
Module.print('Could not create canvas - ' + e);
return null;
}
if (useWebGL) {
#if GL_DEBUG
// Useful to debug native webgl apps: var Module = { printErr: function(x) { console.log(x) } };
var tempCtx = ctx;
var wrapper = {};
for (var prop in tempCtx) {
(function(prop) {
switch (typeof tempCtx[prop]) {
case 'function': {
wrapper[prop] = function() {
if (GL.debug) {
var printArgs = Array.prototype.slice.call(arguments).map(Runtime.prettyPrint);
Module.printErr('[gl_f:' + prop + ':' + printArgs + ']');
}
var ret = tempCtx[prop].apply(tempCtx, arguments);
if (GL.debug && typeof ret != 'undefined') {
Module.printErr('[ gl:' + prop + ':return:' + Runtime.prettyPrint(ret) + ']');
}
return ret;
}
break;
}
case 'number': case 'string': {
wrapper.__defineGetter__(prop, function() {
//Module.printErr('[gl_g:' + prop + ':' + tempCtx[prop] + ']');
return tempCtx[prop];
});
wrapper.__defineSetter__(prop, function(value) {
if (GL.debug) {
Module.printErr('[gl_s:' + prop + ':' + value + ']');
}
tempCtx[prop] = value;
});
break;
}
}
})(prop);
}
ctx = wrapper;
#endif
// Set the background of the WebGL canvas to black
canvas.style.backgroundColor = "black";
// Warn on context loss
canvas.addEventListener('webglcontextlost', function(event) {
alert('WebGL context lost. You will need to reload the page.');
}, false);
}
if (setInModule) {
Module.ctx = ctx;
Module.useWebGL = useWebGL;
Browser.moduleContextCreatedCallbacks.forEach(function(callback) { callback() });
Browser.init();
}
return ctx;
},
destroyContext: function(canvas, useWebGL, setInModule) {},
fullScreenHandlersInstalled: false,
lockPointer: undefined,
resizeCanvas: undefined,
requestFullScreen: function(lockPointer, resizeCanvas) {
Browser.lockPointer = lockPointer;
Browser.resizeCanvas = resizeCanvas;
if (typeof Browser.lockPointer === 'undefined') Browser.lockPointer = true;
if (typeof Browser.resizeCanvas === 'undefined') Browser.resizeCanvas = false;
var canvas = Module['canvas'];
function fullScreenChange() {
Browser.isFullScreen = false;
if ((document['webkitFullScreenElement'] || document['webkitFullscreenElement'] ||
document['mozFullScreenElement'] || document['mozFullscreenElement'] ||
document['fullScreenElement'] || document['fullscreenElement']) === canvas) {
canvas.cancelFullScreen = document['cancelFullScreen'] ||
document['mozCancelFullScreen'] ||
document['webkitCancelFullScreen'];
canvas.cancelFullScreen = canvas.cancelFullScreen.bind(document);
if (Browser.lockPointer) canvas.requestPointerLock();
Browser.isFullScreen = true;
if (Browser.resizeCanvas) Browser.setFullScreenCanvasSize();
} else if (Browser.resizeCanvas){
Browser.setWindowedCanvasSize();
}
if (Module['onFullScreen']) Module['onFullScreen'](Browser.isFullScreen);
}
if (!Browser.fullScreenHandlersInstalled) {
Browser.fullScreenHandlersInstalled = true;
document.addEventListener('fullscreenchange', fullScreenChange, false);
document.addEventListener('mozfullscreenchange', fullScreenChange, false);
document.addEventListener('webkitfullscreenchange', fullScreenChange, false);
}
canvas.requestFullScreen = canvas['requestFullScreen'] ||
canvas['mozRequestFullScreen'] ||
(canvas['webkitRequestFullScreen'] ? function() { canvas['webkitRequestFullScreen'](Element['ALLOW_KEYBOARD_INPUT']) } : null);
canvas.requestFullScreen();
},
requestAnimationFrame: function(func) {
if (!window.requestAnimationFrame) {
window.requestAnimationFrame = window['requestAnimationFrame'] ||
window['mozRequestAnimationFrame'] ||
window['webkitRequestAnimationFrame'] ||
window['msRequestAnimationFrame'] ||
window['oRequestAnimationFrame'] ||
window['setTimeout'];
}
window.requestAnimationFrame(func);
},
// generic abort-aware wrapper for an async callback
safeCallback: function(func) {
return function() {
if (!ABORT) return func.apply(null, arguments);
};
},
// abort-aware versions
safeRequestAnimationFrame: function(func) {
return Browser.requestAnimationFrame(function() {
if (!ABORT) func();
});
},
safeSetTimeout: function(func, timeout) {
return setTimeout(function() {
if (!ABORT) func();
}, timeout);
},
safeSetInterval: function(func, timeout) {
return setInterval(function() {
if (!ABORT) func();
}, timeout);
},
getUserMedia: function(func) {
if(!window.getUserMedia) {
window.getUserMedia = navigator['getUserMedia'] ||
navigator['mozGetUserMedia'];
}
window.getUserMedia(func);
},
getMovementX: function(event) {
return event['movementX'] ||
event['mozMovementX'] ||
event['webkitMovementX'] ||
0;
},
getMovementY: function(event) {
return event['movementY'] ||
event['mozMovementY'] ||
event['webkitMovementY'] ||
0;
},
mouseX: 0,
mouseY: 0,
mouseMovementX: 0,
mouseMovementY: 0,
calculateMouseEvent: function(event) { // event should be mousemove, mousedown or mouseup
if (Browser.pointerLock) {
// When the pointer is locked, calculate the coordinates
// based on the movement of the mouse.
// Workaround for Firefox bug 764498
if (event.type != 'mousemove' &&
('mozMovementX' in event)) {
Browser.mouseMovementX = Browser.mouseMovementY = 0;
} else {
Browser.mouseMovementX = Browser.getMovementX(event);
Browser.mouseMovementY = Browser.getMovementY(event);
}
// check if SDL is available
if (typeof SDL != "undefined") {
Browser.mouseX = SDL.mouseX + Browser.mouseMovementX;
Browser.mouseY = SDL.mouseY + Browser.mouseMovementY;
} else {
// just add the mouse delta to the current absolut mouse position
// FIXME: ideally this should be clamped against the canvas size and zero
Browser.mouseX += Browser.mouseMovementX;
Browser.mouseY += Browser.mouseMovementY;
}
} else {
// Otherwise, calculate the movement based on the changes
// in the coordinates.
var rect = Module["canvas"].getBoundingClientRect();
var x = event.pageX - (window.scrollX + rect.left);
var y = event.pageY - (window.scrollY + rect.top);
// the canvas might be CSS-scaled compared to its backbuffer;
// SDL-using content will want mouse coordinates in terms
// of backbuffer units.
var cw = Module["canvas"].width;
var ch = Module["canvas"].height;
x = x * (cw / rect.width);
y = y * (ch / rect.height);
Browser.mouseMovementX = x - Browser.mouseX;
Browser.mouseMovementY = y - Browser.mouseY;
Browser.mouseX = x;
Browser.mouseY = y;
}
},
xhrLoad: function(url, onload, onerror) {
var xhr = new XMLHttpRequest();
xhr.open('GET', url, true);
xhr.responseType = 'arraybuffer';
xhr.onload = function() {
if (xhr.status == 200 || (xhr.status == 0 && xhr.response)) { // file URLs can return 0
onload(xhr.response);
} else {
onerror();
}
};
xhr.onerror = onerror;
xhr.send(null);
},
asyncLoad: function(url, onload, onerror, noRunDep) {
Browser.xhrLoad(url, function(arrayBuffer) {
assert(arrayBuffer, 'Loading data file "' + url + '" failed (no arrayBuffer).');
onload(new Uint8Array(arrayBuffer));
if (!noRunDep) removeRunDependency('al ' + url);
}, function(event) {
if (onerror) {
onerror();
} else {
throw 'Loading data file "' + url + '" failed.';
}
});
if (!noRunDep) addRunDependency('al ' + url);
},
resizeListeners: [],
updateResizeListeners: function() {
var canvas = Module['canvas'];
Browser.resizeListeners.forEach(function(listener) {
listener(canvas.width, canvas.height);
});
},
setCanvasSize: function(width, height, noUpdates) {
var canvas = Module['canvas'];
canvas.width = width;
canvas.height = height;
if (!noUpdates) Browser.updateResizeListeners();
},
windowedWidth: 0,
windowedHeight: 0,
setFullScreenCanvasSize: function() {
var canvas = Module['canvas'];
this.windowedWidth = canvas.width;
this.windowedHeight = canvas.height;
canvas.width = screen.width;
canvas.height = screen.height;
// check if SDL is available
if (typeof SDL != "undefined") {
var flags = {{{ makeGetValue('SDL.screen+Runtime.QUANTUM_SIZE*0', '0', 'i32', 0, 1) }}};
flags = flags | 0x00800000; // set SDL_FULLSCREEN flag
{{{ makeSetValue('SDL.screen+Runtime.QUANTUM_SIZE*0', '0', 'flags', 'i32') }}}
}
Browser.updateResizeListeners();
},
setWindowedCanvasSize: function() {
var canvas = Module['canvas'];
canvas.width = this.windowedWidth;
canvas.height = this.windowedHeight;
// check if SDL is available
if (typeof SDL != "undefined") {
var flags = {{{ makeGetValue('SDL.screen+Runtime.QUANTUM_SIZE*0', '0', 'i32', 0, 1) }}};
flags = flags & ~0x00800000; // clear SDL_FULLSCREEN flag
{{{ makeSetValue('SDL.screen+Runtime.QUANTUM_SIZE*0', '0', 'flags', 'i32') }}}
}
Browser.updateResizeListeners();
}
},
emscripten_async_wget: function(url, file, onload, onerror) {
var _url = Pointer_stringify(url);
var _file = Pointer_stringify(file);
var index = _file.lastIndexOf('/');
FS.createPreloadedFile(
_file.substr(0, index),
_file.substr(index +1),
_url, true, true,
function() {
if (onload) Runtime.dynCall('vi', onload, [file]);
},
function() {
if (onerror) Runtime.dynCall('vi', onerror, [file]);
}
);
},
emscripten_async_wget_data: function(url, arg, onload, onerror) {
Browser.asyncLoad(Pointer_stringify(url), function(byteArray) {
var buffer = _malloc(byteArray.length);
HEAPU8.set(byteArray, buffer);
Runtime.dynCall('viii', onload, [arg, buffer, byteArray.length]);
_free(buffer);
}, function() {
if (onerror) Runtime.dynCall('vi', onerror, [arg]);
}, true /* no need for run dependency, this is async but will not do any prepare etc. step */ );
},
emscripten_async_wget2: function(url, file, request, param, arg, onload, onerror, onprogress) {
var _url = Pointer_stringify(url);
var _file = Pointer_stringify(file);
var _request = Pointer_stringify(request);
var _param = Pointer_stringify(param);
var index = _file.lastIndexOf('/');
var http = new XMLHttpRequest();
http.open(_request, _url, true);
http.responseType = 'arraybuffer';
// LOAD
http.onload = function(e) {
if (http.status == 200) {
FS.createDataFile( _file.substr(0, index), _file.substr(index + 1), new Uint8Array(http.response), true, true);
if (onload) Runtime.dynCall('vii', onload, [arg, file]);
} else {
if (onerror) Runtime.dynCall('vii', onerror, [arg, http.status]);
}
};
// ERROR
http.onerror = function(e) {
if (onerror) Runtime.dynCall('vii', onerror, [arg, http.status]);
};
// PROGRESS
http.onprogress = function(e) {
var percentComplete = (e.position / e.totalSize)*100;
if (onprogress) Runtime.dynCall('vii', onprogress, [arg, percentComplete]);
};
// Useful because the browser can limit the number of redirection
try {
if (http.channel instanceof Ci.nsIHttpChannel)
http.channel.redirectionLimit = 0;
} catch (ex) { /* whatever */ }
if (_request == "POST") {
//Send the proper header information along with the request
http.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
http.setRequestHeader("Content-length", _param.length);
http.setRequestHeader("Connection", "close");
http.send(_param);
} else {
http.send(null);
}
},
emscripten_async_prepare: function(file, onload, onerror) {
var _file = Pointer_stringify(file);
var data = FS.analyzePath(_file);
if (!data.exists) return -1;
var index = _file.lastIndexOf('/');
FS.createPreloadedFile(
_file.substr(0, index),
_file.substr(index +1),
new Uint8Array(data.object.contents), true, true,
function() {
if (onload) Runtime.dynCall('vi', onload, [file]);
},
function() {
if (onerror) Runtime.dynCall('vi', onerror, [file]);
},
true // don'tCreateFile - it's already there
);
return 0;
},
emscripten_async_prepare_data: function(data, size, suffix, arg, onload, onerror) {
var _suffix = Pointer_stringify(suffix);
if (!Browser.asyncPrepareDataCounter) Browser.asyncPrepareDataCounter = 0;
var name = 'prepare_data_' + (Browser.asyncPrepareDataCounter++) + '.' + _suffix;
var cname = _malloc(name.length+1);
writeStringToMemory(name, cname);
FS.createPreloadedFile(
'',
name,
{{{ makeHEAPView('U8', 'data', 'data + size') }}},
true, true,
function() {
if (onload) Runtime.dynCall('vii', onload, [arg, cname]);
},
function() {
if (onerror) Runtime.dynCall('vi', onerror, [arg]);
},
true // don'tCreateFile - it's already there
);
return 0;
},
emscripten_async_run_script__deps: ['emscripten_run_script'],
emscripten_async_run_script: function(script, millis) {
Module['noExitRuntime'] = true;
// TODO: cache these to avoid generating garbage
Browser.safeSetTimeout(function() {
_emscripten_run_script(script);
}, millis);
},
emscripten_set_main_loop: function(func, fps, simulateInfiniteLoop) {
Module['noExitRuntime'] = true;
Browser.mainLoop.runner = function() {
if (ABORT) return;
if (Browser.mainLoop.queue.length > 0) {
var start = Date.now();
var blocker = Browser.mainLoop.queue.shift();
blocker.func(blocker.arg);
if (Browser.mainLoop.remainingBlockers) {
var remaining = Browser.mainLoop.remainingBlockers;
var next = remaining%1 == 0 ? remaining-1 : Math.floor(remaining);
if (blocker.counted) {
Browser.mainLoop.remainingBlockers = next;
} else {
// not counted, but move the progress along a tiny bit
next = next + 0.5; // do not steal all the next one's progress
Browser.mainLoop.remainingBlockers = (8*remaining + next)/9;
}
}
console.log('main loop blocker "' + blocker.name + '" took ' + (Date.now() - start) + ' ms'); //, left: ' + Browser.mainLoop.remainingBlockers);
Browser.mainLoop.updateStatus();
setTimeout(Browser.mainLoop.runner, 0);
return;
}
if (Browser.mainLoop.shouldPause) {
// catch pauses from non-main loop sources
Browser.mainLoop.paused = true;
Browser.mainLoop.shouldPause = false;
return;
}
if (Module['preMainLoop']) {
Module['preMainLoop']();
}
Runtime.dynCall('v', func);
if (Module['postMainLoop']) {
Module['postMainLoop']();
}
if (Browser.mainLoop.shouldPause) {
// catch pauses from the main loop itself
Browser.mainLoop.paused = true;
Browser.mainLoop.shouldPause = false;
return;
}
Browser.mainLoop.scheduler();
}
if (fps && fps > 0) {
Browser.mainLoop.scheduler = function() {
setTimeout(Browser.mainLoop.runner, 1000/fps); // doing this each time means that on exception, we stop
}
} else {
Browser.mainLoop.scheduler = function() {
Browser.requestAnimationFrame(Browser.mainLoop.runner);
}
}
Browser.mainLoop.scheduler();
if (simulateInfiniteLoop) {
throw 'SimulateInfiniteLoop';
}
},
emscripten_cancel_main_loop: function() {
Browser.mainLoop.scheduler = null;
Browser.mainLoop.shouldPause = true;
},
emscripten_pause_main_loop: function() {
Browser.mainLoop.pause();
},
emscripten_resume_main_loop: function() {
Browser.mainLoop.resume();
},
_emscripten_push_main_loop_blocker: function(func, arg, name) {
Browser.mainLoop.queue.push({ func: function() {
Runtime.dynCall('vi', func, [arg]);
}, name: Pointer_stringify(name), counted: true });
Browser.mainLoop.updateStatus();
},
_emscripten_push_uncounted_main_loop_blocker: function(func, arg, name) {
Browser.mainLoop.queue.push({ func: function() {
Runtime.dynCall('vi', func, [arg]);
}, name: Pointer_stringify(name), counted: false });
Browser.mainLoop.updateStatus();
},
emscripten_set_main_loop_expected_blockers: function(num) {
Browser.mainLoop.expectedBlockers = num;
Browser.mainLoop.remainingBlockers = num;
Browser.mainLoop.updateStatus();
},
emscripten_async_call: function(func, arg, millis) {
Module['noExitRuntime'] = true;
function wrapper() {
Runtime.getFuncWrapper(func, 'vi')(arg);
}
if (millis >= 0) {
Browser.safeSetTimeout(wrapper, millis);
} else {
Browser.safeRequestAnimationFrame(wrapper);
}
},
emscripten_exit_with_live_runtime: function() {
Module['noExitRuntime'] = true;
throw 'SimulateInfiniteLoop';
},
emscripten_hide_mouse: function() {
var styleSheet = document.styleSheets[0];
var rules = styleSheet.cssRules;
for (var i = 0; i < rules.length; i++) {
if (rules[i].cssText.substr(0, 5) == 'canvas') {
styleSheet.deleteRule(i);
i--;
}
}
styleSheet.insertRule('canvas.emscripten { border: 1px solid black; cursor: none; }', 0);
},
emscripten_set_canvas_size: function(width, height) {
Browser.setCanvasSize(width, height);
},
emscripten_get_now: function() {
if (ENVIRONMENT_IS_NODE) {
var t = process['hrtime']();
return t[0] * 1e3 + t[1] / 1e6;
}
else if (ENVIRONMENT_IS_WEB && window['performance'] && window['performance']['now']) {
return window['performance']['now']();
} else {
return Date.now();
}
},
emscripten_create_worker: function(url) {
url = Pointer_stringify(url);
var id = Browser.workers.length;
var info = {
worker: new Worker(url),
callbacks: [],
awaited: 0,
buffer: 0,
bufferSize: 0
};
info.worker.onmessage = function(msg) {
var info = Browser.workers[id];
if (!info) return; // worker was destroyed meanwhile
var callbackId = msg.data['callbackId'];
var callbackInfo = info.callbacks[callbackId];
if (!callbackInfo) return; // no callback or callback removed meanwhile
info.awaited--;
info.callbacks[callbackId] = null; // TODO: reuse callbackIds, compress this
var data = msg.data['data'];
if (data) {
if (!data.byteLength) data = new Uint8Array(data);
if (!info.buffer || info.bufferSize < data.length) {
if (info.buffer) _free(info.buffer);
info.bufferSize = data.length;
info.buffer = _malloc(data.length);
}
HEAPU8.set(data, info.buffer);
callbackInfo.func(info.buffer, data.length, callbackInfo.arg);
} else {
callbackInfo.func(0, 0, callbackInfo.arg);
}
};
Browser.workers.push(info);
return id;
},
emscripten_destroy_worker: function(id) {
var info = Browser.workers[id];
info.worker.terminate();
if (info.buffer) _free(info.buffer);
Browser.workers[id] = null;
},
emscripten_call_worker: function(id, funcName, data, size, callback, arg) {
funcName = Pointer_stringify(funcName);
var info = Browser.workers[id];
var callbackId = -1;
if (callback) {
callbackId = info.callbacks.length;
info.callbacks.push({
func: Runtime.getFuncWrapper(callback, 'viii'),
arg: arg
});
info.awaited++;
}
info.worker.postMessage({
'funcName': funcName,
'callbackId': callbackId,
'data': data ? new Uint8Array({{{ makeHEAPView('U8', 'data', 'data + size') }}}) : 0 // XXX copy to a new typed array as a workaround for chrome bug 169705
});
},
emscripten_worker_respond: function(data, size) {
if (!inWorkerCall) throw 'not in worker call!';
if (workerResponded) throw 'already responded!';
workerResponded = true;
postMessage({
'callbackId': workerCallbackId,
'data': data ? new Uint8Array({{{ makeHEAPView('U8', 'data', 'data + size') }}}) : 0 // XXX copy to a new typed array as a workaround for chrome bug 169705
});
},
emscripten_get_worker_queue_size: function(id) {
var info = Browser.workers[id];
if (!info) return -1;
return info.awaited;
}
});
/* Useful stuff for browser debugging
function slowLog(label, text) {
if (!slowLog.labels) slowLog.labels = {};
if (!slowLog.labels[label]) slowLog.labels[label] = 0;
var now = Date.now();
if (now - slowLog.labels[label] > 1000) {
Module.print(label + ': ' + text);
slowLog.labels[label] = now;
}
}
*/