diff options
Diffstat (limited to 'src/library_browser.js')
-rw-r--r-- | src/library_browser.js | 343 |
1 files changed, 251 insertions, 92 deletions
diff --git a/src/library_browser.js b/src/library_browser.js index 17dff2eb..075bd8bf 100644 --- a/src/library_browser.js +++ b/src/library_browser.js @@ -3,24 +3,30 @@ // Utilities for browser environments mergeInto(LibraryManager.library, { - emscripten_set_main_loop: function(func, fps) { - fps = fps || 60; // TODO: use requestAnimationFrame - _emscripten_set_main_loop.cancel = false; - var jsFunc = FUNCTION_TABLE[func]; - function doOne() { - if (_emscripten_set_main_loop.cancel) return; - jsFunc(); - setTimeout(doOne, 1000/fps); // doing this each time means that on exception, we stop - } - setTimeout(doOne, 1000/fps); - }, - - emscripten_cancel_main_loop: function(func) { - _emscripten_set_main_loop.cancel = true; - }, - + $Browser__postset: 'Module["requestFullScreen"] = function() { Browser.requestFullScreen() };\n' + // exports + 'Module["requestAnimationFrame"] = function(func) { Browser.requestAnimationFrame(func) };\n' + + 'Module["pauseMainLoop"] = function() { Browser.mainLoop.pause() };\n' + + 'Module["resumeMainLoop"] = function() { Browser.mainLoop.resume() };\n', $Browser: { - createContext: function(canvas, useWebGL) { + mainLoop: { + scheduler: null, + shouldPause: false, + paused: false, + pause: function() { + Browser.mainLoop.shouldPause = true; + }, + resume: function() { + if (Browser.mainLoop.paused) { + Browser.mainLoop.paused = false; + Browser.mainLoop.scheduler(); + } + Browser.mainLoop.shouldPause = false; + }, + }, + pointerLock: false, + moduleContextCreatedCallbacks: [], + + createContext: function(canvas, useWebGL, setInModule) { #if !USE_TYPED_ARRAYS if (useWebGL) { Module.print('(USE_TYPED_ARRAYS needs to be enabled for WebGL)'); @@ -39,50 +45,19 @@ mergeInto(LibraryManager.library, { // Useful to debug native webgl apps: var Module = { printErr: function(x) { console.log(x) } }; var tempCtx = ctx; var wrapper = {}; - wrapper.objectMap = new WeakMap(); - wrapper.objectCounter = 1; for (var prop in tempCtx) { (function(prop) { switch (typeof tempCtx[prop]) { case 'function': { wrapper[prop] = function() { - var printArgs = Array.prototype.slice.call(arguments).map(function(arg) { - if (wrapper.objectMap[arg]) return '<' + arg + '|' + wrapper.objectMap[arg] + '>'; - if (arg.toString() == '[object HTMLImageElement]') { - return arg + '\n\n'; - } - if (arg.byteLength) { - var buf = new ArrayBuffer(32); - var i8buf = new Int8Array(buf); - var f32buf = new Float32Array(buf); - switch(arg.toString()) { - case '[object Uint8Array]': - i8buf.set(arg.subarray(0, 32)); - break; - case '[object Float32Array]': - f32buf.set(arg.subarray(0, 5)); - break; - default: - alert('unknown array for debugging: ' + arg); - throw 'see alert'; - } - var ret = '{' + arg.byteLength + ':\n'; - var arr = Array.prototype.slice.call(i8buf); - ret += 'i8:' + arr.toString().replace(/,/g, ',') + '\n'; - arr = Array.prototype.slice.call(f32buf, 0, 8); - ret += 'f32:' + arr.toString().replace(/,/g, ',') + '}'; - return ret; - } - return arg; - }); - Module.printErr('[gl_f:' + prop + ':' + printArgs + ']'); + 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); - var printRet = ret; - if (typeof ret == 'object') { - wrapper.objectMap[ret] = wrapper.objectCounter++; - printRet = '<' + ret + '|' + wrapper.objectMap[ret] + '>'; + if (GL.debug && typeof ret != 'undefined') { + Module.printErr('[ gl:' + prop + ':return:' + Runtime.prettyPrint(ret) + ']'); } - Module.printErr('[ gl:' + prop + ':return:' + printRet + ']'); return ret; } break; @@ -93,7 +68,9 @@ mergeInto(LibraryManager.library, { return tempCtx[prop]; }); wrapper.__defineSetter__(prop, function(value) { - Module.printErr('[gl_s:' + prop + ':' + value + ']'); + if (GL.debug) { + Module.printErr('[gl_s:' + prop + ':' + value + ']'); + } tempCtx[prop] = value; }); break; @@ -105,50 +82,232 @@ mergeInto(LibraryManager.library, { #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() }); } return ctx; }, - // Given binary data for an image, in a format like PNG or JPG, we convert it - // to flat pixel data. We do so using the browser's native code. - // This is deprecated, it is preferred to load binary files, createObjectURL, etc., - // see the sdl_* tests. - /*decodeImage: function(pixels, format) { - function encodeBase64(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]; - } + requestFullScreen: function() { + var canvas = Module.canvas; + function fullScreenChange() { + if (Module['onFullScreen']) Module['onFullScreen'](); + if (document['webkitFullScreenElement'] === canvas || + document['mozFullScreenElement'] === canvas || + document['fullScreenElement'] === canvas) { + canvas.requestPointerLock = canvas['requestPointerLock'] || + canvas['mozRequestPointerLock'] || + canvas['webkitRequestPointerLock']; + canvas.requestPointerLock(); } - if (leftbits == 2) { - ret += BASE[(leftchar&3) << 4]; - ret += PAD + PAD; - } else if (leftbits == 4) { - ret += BASE[(leftchar&0xf) << 2]; - ret += PAD; + } + + 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; + } + + 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(); + }, + + requestAnimationFrame: function(func) { + if (!window.requestAnimationFrame) { + window.requestAnimationFrame = window['requestAnimationFrame'] || + window['mozRequestAnimationFrame'] || + window['webkitRequestAnimationFrame'] || + window['msRequestAnimationFrame'] || + window['oRequestAnimationFrame'] || + window['setTimeout']; + } + window.requestAnimationFrame(func); + }, + + getMovementX: function(event) { + return event['movementX'] || + event['mozMovementX'] || + event['webkitMovementX'] || + 0; + }, + + getMovementY: function(event) { + return event['movementY'] || + event['mozMovementY'] || + event['webkitMovementY'] || + 0; + }, + + xhrLoad: function(url, onload, onerror) { + var xhr = new XMLHttpRequest(); + xhr.open('GET', url, true); + xhr.responseType = 'arraybuffer'; + xhr.onload = function() { + if (xhr.status == 200) { + onload(xhr.response); + } else { + onerror(); } - return ret; + }; + xhr.onerror = onerror; + xhr.send(null); + }, + + asyncLoad: function(url, callback) { + Browser.xhrLoad(url, function(arrayBuffer) { + assert(arrayBuffer, 'Loading data file "' + url + '" failed (no arrayBuffer).'); + callback(new Uint8Array(arrayBuffer)); + removeRunDependency(); + }, function(event) { + throw 'Loading data file "' + url + '" failed.'; + }); + addRunDependency(); + } + }, + + emscripten_async_wget: function(url, file, onload, onerror) { + url = Pointer_stringify(url); + + Browser.xhrLoad(url, function(response) { + var absolute = Pointer_stringify(file); + var index = absolute.lastIndexOf('/'); + FS.createDataFile( + absolute.substr(0, index), + absolute.substr(index +1), + new Uint8Array(response), + true, true); + + if (onload) { + FUNCTION_TABLE[onload](file); + } + }, function(event) { + if (onerror) { + FUNCTION_TABLE[onerror](file); } - var image = new Image(); - image.src = 'data:image/' + format + ';base64,' + encodeBase64(pixels); - assert(image.complete, 'Image could not be decoded'); // page reload might fix it, decoding is async... need .onload handler... - var canvas = document.createElement('canvas'); - canvas.width = image.width; - canvas.height = image.height; - var ctx = canvas.getContext('2d'); - ctx.drawImage(image, 0, 0); - var imageData = ctx.getImageData(0, 0, canvas.width, canvas.height); - return imageData; - },*/ + }); + }, + + 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 + setTimeout(function() { + _emscripten_run_script(script); + }, millis); + }, + + emscripten_set_main_loop: function(func, fps) { + Module['noExitRuntime'] = true; + + var jsFunc = FUNCTION_TABLE[func]; + var wrapper = function() { + if (Browser.mainLoop.shouldPause) { + // catch pauses from non-main loop sources + Browser.mainLoop.paused = true; + Browser.mainLoop.shouldPause = false; + return; + } + jsFunc(); + 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(wrapper, 1000/fps); // doing this each time means that on exception, we stop + } + } else { + Browser.mainLoop.scheduler = function() { + Browser.requestAnimationFrame(wrapper); + } + } + Browser.mainLoop.scheduler(); + }, + + emscripten_cancel_main_loop: function(func) { + Browser.mainLoop.scheduler = null; + Browser.mainLoop.shouldPause = true; + }, + + emscripten_pause_main_loop: function(func) { + Browser.mainLoop.pause(); + }, + + emscripten_resume_main_loop: function(func) { + Browser.mainLoop.resume(); + }, + + emscripten_async_call: function(func, millis) { + Module['noExitRuntime'] = true; + + var asyncCall = Runtime.getFuncWrapper(func); + if (millis >= 0) { + setTimeout(asyncCall, millis); + } else { + Browser.requestAnimationFrame(asyncCall); + } + }, + + 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) { + Module['canvas'].width = width; + Module['canvas'].height = height; + }, + + emscripten_get_now: function() { + if (window['performance'] && window['performance']['now']) { + return window['performance']['now'](); + } else { + return Date.now(); + } } }); +/* 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; + } +} + +*/ + |