aboutsummaryrefslogtreecommitdiff
path: root/src/library_browser.js
diff options
context:
space:
mode:
Diffstat (limited to 'src/library_browser.js')
-rw-r--r--src/library_browser.js269
1 files changed, 216 insertions, 53 deletions
diff --git a/src/library_browser.js b/src/library_browser.js
index fd09f478..a690286b 100644
--- a/src/library_browser.js
+++ b/src/library_browser.js
@@ -3,10 +3,11 @@
// Utilities for browser environments
mergeInto(LibraryManager.library, {
- $Browser__postset: 'Module["requestFullScreen"] = function() { Browser.requestFullScreen() };\n' + // exports
+ $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["resumeMainLoop"] = function() { Browser.mainLoop.resume() };\n' +
+ 'Module["getUserMedia"] = function() { Browser.getUserMedia() }',
$Browser: {
mainLoop: {
scheduler: null,
@@ -40,13 +41,14 @@ mergeInto(LibraryManager.library, {
}
}
},
+ isFullScreen: false,
pointerLock: false,
moduleContextCreatedCallbacks: [],
workers: [],
- ensureObjects: function() {
- if (Browser.ensured) return;
- Browser.ensured = true;
+ init: function() {
+ if (Browser.initted) return;
+ Browser.initted = true;
try {
new Blob();
Browser.hasBlobConstructor = true;
@@ -68,20 +70,20 @@ mergeInto(LibraryManager.library, {
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(-3)];
- return ret;
+ }[name.substr(name.lastIndexOf('.')+1)];
}
if (!Module["preloadPlugins"]) Module["preloadPlugins"] = [];
var imagePlugin = {};
imagePlugin['canHandle'] = function(name) {
- return name.substr(-4) in { '.jpg': 1, '.png': 1, '.bmp': 1 };
+ return !Module.noImageDecoding && /\.(jpg|jpeg|png|bmp)$/.exec(name);
};
imagePlugin['handle'] = function(byteArray, name, onload, onerror) {
var b = null;
@@ -98,7 +100,9 @@ mergeInto(LibraryManager.library, {
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');
@@ -121,7 +125,7 @@ mergeInto(LibraryManager.library, {
var audioPlugin = {};
audioPlugin['canHandle'] = function(name) {
- return name.substr(-4) in { '.ogg': 1, '.wav': 1, '.mp3': 1 };
+ return !Module.noAudioDecoding && name.substr(-4) in { '.ogg': 1, '.wav': 1, '.mp3': 1 };
};
audioPlugin['handle'] = function(byteArray, name, onload, onerror) {
var done = false;
@@ -144,7 +148,9 @@ mergeInto(LibraryManager.library, {
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) {
@@ -179,7 +185,7 @@ mergeInto(LibraryManager.library, {
};
audio.src = url;
// workaround for chrome bug 124926 - we do not always get oncanplaythrough or onerror
- setTimeout(function() {
+ Browser.safeSetTimeout(function() {
finish(audio); // try to use it even though it is not necessarily ready to play
}, 10000);
} else {
@@ -187,6 +193,37 @@ mergeInto(LibraryManager.library, {
}
};
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) {
@@ -196,8 +233,18 @@ mergeInto(LibraryManager.library, {
return null;
}
#endif
+ var ctx;
try {
- var ctx = canvas.getContext(useWebGL ? 'experimental-webgl' : '2d');
+ 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);
@@ -255,44 +302,52 @@ mergeInto(LibraryManager.library, {
Module.ctx = ctx;
Module.useWebGL = useWebGL;
Browser.moduleContextCreatedCallbacks.forEach(function(callback) { callback() });
+ Browser.init();
}
return ctx;
},
- requestFullScreen: function() {
+ 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() {
- var isFullScreen = false;
+ Browser.isFullScreen = false;
if ((document['webkitFullScreenElement'] || document['webkitFullscreenElement'] ||
document['mozFullScreenElement'] || document['mozFullscreenElement'] ||
document['fullScreenElement'] || document['fullscreenElement']) === canvas) {
- canvas.requestPointerLock = canvas['requestPointerLock'] ||
- canvas['mozRequestPointerLock'] ||
- canvas['webkitRequestPointerLock'];
- canvas.requestPointerLock();
- isFullScreen = true;
+ 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'](isFullScreen);
+ if (Module['onFullScreen']) Module['onFullScreen'](Browser.isFullScreen);
}
- document.addEventListener('fullscreenchange', fullScreenChange, false);
- document.addEventListener('mozfullscreenchange', fullScreenChange, false);
- document.addEventListener('webkitfullscreenchange', fullScreenChange, false);
-
- function pointerLockChange() {
- Browser.pointerLock = document['pointerLockElement'] === canvas ||
- document['mozPointerLockElement'] === canvas ||
- document['webkitPointerLockElement'] === canvas;
+ if (!Browser.fullScreenHandlersInstalled) {
+ Browser.fullScreenHandlersInstalled = true;
+ document.addEventListener('fullscreenchange', fullScreenChange, false);
+ document.addEventListener('mozfullscreenchange', fullScreenChange, false);
+ document.addEventListener('webkitfullscreenchange', fullScreenChange, false);
}
- document.addEventListener('pointerlockchange', pointerLockChange, false);
- document.addEventListener('mozpointerlockchange', pointerLockChange, false);
- document.addEventListener('webkitpointerlockchange', pointerLockChange, false);
-
canvas.requestFullScreen = canvas['requestFullScreen'] ||
canvas['mozRequestFullScreen'] ||
(canvas['webkitRequestFullScreen'] ? function() { canvas['webkitRequestFullScreen'](Element['ALLOW_KEYBOARD_INPUT']) } : null);
- canvas.requestFullScreen();
+ canvas.requestFullScreen();
},
requestAnimationFrame: function(func) {
@@ -307,6 +362,38 @@ mergeInto(LibraryManager.library, {
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) {
+ Browser.requestAnimationFrame(function() {
+ if (!ABORT) func();
+ });
+ },
+ safeSetTimeout: function(func, timeout) {
+ setTimeout(function() {
+ if (!ABORT) func();
+ }, timeout);
+ },
+ safeSetInterval: function(func, timeout) {
+ 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'] ||
@@ -321,12 +408,53 @@ mergeInto(LibraryManager.library, {
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);
+ }
+ Browser.mouseX = SDL.mouseX + Browser.mouseMovementX;
+ Browser.mouseY = SDL.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) {
+ if (xhr.status == 200 || (xhr.status == 0 && xhr.response)) { // file URLs can return 0
onload(xhr.response);
} else {
onerror();
@@ -365,7 +493,32 @@ mergeInto(LibraryManager.library, {
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;
+ 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;
+ 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) {
@@ -389,10 +542,10 @@ mergeInto(LibraryManager.library, {
Browser.asyncLoad(Pointer_stringify(url), function(byteArray) {
var buffer = _malloc(byteArray.length);
HEAPU8.set(byteArray, buffer);
- FUNCTION_TABLE[onload](arg, buffer, byteArray.length);
+ Runtime.dynCall('viii', onload, [arg, buffer, byteArray.length]);
_free(buffer);
}, function() {
- if (onerror) FUNCTION_TABLE[onerror](arg);
+ if (onerror) Runtime.dynCall('vi', onerror, [arg]);
}, true /* no need for run dependency, this is async but will not do any prepare etc. step */ );
},
@@ -402,34 +555,34 @@ mergeInto(LibraryManager.library, {
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) FUNCTION_TABLE[onload](arg, file);
+ if (onload) Runtime.dynCall('vii', onload, [arg, file]);
} else {
- if (onerror) FUNCTION_TABLE[onerror](arg, http.status);
+ if (onerror) Runtime.dynCall('vii', onerror, [arg, http.status]);
}
};
-
+
// ERROR
http.onerror = function(e) {
- if (onerror) FUNCTION_TABLE[onerror](arg, http.status);
+ if (onerror) Runtime.dynCall('vii', onerror, [arg, http.status]);
};
-
+
// PROGRESS
http.onprogress = function(e) {
var percentComplete = (e.position / e.totalSize)*100;
- if (onprogress) FUNCTION_TABLE[onprogress](arg, percentComplete);
+ if (onprogress) Runtime.dynCall('vii', onprogress, [arg, percentComplete]);
};
-
+
// Useful because the browser can limit the number of redirection
- try {
+ try {
if (http.channel instanceof Ci.nsIHttpChannel)
http.channel.redirectionLimit = 0;
} catch (ex) { /* whatever */ }
@@ -444,7 +597,7 @@ mergeInto(LibraryManager.library, {
http.send(null);
}
},
-
+
emscripten_async_prepare: function(file, onload, onerror) {
var _file = Pointer_stringify(file);
var data = FS.analyzePath(_file);
@@ -492,7 +645,7 @@ mergeInto(LibraryManager.library, {
Module['noExitRuntime'] = true;
// TODO: cache these to avoid generating garbage
- setTimeout(function() {
+ Browser.safeSetTimeout(function() {
_emscripten_run_script(script);
}, millis);
},
@@ -501,6 +654,7 @@ mergeInto(LibraryManager.library, {
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();
@@ -558,7 +712,7 @@ mergeInto(LibraryManager.library, {
Browser.mainLoop.scheduler();
if (simulateInfiniteLoop) {
- throw 'emscripten_set_main_loop simulating infinite loop by throwing so we get right into the JS event loop';
+ throw 'SimulateInfiniteLoop';
}
},
@@ -603,12 +757,17 @@ mergeInto(LibraryManager.library, {
}
if (millis >= 0) {
- setTimeout(wrapper, millis);
+ Browser.safeSetTimeout(wrapper, millis);
} else {
- Browser.requestAnimationFrame(wrapper);
+ 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;
@@ -626,7 +785,11 @@ mergeInto(LibraryManager.library, {
},
emscripten_get_now: function() {
- if (window['performance'] && window['performance']['now']) {
+ if (ENVIRONMENT_IS_NODE) {
+ var t = process['hrtime']();
+ return t[0] * 1e3 + t[1] / 1e6;
+ }
+ else if (window['performance'] && window['performance']['now']) {
return window['performance']['now']();
} else {
return Date.now();
@@ -691,7 +854,7 @@ mergeInto(LibraryManager.library, {
info.worker.postMessage({
'funcName': funcName,
'callbackId': callbackId,
- 'data': data ? {{{ makeHEAPView('U8', 'data', 'data + size') }}} : 0
+ 'data': data ? new Uint8Array({{{ makeHEAPView('U8', 'data', 'data + size') }}}) : 0 // XXX copy to a new typed array as a workaround for chrome bug 169705
});
},
@@ -701,7 +864,7 @@ mergeInto(LibraryManager.library, {
workerResponded = true;
postMessage({
'callbackId': workerCallbackId,
- 'data': data ? {{{ makeHEAPView('U8', 'data', 'data + size') }}} : 0
+ 'data': data ? new Uint8Array({{{ makeHEAPView('U8', 'data', 'data + size') }}}) : 0 // XXX copy to a new typed array as a workaround for chrome bug 169705
});
},