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.js231
1 files changed, 188 insertions, 43 deletions
diff --git a/src/library_browser.js b/src/library_browser.js
index b800292c..4be7315e 100644
--- a/src/library_browser.js
+++ b/src/library_browser.js
@@ -1,7 +1,6 @@
//"use strict";
// Utilities for browser environments
-
mergeInto(LibraryManager.library, {
$Browser__deps: ['$PATH'],
$Browser__postset: 'Module["requestFullScreen"] = function Module_requestFullScreen(lockPointer, resizeCanvas) { Browser.requestFullScreen(lockPointer, resizeCanvas) };\n' + // exports
@@ -197,24 +196,33 @@ mergeInto(LibraryManager.library, {
// Canvas event setup
var canvas = Module['canvas'];
+
+ // forced aspect ratio can be enabled by defining 'forcedAspectRatio' on Module
+ // Module['forcedAspectRatio'] = 4 / 3;
+
canvas.requestPointerLock = canvas['requestPointerLock'] ||
canvas['mozRequestPointerLock'] ||
- canvas['webkitRequestPointerLock'];
+ canvas['webkitRequestPointerLock'] ||
+ canvas['msRequestPointerLock'] ||
+ function(){};
canvas.exitPointerLock = document['exitPointerLock'] ||
document['mozExitPointerLock'] ||
document['webkitExitPointerLock'] ||
+ document['msExitPointerLock'] ||
function(){}; // no-op if function does not exist
canvas.exitPointerLock = canvas.exitPointerLock.bind(document);
function pointerLockChange() {
Browser.pointerLock = document['pointerLockElement'] === canvas ||
document['mozPointerLockElement'] === canvas ||
- document['webkitPointerLockElement'] === canvas;
+ document['webkitPointerLockElement'] === canvas ||
+ document['msPointerLockElement'] === canvas;
}
document.addEventListener('pointerlockchange', pointerLockChange, false);
document.addEventListener('mozpointerlockchange', pointerLockChange, false);
document.addEventListener('webkitpointerlockchange', pointerLockChange, false);
+ document.addEventListener('mspointerlockchange', pointerLockChange, false);
if (Module['elementPointerLock']) {
canvas.addEventListener("click", function(ev) {
@@ -342,20 +350,32 @@ mergeInto(LibraryManager.library, {
var canvas = Module['canvas'];
function fullScreenChange() {
Browser.isFullScreen = false;
+ var canvasContainer = canvas.parentNode;
if ((document['webkitFullScreenElement'] || document['webkitFullscreenElement'] ||
document['mozFullScreenElement'] || document['mozFullscreenElement'] ||
- document['fullScreenElement'] || document['fullscreenElement']) === canvas) {
+ document['fullScreenElement'] || document['fullscreenElement'] ||
+ document['msFullScreenElement'] || document['msFullscreenElement'] ||
+ document['webkitCurrentFullScreenElement']) === canvasContainer) {
canvas.cancelFullScreen = document['cancelFullScreen'] ||
document['mozCancelFullScreen'] ||
- document['webkitCancelFullScreen'];
+ document['webkitCancelFullScreen'] ||
+ document['msExitFullscreen'] ||
+ document['exitFullscreen'] ||
+ function() {};
canvas.cancelFullScreen = canvas.cancelFullScreen.bind(document);
if (Browser.lockPointer) canvas.requestPointerLock();
Browser.isFullScreen = true;
if (Browser.resizeCanvas) Browser.setFullScreenCanvasSize();
- } else if (Browser.resizeCanvas){
- Browser.setWindowedCanvasSize();
+ } else {
+
+ // remove the full screen specific parent of the canvas again to restore the HTML structure from before going full screen
+ canvasContainer.parentNode.insertBefore(canvas, canvasContainer);
+ canvasContainer.parentNode.removeChild(canvasContainer);
+
+ if (Browser.resizeCanvas) Browser.setWindowedCanvasSize();
}
if (Module['onFullScreen']) Module['onFullScreen'](Browser.isFullScreen);
+ Browser.updateCanvasDimensions(canvas);
}
if (!Browser.fullScreenHandlersInstalled) {
@@ -363,12 +383,20 @@ mergeInto(LibraryManager.library, {
document.addEventListener('fullscreenchange', fullScreenChange, false);
document.addEventListener('mozfullscreenchange', fullScreenChange, false);
document.addEventListener('webkitfullscreenchange', fullScreenChange, false);
+ document.addEventListener('MSFullscreenChange', fullScreenChange, false);
}
- canvas.requestFullScreen = canvas['requestFullScreen'] ||
- canvas['mozRequestFullScreen'] ||
- (canvas['webkitRequestFullScreen'] ? function() { canvas['webkitRequestFullScreen'](Element['ALLOW_KEYBOARD_INPUT']) } : null);
- canvas.requestFullScreen();
+ // create a new parent to ensure the canvas has no siblings. this allows browsers to optimize full screen performance when its parent is the full screen root
+ var canvasContainer = document.createElement("div");
+ canvas.parentNode.insertBefore(canvasContainer, canvas);
+ canvasContainer.appendChild(canvas);
+
+ // use parent of canvas as full screen root to allow aspect ratio correction (Firefox stretches the root to screen size)
+ canvasContainer.requestFullScreen = canvasContainer['requestFullScreen'] ||
+ canvasContainer['mozRequestFullScreen'] ||
+ canvasContainer['msRequestFullscreen'] ||
+ (canvasContainer['webkitRequestFullScreen'] ? function() { canvasContainer['webkitRequestFullScreen'](Element['ALLOW_KEYBOARD_INPUT']) } : null);
+ canvasContainer.requestFullScreen();
},
requestAnimationFrame: function requestAnimationFrame(func) {
@@ -454,6 +482,8 @@ mergeInto(LibraryManager.library, {
mouseY: 0,
mouseMovementX: 0,
mouseMovementY: 0,
+ touches: {},
+ lastTouches: {},
calculateMouseEvent: function(event) { // event should be mousemove, mousedown or mouseup
if (Browser.pointerLock) {
@@ -482,8 +512,9 @@ mergeInto(LibraryManager.library, {
// Otherwise, calculate the movement based on the changes
// in the coordinates.
var rect = Module["canvas"].getBoundingClientRect();
- var x, y;
-
+ var cw = Module["canvas"].width;
+ var ch = Module["canvas"].height;
+
// Neither .scrollX or .pageXOffset are defined in a spec, but
// we prefer .scrollX because it is currently in a spec draft.
// (see: http://www.w3.org/TR/2013/WD-cssom-view-20131217/)
@@ -494,26 +525,37 @@ mergeInto(LibraryManager.library, {
// and we have no viable fallback.
assert((typeof scrollX !== 'undefined') && (typeof scrollY !== 'undefined'), 'Unable to retrieve scroll position, mouse positions likely broken.');
#endif
- if (event.type == 'touchstart' ||
- event.type == 'touchend' ||
- event.type == 'touchmove') {
- var t = event.touches.item(0);
- if (t) {
- x = t.pageX - (scrollX + rect.left);
- y = t.pageY - (scrollY + rect.top);
- } else {
- return;
+
+ if (event.type === 'touchstart' || event.type === 'touchend' || event.type === 'touchmove') {
+ var touch = event.touch;
+ if (touch === undefined) {
+ return; // the "touch" property is only defined in SDL
+
}
- } else {
- x = event.pageX - (scrollX + rect.left);
- y = event.pageY - (scrollY + rect.top);
+ var adjustedX = touch.pageX - (scrollX + rect.left);
+ var adjustedY = touch.pageY - (scrollY + rect.top);
+
+ adjustedX = adjustedX * (cw / rect.width);
+ adjustedY = adjustedY * (ch / rect.height);
+
+ var coords = { x: adjustedX, y: adjustedY };
+
+ if (event.type === 'touchstart') {
+ Browser.lastTouches[touch.identifier] = coords;
+ Browser.touches[touch.identifier] = coords;
+ } else if (event.type === 'touchend' || event.type === 'touchmove') {
+ Browser.lastTouches[touch.identifier] = Browser.touches[touch.identifier];
+ Browser.touches[touch.identifier] = { x: adjustedX, y: adjustedY };
+ }
+ return;
}
+ var x = event.pageX - (scrollX + rect.left);
+ var y = event.pageY - (scrollY + rect.top);
+
// the canvas might be CSS-scaled compared to its backbuffer;
// SDL-using content will want mouse coordinates in terms
// of backbuffer units.
- var cw = Module["canvas"].width;
- var ch = Module["canvas"].height;
x = x * (cw / rect.width);
y = y * (ch / rect.height);
@@ -565,19 +607,13 @@ mergeInto(LibraryManager.library, {
setCanvasSize: function(width, height, noUpdates) {
var canvas = Module['canvas'];
- canvas.width = width;
- canvas.height = height;
+ Browser.updateCanvasDimensions(canvas, width, height);
if (!noUpdates) Browser.updateResizeListeners();
},
windowedWidth: 0,
windowedHeight: 0,
setFullScreenCanvasSize: function() {
- var canvas = Module['canvas'];
- this.windowedWidth = canvas.width;
- this.windowedHeight = canvas.height;
- canvas.width = screen.width;
- canvas.height = screen.height;
// check if SDL is available
if (typeof SDL != "undefined") {
var flags = {{{ makeGetValue('SDL.screen+Runtime.QUANTUM_SIZE*0', '0', 'i32', 0, 1) }}};
@@ -588,9 +624,6 @@ mergeInto(LibraryManager.library, {
},
setWindowedCanvasSize: function() {
- var canvas = Module['canvas'];
- canvas.width = this.windowedWidth;
- canvas.height = this.windowedHeight;
// check if SDL is available
if (typeof SDL != "undefined") {
var flags = {{{ makeGetValue('SDL.screen+Runtime.QUANTUM_SIZE*0', '0', 'i32', 0, 1) }}};
@@ -598,8 +631,55 @@ mergeInto(LibraryManager.library, {
{{{ makeSetValue('SDL.screen+Runtime.QUANTUM_SIZE*0', '0', 'flags', 'i32') }}}
}
Browser.updateResizeListeners();
- }
+ },
+ updateCanvasDimensions : function(canvas, wNative, hNative) {
+ if (wNative && hNative) {
+ canvas.widthNative = wNative;
+ canvas.heightNative = hNative;
+ } else {
+ wNative = canvas.widthNative;
+ hNative = canvas.heightNative;
+ }
+ var w = wNative;
+ var h = hNative;
+ if (Module['forcedAspectRatio'] && Module['forcedAspectRatio'] > 0) {
+ if (w/h < Module['forcedAspectRatio']) {
+ w = Math.round(h * Module['forcedAspectRatio']);
+ } else {
+ h = Math.round(w / Module['forcedAspectRatio']);
+ }
+ }
+ if (((document['webkitFullScreenElement'] || document['webkitFullscreenElement'] ||
+ document['mozFullScreenElement'] || document['mozFullscreenElement'] ||
+ document['fullScreenElement'] || document['fullscreenElement'] ||
+ document['msFullScreenElement'] || document['msFullscreenElement'] ||
+ document['webkitCurrentFullScreenElement']) === canvas.parentNode) && (typeof screen != 'undefined')) {
+ var factor = Math.min(screen.width / w, screen.height / h);
+ w = Math.round(w * factor);
+ h = Math.round(h * factor);
+ }
+ if (Browser.resizeCanvas) {
+ if (canvas.width != w) canvas.width = w;
+ if (canvas.height != h) canvas.height = h;
+ if (typeof canvas.style != 'undefined') {
+ canvas.style.removeProperty( "width");
+ canvas.style.removeProperty("height");
+ }
+ } else {
+ if (canvas.width != wNative) canvas.width = wNative;
+ if (canvas.height != hNative) canvas.height = hNative;
+ if (typeof canvas.style != 'undefined') {
+ if (w != wNative || h != hNative) {
+ canvas.style.setProperty( "width", w + "px", "important");
+ canvas.style.setProperty("height", h + "px", "important");
+ } else {
+ canvas.style.removeProperty( "width");
+ canvas.style.removeProperty("height");
+ }
+ }
+ }
+ }
},
emscripten_async_wget: function(url, file, onload, onerror) {
@@ -657,8 +737,59 @@ mergeInto(LibraryManager.library, {
// PROGRESS
http.onprogress = function http_onprogress(e) {
- var percentComplete = (e.position / e.totalSize)*100;
- if (onprogress) Runtime.dynCall('vii', onprogress, [arg, percentComplete]);
+ if (e.lengthComputable || (e.lengthComputable === undefined && e.totalSize != 0)) {
+ var percentComplete = (e.position / e.totalSize)*100;
+ if (onprogress) Runtime.dynCall('vii', onprogress, [arg, percentComplete]);
+ }
+ };
+
+ // Useful because the browser can limit the number of redirection
+ try {
+ if (http.channel instanceof Ci.nsIHttpChannel)
+ http.channel.redirectionLimit = 0;
+ } catch (ex) { /* whatever */ }
+
+ if (_request == "POST") {
+ //Send the proper header information along with the request
+ http.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
+ http.setRequestHeader("Content-length", _param.length);
+ http.setRequestHeader("Connection", "close");
+ http.send(_param);
+ } else {
+ http.send(null);
+ }
+ },
+
+ emscripten_async_wget2_data: function(url, request, param, arg, free, onload, onerror, onprogress) {
+ var _url = Pointer_stringify(url);
+ var _request = Pointer_stringify(request);
+ var _param = Pointer_stringify(param);
+
+ var http = new XMLHttpRequest();
+ http.open(_request, _url, true);
+ http.responseType = 'arraybuffer';
+
+ // LOAD
+ http.onload = function http_onload(e) {
+ if (http.status == 200 || _url.substr(0,4).toLowerCase() != "http") {
+ var byteArray = new Uint8Array(http.response);
+ var buffer = _malloc(byteArray.length);
+ HEAPU8.set(byteArray, buffer);
+ if (onload) Runtime.dynCall('viii', onload, [arg, buffer, byteArray.length]);
+ if (free) _free(buffer);
+ } else {
+ if (onerror) Runtime.dynCall('viii', onerror, [arg, http.status, http.statusText]);
+ }
+ };
+
+ // ERROR
+ http.onerror = function http_onerror(e) {
+ if (onerror) Runtime.dynCall('viii', onerror, [arg, http.status, http.statusText]);
+ };
+
+ // PROGRESS
+ http.onprogress = function http_onprogress(e) {
+ if (onprogress) Runtime.dynCall('viii', onprogress, [arg, e.loaded, e.lengthComputable || e.lengthComputable === undefined ? e.total : 0]);
};
// Useful because the browser can limit the number of redirection
@@ -943,8 +1074,11 @@ mergeInto(LibraryManager.library, {
var callbackId = msg.data['callbackId'];
var callbackInfo = info.callbacks[callbackId];
if (!callbackInfo) return; // no callback or callback removed meanwhile
- info.awaited--;
- info.callbacks[callbackId] = null; // TODO: reuse callbackIds, compress this
+ // Don't trash our callback state if we expect additional calls.
+ if (msg.data['finalResponse']) {
+ info.awaited--;
+ info.callbacks[callbackId] = null; // TODO: reuse callbackIds, compress this
+ }
var data = msg.data['data'];
if (data) {
if (!data.byteLength) data = new Uint8Array(data);
@@ -991,12 +1125,23 @@ mergeInto(LibraryManager.library, {
});
},
+ emscripten_worker_respond_provisionally: function(data, size) {
+ if (!inWorkerCall) throw 'not in worker call!';
+ if (workerResponded) throw 'already responded with final response!';
+ postMessage({
+ 'callbackId': workerCallbackId,
+ 'finalResponse': false,
+ 'data': data ? new Uint8Array({{{ makeHEAPView('U8', 'data', 'data + size') }}}) : 0 // XXX copy to a new typed array as a workaround for chrome bug 169705
+ });
+ },
+
emscripten_worker_respond: function(data, size) {
if (!inWorkerCall) throw 'not in worker call!';
- if (workerResponded) throw 'already responded!';
+ if (workerResponded) throw 'already responded with final response!';
workerResponded = true;
postMessage({
'callbackId': workerCallbackId,
+ 'finalResponse': true,
'data': data ? new Uint8Array({{{ makeHEAPView('U8', 'data', 'data + size') }}}) : 0 // XXX copy to a new typed array as a workaround for chrome bug 169705
});
},