Revision e6ccdf76a35f72c6800a9a19c59230b43e94c844 authored by Sam Clegg on 01 April 2023, 00:47:01 UTC, committed by Sam Clegg on 03 April 2023, 18:25:45 UTC
Followup to #12704.
1 parent a90c5ab
proxyClient.js
/**
* @license
* Copyright 2013 The Emscripten Authors
* SPDX-License-Identifier: MIT
*/
// proxy to/from worker
if (typeof Module == 'undefined') {
console.warn('no Module object defined - cannot proxy canvas rendering and input events, etc.');
Module = {
canvas: {
addEventListener: function() {},
getBoundingClientRect: function() { return { bottom: 0, height: 0, left: 0, right: 0, top: 0, width: 0 } },
},
};
}
if (!Module.hasOwnProperty('print')) {
Module['print'] = function(x) {
console.log(x);
};
}
if (!Module.hasOwnProperty('printErr')) {
Module['printErr'] = function(x) {
console.error(x);
};
}
// utils
function FPSTracker(text) {
var last = 0;
var mean = 0;
var counter = 0;
this.tick = function() {
var now = Date.now();
if (last > 0) {
var diff = now - last;
mean = 0.99*mean + 0.01*diff;
if (counter++ === 60) {
counter = 0;
dump(text + ' fps: ' + (1000/mean).toFixed(2) + '\n');
}
}
last = now;
};
}
/*
function GenericTracker(text) {
var mean = 0;
var counter = 0;
this.tick = function(value) {
mean = 0.99*mean + 0.01*value;
if (counter++ === 60) {
counter = 0;
dump(text + ': ' + (mean).toFixed(2) + '\n');
}
};
}
*/
// render
var renderFrameData = null;
function renderFrame() {
var dst = Module.canvasData.data;
if (dst.set) {
dst.set(renderFrameData);
} else {
for (var i = 0; i < renderFrameData.length; i++) {
dst[i] = renderFrameData[i];
}
}
Module.ctx.putImageData(Module.canvasData, 0, 0);
renderFrameData = null;
}
window.requestAnimationFrame = window.requestAnimationFrame || window.mozRequestAnimationFrame ||
window.webkitRequestAnimationFrame || window.msRequestAnimationFrame ||
renderFrame;
/*
(function() {
var trueRAF = window.requestAnimationFrame;
var tracker = new FPSTracker('client');
window.requestAnimationFrame = function(func) {
trueRAF(function() {
tracker.tick();
func();
});
}
})();
*/
// end render
// IDBStore
var IDBStore = {{{ IDBStore.js }}};
// Frame throttling
var frameId = 0;
// Temporarily handling this at run-time pending Python preprocessor support
var SUPPORT_BASE64_EMBEDDING;
// Worker
var filename;
if (!filename) {
filename = '{{{ filename }}}';
}
var workerURL = filename;
if (SUPPORT_BASE64_EMBEDDING) {
var fileBytes = tryParseAsDataURI(filename);
if (fileBytes) {
workerURL = URL.createObjectURL(new Blob([fileBytes], {type: 'application/javascript'}));
}
}
var worker = new Worker(workerURL);
WebGLClient.prefetch();
setTimeout(function() {
worker.postMessage({
target: 'worker-init',
width: Module.canvas.width,
height: Module.canvas.height,
boundingClientRect: cloneObject(Module.canvas.getBoundingClientRect()),
URL: document.URL,
currentScriptUrl: filename,
preMain: true });
}, 0); // delay til next frame, to make sure html is ready
var workerResponded = false;
worker.onmessage = function worker_onmessage(event) {
//dump('\nclient got ' + JSON.stringify(event.data).substr(0, 150) + '\n');
if (!workerResponded) {
workerResponded = true;
if (Module.setStatus) Module.setStatus('');
if (SUPPORT_BASE64_EMBEDDING && workerURL !== filename) URL.revokeObjectURL(workerURL);
}
var data = event.data;
switch (data.target) {
case 'stdout': {
Module['print'](data.content);
break;
}
case 'stderr': {
Module['printErr'](data.content);
break;
}
case 'window': {
window[data.method]();
break;
}
case 'canvas': {
switch (data.op) {
case 'getContext': {
Module.ctx = Module.canvas.getContext(data.type, data.attributes);
if (data.type !== '2d') {
// possible GL_DEBUG entry point: Module.ctx = wrapDebugGL(Module.ctx);
Module.glClient = new WebGLClient();
}
break;
}
case 'resize': {
Module.canvas.width = data.width;
Module.canvas.height = data.height;
if (Module.ctx && Module.ctx.getImageData) Module.canvasData = Module.ctx.getImageData(0, 0, data.width, data.height);
worker.postMessage({ target: 'canvas', boundingClientRect: cloneObject(Module.canvas.getBoundingClientRect()) });
break;
}
case 'render': {
if (renderFrameData) {
// previous image was not rendered yet, just update image
renderFrameData = data.image.data;
} else {
// previous image was rendered so update image and request another frame
renderFrameData = data.image.data;
window.requestAnimationFrame(renderFrame);
}
break;
}
case 'setObjectProperty': {
Module.canvas[data.object][data.property] = data.value;
break;
}
default: throw 'eh?';
}
break;
}
case 'gl': {
Module.glClient.onmessage(data);
break;
}
case 'tick': {
frameId = data.id;
worker.postMessage({ target: 'tock', id: frameId });
break;
}
case 'Image': {
assert(data.method === 'src');
var img = new Image();
img.onload = () => {
assert(img.complete);
var canvas = document.createElement('canvas');
canvas.width = img.width;
canvas.height = img.height;
var ctx = canvas.getContext('2d');
ctx.drawImage(img, 0, 0);
var imageData = ctx.getImageData(0, 0, img.width, img.height);
worker.postMessage({ target: 'Image', method: 'onload', id: data.id, width: img.width, height: img.height, data: imageData.data, preMain: true });
};
img.onerror = () => {
worker.postMessage({ target: 'Image', method: 'onerror', id: data.id, preMain: true });
};
img.src = data.src;
break;
}
case 'IDBStore': {
switch (data.method) {
case 'loadBlob': {
IDBStore.getFile(data.db, data.id, function(error, blob) {
worker.postMessage({
target: 'IDBStore',
method: 'response',
blob: error ? null : blob
});
});
break;
}
case 'storeBlob': {
IDBStore.setFile(data.db, data.id, data.blob, function(error) {
worker.postMessage({
target: 'IDBStore',
method: 'response',
error: !!error
});
});
break;
}
}
break;
}
case 'custom': {
if (Module['onCustomMessage']) {
Module['onCustomMessage'](event);
} else {
throw 'Custom message received but client Module.onCustomMessage not implemented.';
}
break;
}
case 'setimmediate': {
worker.postMessage({target: 'setimmediate'});
break;
}
default: throw 'what? ' + data.target;
}
};
function postCustomMessage(data, options = {}) {
worker.postMessage({ target: 'custom', userData: data, preMain: options.preMain });
}
function cloneObject(event) {
var ret = {};
for (var x in event) {
if (x == x.toUpperCase()) continue;
var prop = event[x];
if (typeof prop == 'number' || typeof prop == 'string') ret[x] = prop;
}
return ret;
};
// Only prevent default on backspace/tab because we don't want unexpected navigation.
// Do not prevent default on the rest as we need the keypress event.
function shouldPreventDefault(event) {
if (event.type === 'keydown' && event.keyCode !== 8 /* backspace */ && event.keyCode !== 9 /* tab */) {
return false; // keypress, back navigation
} else {
return true; // NO keypress, NO back navigation
}
};
['keydown', 'keyup', 'keypress', 'blur', 'visibilitychange'].forEach(function(event) {
document.addEventListener(event, function(event) {
worker.postMessage({ target: 'document', event: cloneObject(event) });
if (shouldPreventDefault(event)) {
event.preventDefault();
}
});
});
['unload'].forEach(function(event) {
window.addEventListener(event, function(event) {
worker.postMessage({ target: 'window', event: cloneObject(event) });
});
});
['mousedown', 'mouseup', 'mousemove', 'DOMMouseScroll', 'mousewheel', 'mouseout'].forEach(function(event) {
Module.canvas.addEventListener(event, function(event) {
worker.postMessage({ target: 'canvas', event: cloneObject(event) });
event.preventDefault();
}, true);
});
![swh spinner](/static/img/swh-spinner.gif)
Computing file changes ...