242 lines
7.9 KiB
HTML
242 lines
7.9 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="en">
|
|
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>Document</title>
|
|
</head>
|
|
|
|
<body>
|
|
<canvas class="fill-screen" id="canvas"></canvas>
|
|
</body>
|
|
<style>
|
|
@font-face {
|
|
font-family: main_font;
|
|
src: url(FiraCode-Regular.ttf);
|
|
}
|
|
* {
|
|
margin: 0;
|
|
padding: 0;
|
|
overflow: hidden;
|
|
}
|
|
.fill-screen {
|
|
position: absolute;
|
|
top: 0;
|
|
left: 0;
|
|
width: 100%;
|
|
height: 100%;
|
|
}
|
|
</style>
|
|
|
|
</html>
|
|
<script>
|
|
|
|
class stream_t {
|
|
constructor(dataview, ptr, len) {
|
|
this.dv = dataview;
|
|
this.ptr = ptr;
|
|
this.len = len;
|
|
this.i = 0;
|
|
}
|
|
|
|
write_u32(value) {
|
|
if (this.i + 4 <= this.len) {
|
|
this.dv.setUint32(this.ptr + this.i, value, true);
|
|
this.i += 4;
|
|
} else {
|
|
console.log("reached end of buffer while stream writing");
|
|
}
|
|
}
|
|
|
|
write_u16(value) {
|
|
if (this.i + 2 <= this.len) {
|
|
this.dv.setUint16(this.ptr + this.i, value, true);
|
|
this.i += 2;
|
|
} else {
|
|
console.log("reached end of buffer while stream writing");
|
|
}
|
|
}
|
|
}
|
|
|
|
class memory_t {
|
|
exports = null; // this is set after wasm module created
|
|
constructor(wasm_memory) {
|
|
this.mem = wasm_memory;
|
|
this.u8 = new Uint8Array(this.mem.buffer);
|
|
this.data_view = new DataView(this.mem.buffer);
|
|
this.utf8decoder = new TextDecoder("utf-8");
|
|
this.utf8encoder = new TextEncoder("utf-8");
|
|
}
|
|
|
|
read_cstr(str, len) {
|
|
const arr = this.u8.subarray(str, str+len);
|
|
const text = this.utf8decoder.decode(arr);
|
|
return text;
|
|
}
|
|
|
|
write_string_into_cmemory(ptr, ptr_len, string) {
|
|
const bytes = this.utf8encoder.encode(string);
|
|
let i = 0;
|
|
for (; i < bytes.length && i < (ptr_len-1); i += 1) {
|
|
this.u8[ptr + i] = bytes[i];
|
|
}
|
|
this.u8[ptr + i] = 0;
|
|
}
|
|
|
|
write_string_to_cglobal(temp, string) {
|
|
const ptr = this.exports[temp].value;
|
|
const len = this.data_view.getInt32(this.exports[temp + "_len"].value, true);
|
|
this.write_string_into_cmemory(ptr, len, string);
|
|
return ptr;
|
|
}
|
|
|
|
stream_write(ptr, len) {
|
|
return new stream_t(this.data_view, ptr, len);
|
|
}
|
|
}
|
|
|
|
const canvas = document.getElementById("canvas");
|
|
const ctx2d = canvas.getContext('2d');
|
|
|
|
(async function main() {
|
|
if (!ctx2d) {
|
|
alert('Outdated browser, cant draw :(');
|
|
return;
|
|
}
|
|
|
|
const mem = new memory_t(new WebAssembly['Memory']({ initial: 2000, maximum: 65536 }));
|
|
const request = await fetch('main.wasm');
|
|
const binary = await request.arrayBuffer();
|
|
|
|
const wasm_os_imports = {
|
|
wasm_local_time_now: (ptr, len) => {
|
|
const date = new Date();
|
|
const stream = mem.stream_write(ptr, len);
|
|
stream.write_u16(date.getMilliseconds());
|
|
stream.write_u16(date.getSeconds());
|
|
stream.write_u16(date.getMinutes());
|
|
stream.write_u16(date.getHours());
|
|
stream.write_u16(date.getDate());
|
|
stream.write_u16(date.getMonth());
|
|
stream.write_u16(date.getFullYear());
|
|
},
|
|
|
|
wasm_universal_time_now: (ptr, len) => {
|
|
const date = new Date();
|
|
const stream = mem.stream_write(ptr, len);
|
|
stream.write_u16(date.getUTCMilliseconds());
|
|
stream.write_u16(date.getUTCSeconds());
|
|
stream.write_u16(date.getUTCMinutes());
|
|
stream.write_u16(date.getUTCHours());
|
|
stream.write_u16(date.getUTCDate());
|
|
stream.write_u16(date.getUTCMonth());
|
|
stream.write_u16(date.getUTCFullYear());
|
|
},
|
|
|
|
wasm_milliseconds_now: () => { return performance.now(); },
|
|
};
|
|
|
|
let wasm_app_imports = {
|
|
// core
|
|
memory: mem.mem,
|
|
wasm_parse_float: (str, len) => { return parseFloat(mem.read_cstr(str, len)); },
|
|
wasm_alert: (str, len) => { alert(mem.read_cstr(str,len)); },
|
|
wasm_trap: () => { throw new Error(); },
|
|
wasm_write_to_console: (str, len) => { console.log(mem.read_cstr(str, len)); },
|
|
|
|
// gfx
|
|
wasm_draw_text: (str, len, x, y, font_str, font_len, font_size, r, g, b, a) => {
|
|
ctx2d.font = `${font_size}px ${mem.read_cstr(font_str, font_len)}`;
|
|
ctx2d.fillStyle = `rgba(${r}, ${g}, ${b}, ${a})`;
|
|
ctx2d.textBaseline = "hanging";
|
|
ctx2d.fillText(mem.read_cstr(str, len), x, y);
|
|
},
|
|
wasm_measure_text: (str, len, font_str, font_len, font_size) => {
|
|
ctx2d.font = `${font_size}px ${mem.read_cstr(font_str, font_len)}`;
|
|
ctx2d.textBaseline = "hanging";
|
|
const metrics = ctx2d.measureText(mem.read_cstr(str, len));
|
|
return metrics.width;
|
|
},
|
|
wasm_get_font_height: (font_str, font_len, font_size) => {
|
|
ctx2d.font = `${font_size}px ${mem.read_cstr(font_str, font_len)}`;
|
|
ctx2d.textBaseline = "hanging";
|
|
return ctx2d.measureText('NothinBelowTheBaseline').actualBoundingBoxDescent;
|
|
},
|
|
wasm_draw_rect: (x, y, w, h, r, g, b, a) => {
|
|
ctx2d.beginPath();
|
|
ctx2d.rect(x, y, w, h);
|
|
ctx2d.fillStyle = `rgba(${r}, ${g}, ${b}, ${a})`;
|
|
ctx2d.fill();
|
|
},
|
|
wasm_clear: () => {
|
|
ctx2d.clearRect(0, 0, canvas.width, canvas.height);
|
|
},
|
|
wasm_set_clip: (x, y, w, h) => {
|
|
ctx2d.restore();
|
|
ctx2d.save();
|
|
ctx2d.beginPath();
|
|
ctx2d.rect(x, y, w, h);
|
|
ctx2d.clip();
|
|
},
|
|
|
|
};
|
|
|
|
Object.assign(wasm_app_imports, wasm_os_imports);
|
|
|
|
const program = await WebAssembly['instantiate'](binary, { "env": wasm_app_imports });
|
|
const instance = program['instance'];
|
|
const wasm_exports = instance['exports'];
|
|
mem.exports = wasm_exports;
|
|
wasm_exports['wasm_init'](window.devicePixelRatio);
|
|
|
|
let awake = true;
|
|
|
|
function wasm_update(time) {
|
|
const dpr = window.devicePixelRatio;
|
|
canvas.width = canvas.getBoundingClientRect().width * dpr;
|
|
canvas.height = canvas.getBoundingClientRect().height * dpr;
|
|
let animating = wasm_exports['wasm_update'](time, canvas.width, canvas.height, dpr);
|
|
if (animating) {
|
|
requestAnimationFrame(wasm_update);
|
|
} else {
|
|
awake = false;
|
|
}
|
|
}
|
|
|
|
function wake_up() {
|
|
if (awake) return;
|
|
awake = true;
|
|
window.requestAnimationFrame(wasm_update);
|
|
}
|
|
|
|
addEventListener("resize", (event) => { wake_up(); });
|
|
addEventListener("keydown", (event) => {
|
|
wasm_exports["wasm_key_down"](mem.write_string_to_cglobal("wasm_temp_buff1", event.key), event.ctrlKey, event.shiftKey, event.altKey, event.metaKey);
|
|
wake_up();
|
|
});
|
|
addEventListener("keyup", (event) => {
|
|
wasm_exports["wasm_key_up"](mem.write_string_to_cglobal("wasm_temp_buff1", event.key), event.ctrlKey, event.shiftKey, event.altKey, event.metaKey);
|
|
wake_up();
|
|
});
|
|
addEventListener("mousemove", (event) => {
|
|
wasm_exports["wasm_mouse_move"](event.clientX, event.clientY, event.ctrlKey, event.shiftKey, event.altKey, event.metaKey);
|
|
wake_up();
|
|
});
|
|
addEventListener("mousedown", (event) => {
|
|
wasm_exports["wasm_mouse_down"](event.clientX, event.clientY, event.button, event.ctrlKey, event.shiftKey, event.altKey, event.metaKey);
|
|
wake_up();
|
|
});
|
|
addEventListener("mouseup", (event) => {
|
|
wasm_exports["wasm_mouse_up"](event.clientX, event.clientY, event.button, event.ctrlKey, event.shiftKey, event.altKey, event.metaKey);
|
|
wake_up();
|
|
});
|
|
addEventListener("wheel", (event) => {
|
|
wasm_exports["wasm_mouse_wheel"](event.clientX, event.clientY, event.deltaX, event.deltaY, event.deltaZ, event.ctrlKey, event.shiftKey, event.altKey, event.metaKey);
|
|
wake_up();
|
|
});
|
|
|
|
requestAnimationFrame(wasm_update);
|
|
})()
|
|
</script> |