174 lines
4.1 KiB
JavaScript
174 lines
4.1 KiB
JavaScript
const EVENT_UP = 0;
|
|
const EVENT_DOWN = 1;
|
|
const EVENT_SELECT = 2;
|
|
const EVENT_BACK = 3;
|
|
|
|
const canvas = document.getElementById('display');
|
|
const context = canvas.getContext('2d');
|
|
const status = document.getElementById('status');
|
|
const buttons = Array.from(document.querySelectorAll('[data-event]'));
|
|
|
|
let wasm;
|
|
let lastPointerTime = -Infinity;
|
|
|
|
async function loadWasm() {
|
|
const imports = {};
|
|
const response = fetch('./pkg/kinda.wasm');
|
|
|
|
if ('instantiateStreaming' in WebAssembly) {
|
|
try {
|
|
return await WebAssembly.instantiateStreaming(response, imports);
|
|
} catch (error) {
|
|
console.warn('Falling back to ArrayBuffer WebAssembly load', error);
|
|
}
|
|
}
|
|
|
|
const bytes = await (await fetch('./pkg/kinda.wasm')).arrayBuffer();
|
|
return WebAssembly.instantiate(bytes, imports);
|
|
}
|
|
|
|
function render() {
|
|
const width = wasm.kinda_sim_width();
|
|
const height = wasm.kinda_sim_height();
|
|
const pixelPointer = wasm.kinda_sim_pixels();
|
|
const pixelLength = wasm.kinda_sim_pixel_len();
|
|
const pixels = new Uint8Array(wasm.memory.buffer, pixelPointer, pixelLength);
|
|
|
|
canvas.width = width;
|
|
canvas.height = height;
|
|
|
|
const image = context.createImageData(width, height);
|
|
for (let source = 0, target = 0; source < pixels.length; source += 1, target += 4) {
|
|
const value = Math.round(18 + (pixels[source] * 230) / 255);
|
|
image.data[target] = value;
|
|
image.data[target + 1] = value;
|
|
image.data[target + 2] = value;
|
|
image.data[target + 3] = 255;
|
|
}
|
|
|
|
context.putImageData(image, 0, 0);
|
|
}
|
|
|
|
function sendInput(eventCode) {
|
|
if (!wasm) {
|
|
return;
|
|
}
|
|
|
|
wasm.kinda_sim_input(eventCode);
|
|
render();
|
|
}
|
|
|
|
function sendTouch(clientX, clientY) {
|
|
if (!wasm) {
|
|
return;
|
|
}
|
|
|
|
const bounds = canvas.getBoundingClientRect();
|
|
const x = Math.max(
|
|
0,
|
|
Math.min(canvas.width - 1, Math.floor(((clientX - bounds.left) * canvas.width) / bounds.width))
|
|
);
|
|
const y = Math.max(
|
|
0,
|
|
Math.min(canvas.height - 1, Math.floor(((clientY - bounds.top) * canvas.height) / bounds.height))
|
|
);
|
|
|
|
wasm.kinda_sim_touch(x, y);
|
|
render();
|
|
}
|
|
|
|
function capturePointer(event) {
|
|
if (!canvas.setPointerCapture || event.pointerId === undefined) {
|
|
return;
|
|
}
|
|
|
|
try {
|
|
canvas.setPointerCapture(event.pointerId);
|
|
} catch (_) {
|
|
// Synthetic and some browser-generated events do not have an active pointer.
|
|
}
|
|
}
|
|
|
|
function handlePointerDown(event) {
|
|
event.preventDefault();
|
|
lastPointerTime = event.timeStamp;
|
|
capturePointer(event);
|
|
sendTouch(event.clientX, event.clientY);
|
|
}
|
|
|
|
function handleMouseDown(event) {
|
|
if (event.timeStamp - lastPointerTime < 50) {
|
|
return;
|
|
}
|
|
|
|
event.preventDefault();
|
|
sendTouch(event.clientX, event.clientY);
|
|
}
|
|
|
|
function handleTouchStart(event) {
|
|
if (event.timeStamp - lastPointerTime < 50) {
|
|
return;
|
|
}
|
|
|
|
const touch = event.changedTouches[0];
|
|
if (!touch) {
|
|
return;
|
|
}
|
|
|
|
event.preventDefault();
|
|
sendTouch(touch.clientX, touch.clientY);
|
|
}
|
|
|
|
function keyboardEventCode(event) {
|
|
if (event.key === 'ArrowUp') {
|
|
return EVENT_UP;
|
|
}
|
|
|
|
if (event.key === 'ArrowDown') {
|
|
return EVENT_DOWN;
|
|
}
|
|
|
|
if (event.key === 'Enter') {
|
|
return EVENT_SELECT;
|
|
}
|
|
|
|
if (event.key === 'Escape' || event.key === 'Backspace') {
|
|
return EVENT_BACK;
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
buttons.forEach((button) => {
|
|
button.disabled = true;
|
|
button.addEventListener('click', () => sendInput(Number(button.dataset.event)));
|
|
});
|
|
|
|
canvas.addEventListener('pointerdown', handlePointerDown);
|
|
canvas.addEventListener('mousedown', handleMouseDown);
|
|
canvas.addEventListener('touchstart', handleTouchStart, { passive: false });
|
|
|
|
window.addEventListener('keydown', (event) => {
|
|
const eventCode = keyboardEventCode(event);
|
|
if (eventCode === null) {
|
|
return;
|
|
}
|
|
|
|
event.preventDefault();
|
|
sendInput(eventCode);
|
|
});
|
|
|
|
try {
|
|
const instance = await loadWasm();
|
|
wasm = instance.instance.exports;
|
|
wasm.kinda_sim_init();
|
|
render();
|
|
buttons.forEach((button) => {
|
|
button.disabled = false;
|
|
});
|
|
status.textContent = 'Ready. Press buttons or click/touch the display.';
|
|
} catch (error) {
|
|
console.error(error);
|
|
status.textContent = error instanceof Error ? error.message : 'Could not load simulator.';
|
|
}
|