diff options
Diffstat (limited to 'src/library_browser.js')
-rw-r--r-- | src/library_browser.js | 231 |
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 }); }, |