acrylic-web 0.1.21

acrylic apps as webapps
Documentation
<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8" />
		<meta name="viewport" content="width=device-width, initial-scale=1.0" />
		<link rel="icon" href="https://emojipedia-us.s3.amazonaws.com/source/skype/289/woman-cartwheeling_1f938-200d-2640-fe0f.png" type="image/x-icon">
		<title>[acrylic demo]</title>
		<style>
html, body {
	display: flex;
	width: 100%;
	height: 100%;
	margin: 0;
	padding: 0;
	border: 0;
	background-color: #050505;
	overflow: hidden;
}
		</style>
	</head>
	<body onload="onLoad();">
		<canvas></canvas>
		<script>
let canvas = document.querySelector('canvas');
let ctx = canvas.getContext('2d');
let wasm;
let app;
let imageData;
let w, h;
let addr, slice;
let pendingRequest = null;
let urlPrefix = null;
let timeout = null;
let previousFrame = performance.now();
const targetFPS = 10;
const targetFrameTime = parseInt(1000 / targetFPS);
let lastFrameTime;

function raw_log(s, l) {
	let mem = wasm.exports.memory;
	let slice = new Uint8Array(mem.buffer, s, l);
	let str = String.fromCharCode.apply(null, slice);
	console.log(str);
}

function raw_is_request_pending() {
	return pendingRequest === null ? 0 : 1;
}

function onRequestLoad(event) {
	clearTimeout(timeout);
	let len = pendingRequest.response.byteLength;
	let addr = wasm.exports.alloc_response_bytes(len);
	let mem = wasm.exports.memory;
	let dst = new Uint8Array(mem.buffer, addr, len);
	let src = new Uint8Array(pendingRequest.response);
	for (let i = 0; i < len; i++) dst[i] = src[i];
	wasm.exports.process_response(app);
	wasm.exports.drop_response_bytes();
	pendingRequest = null;
	needsSliceRefresh = true;
	// todo: check for next requests
	frame();
}

function raw_set_request_url_prefix(s, l) {
	let mem = wasm.exports.memory;
	let slice = new Uint8Array(mem.buffer, s, l);
	urlPrefix = String.fromCharCode.apply(null, slice);
}

function raw_set_request_url(s, l) {
	let mem = wasm.exports.memory;
	let slice = new Uint8Array(mem.buffer, s, l);
	let url = String.fromCharCode.apply(null, slice);

	pendingRequest = new XMLHttpRequest();
	pendingRequest.responseType = "arraybuffer";
	pendingRequest.addEventListener("load", onRequestLoad);
	// todo: call discard_request on error
	pendingRequest.open("GET", urlPrefix + url);
	pendingRequest.send();
}

function frame() {
	if (w != window.innerWidth || h != window.innerHeight) {
		w = window.innerWidth;
		h = window.innerHeight;
		addr = wasm.exports.set_output_size(app, w, h);
		canvas.width = w;
		canvas.height = h;
		needsSliceRefresh = true;
	}
	if (needsSliceRefresh) {
		let mem = wasm.exports.memory;
		slice = new Uint8ClampedArray(mem.buffer, addr, w * h * 4);
		needsSliceRefresh = false;
	}

	wasm.exports.frame(app);
	ctx.putImageData(new ImageData(slice, w, h), 0, 0);

	let now = performance.now();
	lastFrameTime = now - previousFrame;
	previousFrame = now;
	let delay = (lastFrameTime > targetFrameTime) ? 0 : (targetFrameTime - lastFrameTime);
	timeout = setTimeout(frame, delay);
}

const env = {
	raw_log,
	raw_is_request_pending,
	raw_set_request_url_prefix,
	raw_set_request_url,
};

function startWasm(file, debug) {
	let path = 'target/wasm32-unknown-unknown/';
	fetch( path + (debug ? 'debug/' : 'release/') + file + '.wasm').then(response => {
		response.arrayBuffer().then(buffer => {
			WebAssembly.compile(buffer).then(module => {
				WebAssembly.instantiate(module, { env }).then(mod => {
					wasm = mod;
					app = wasm.exports.init();
					frame();
				});
			});
		});
	});
}

function findWasm(debug) {
	fetch('Cargo.toml').then(response => {
		response.text().then(text => {
			text.split('\n').map(line => {
				let tokens = line.split('"');
				if (tokens[0] == 'name = ') {
					let file = tokens[1].replaceAll('-', '_');
					startWasm(file, debug);
				}
			});
		});
	});
}

function onLoad() {
	let hash = document.location.hash;
	let debug;
	if (hash == '#debug') {
		debug = true;
	} else if (hash == '#release') {
		debug = false;
	} else {
		if (confirm('Use release build?')) {
			document.location = '#release';
			debug = false;
		} else if (confirm('Use debug build?')) {
			document.location = '#debug';
			debug = true;
		} else {
			document.location = '';
		}
	}
	findWasm(debug);
}
		</script>
	</body>
</html>