|
|
@@ -0,0 +1,1241 @@
|
|
|
+// This Source Code Form is subject to the terms of the MIT License.
|
|
|
+// If a copy of the MIT License was not distributed with this file,
|
|
|
+// You can obtain one at https://spdx.org/licenses/MIT.html.
|
|
|
+
|
|
|
+
|
|
|
+// Worker State
|
|
|
+
|
|
|
+const MTY = {
|
|
|
+ keys: {},
|
|
|
+ keysRev: {},
|
|
|
+ glIndex: 0,
|
|
|
+ glObj: {},
|
|
|
+ fds: {},
|
|
|
+ fdIndex: 0,
|
|
|
+};
|
|
|
+
|
|
|
+
|
|
|
+// Allocation
|
|
|
+
|
|
|
+function mty_cfunc(ptr) {
|
|
|
+ return MTY.exports.__indirect_function_table.get(ptr);
|
|
|
+}
|
|
|
+
|
|
|
+function mty_alloc(size, el) {
|
|
|
+ return MTY.exports.mty_system_alloc(size, el ? el : 1);
|
|
|
+}
|
|
|
+
|
|
|
+function mty_free(ptr) {
|
|
|
+ MTY.exports.mty_system_free(ptr);
|
|
|
+}
|
|
|
+
|
|
|
+function mty_dup_c(buf) {
|
|
|
+ const ptr = mty_alloc(buf.byteLength + 1);
|
|
|
+ mty_memcpy(ptr, buf);
|
|
|
+
|
|
|
+ return ptr;
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+// window.localStorage
|
|
|
+
|
|
|
+function mty_get_ls(key) {
|
|
|
+ postMessage({
|
|
|
+ type: 'get-ls',
|
|
|
+ key: key,
|
|
|
+ sab: MTY.sab,
|
|
|
+ sync: MTY.sync,
|
|
|
+ });
|
|
|
+
|
|
|
+ mty_wait(MTY.sync);
|
|
|
+
|
|
|
+ const size = MTY.sab[0];
|
|
|
+ if (size == 0)
|
|
|
+ return 0;
|
|
|
+
|
|
|
+ const sab8 = new Uint8Array(new SharedArrayBuffer(size));
|
|
|
+
|
|
|
+ postMessage({
|
|
|
+ type: 'async-copy',
|
|
|
+ sab8: sab8,
|
|
|
+ sync: MTY.sync,
|
|
|
+ });
|
|
|
+
|
|
|
+ mty_wait(MTY.sync);
|
|
|
+
|
|
|
+ return sab8;
|
|
|
+}
|
|
|
+
|
|
|
+function mty_set_ls(key, val) {
|
|
|
+ postMessage({
|
|
|
+ type: 'set-ls',
|
|
|
+ key: key,
|
|
|
+ val: val,
|
|
|
+ sync: MTY.sync,
|
|
|
+ });
|
|
|
+
|
|
|
+ mty_wait(MTY.sync);
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+// <unistd.h> stubs
|
|
|
+
|
|
|
+const MTY_UNISTD_API = {
|
|
|
+ flock: function (fd, flags) {
|
|
|
+ return 0;
|
|
|
+ },
|
|
|
+};
|
|
|
+
|
|
|
+
|
|
|
+// GL
|
|
|
+
|
|
|
+function mty_gl_new(obj) {
|
|
|
+ MTY.glObj[MTY.glIndex] = obj;
|
|
|
+
|
|
|
+ return MTY.glIndex++;
|
|
|
+}
|
|
|
+
|
|
|
+function mty_gl_del(index) {
|
|
|
+ let obj = MTY.glObj[index];
|
|
|
+
|
|
|
+ delete MTY.glObj[index];
|
|
|
+
|
|
|
+ return obj;
|
|
|
+}
|
|
|
+
|
|
|
+function mty_gl_obj(index) {
|
|
|
+ return MTY.glObj[index];
|
|
|
+}
|
|
|
+
|
|
|
+const MTY_GL_API = {
|
|
|
+ glGenFramebuffers: function (n, ids) {
|
|
|
+ for (let x = 0; x < n; x++)
|
|
|
+ mty_set_uint32(ids + x * 4, mty_gl_new(MTY.gl.createFramebuffer()));
|
|
|
+ },
|
|
|
+ glDeleteFramebuffers: function (n, ids) {
|
|
|
+ for (let x = 0; x < n; x++)
|
|
|
+ MTY.gl.deleteFramebuffer(mty_gl_del(mty_get_uint32(ids + x * 4)));
|
|
|
+ },
|
|
|
+ glBindFramebuffer: function (target, fb) {
|
|
|
+ MTY.gl.bindFramebuffer(target, fb ? mty_gl_obj(fb) : null);
|
|
|
+ },
|
|
|
+ glFramebufferTexture2D: function (target, attachment, textarget, texture, level) {
|
|
|
+ MTY.gl.framebufferTexture2D(target, attachment, textarget, mty_gl_obj(texture), level);
|
|
|
+ },
|
|
|
+ glEnable: function (cap) {
|
|
|
+ MTY.gl.enable(cap);
|
|
|
+ },
|
|
|
+ glDisable: function (cap) {
|
|
|
+ MTY.gl.disable(cap);
|
|
|
+ },
|
|
|
+ glViewport: function (x, y, width, height) {
|
|
|
+ MTY.gl.viewport(x, y, width, height);
|
|
|
+ },
|
|
|
+ glBindTexture: function (target, texture) {
|
|
|
+ MTY.gl.bindTexture(target, texture ? mty_gl_obj(texture) : null);
|
|
|
+ },
|
|
|
+ glDeleteTextures: function (n, ids) {
|
|
|
+ for (let x = 0; x < n; x++)
|
|
|
+ MTY.gl.deleteTexture(mty_gl_del(mty_get_uint32(ids + x * 4)));
|
|
|
+ },
|
|
|
+ glTexParameteri: function (target, pname, param) {
|
|
|
+ MTY.gl.texParameteri(target, pname, param);
|
|
|
+ },
|
|
|
+ glGenTextures: function (n, ids) {
|
|
|
+ for (let x = 0; x < n; x++)
|
|
|
+ mty_set_uint32(ids + x * 4, mty_gl_new(MTY.gl.createTexture()));
|
|
|
+ },
|
|
|
+ glTexImage2D: function (target, level, internalformat, width, height, border, format, type, data) {
|
|
|
+ MTY.gl.texImage2D(target, level, internalformat, width, height, border, format, type,
|
|
|
+ new Uint8Array(MTY_MEMORY.buffer, data));
|
|
|
+ },
|
|
|
+ glTexSubImage2D: function (target, level, xoffset, yoffset, width, height, format, type, pixels) {
|
|
|
+ MTY.gl.texSubImage2D(target, level, xoffset, yoffset, width, height, format, type,
|
|
|
+ new Uint8Array(MTY_MEMORY.buffer, pixels));
|
|
|
+ },
|
|
|
+ glDrawElements: function (mode, count, type, indices) {
|
|
|
+ MTY.gl.drawElements(mode, count, type, indices);
|
|
|
+ },
|
|
|
+ glGetAttribLocation: function (program, c_name) {
|
|
|
+ return MTY.gl.getAttribLocation(mty_gl_obj(program), mty_str_to_js(c_name));
|
|
|
+ },
|
|
|
+ glShaderSource: function (shader, count, c_strings, c_len) {
|
|
|
+ let source = '';
|
|
|
+ for (let x = 0; x < count; x++)
|
|
|
+ source += mty_str_to_js(mty_get_uint32(c_strings + x * 4));
|
|
|
+
|
|
|
+ MTY.gl.shaderSource(mty_gl_obj(shader), source);
|
|
|
+ },
|
|
|
+ glBindBuffer: function (target, buffer) {
|
|
|
+ MTY.gl.bindBuffer(target, buffer ? mty_gl_obj(buffer) : null);
|
|
|
+ },
|
|
|
+ glVertexAttribPointer: function (index, size, type, normalized, stride, pointer) {
|
|
|
+ MTY.gl.vertexAttribPointer(index, size, type, normalized, stride, pointer);
|
|
|
+ },
|
|
|
+ glCreateProgram: function () {
|
|
|
+ return mty_gl_new(MTY.gl.createProgram());
|
|
|
+ },
|
|
|
+ glUniform1i: function (loc, v0) {
|
|
|
+ MTY.gl.uniform1i(mty_gl_obj(loc), v0);
|
|
|
+ },
|
|
|
+ glUniform1f: function (loc, v0) {
|
|
|
+ MTY.gl.uniform1f(mty_gl_obj(loc), v0);
|
|
|
+ },
|
|
|
+ glUniform4i: function (loc, v0, v1, v2, v3) {
|
|
|
+ MTY.gl.uniform4i(mty_gl_obj(loc), v0, v1, v2, v3);
|
|
|
+ },
|
|
|
+ glUniform4f: function (loc, v0, v1, v2, v3) {
|
|
|
+ MTY.gl.uniform4f(mty_gl_obj(loc), v0, v1, v2, v3);
|
|
|
+ },
|
|
|
+ glActiveTexture: function (texture) {
|
|
|
+ MTY.gl.activeTexture(texture);
|
|
|
+ },
|
|
|
+ glDeleteBuffers: function (n, ids) {
|
|
|
+ for (let x = 0; x < n; x++)
|
|
|
+ MTY.gl.deleteBuffer(mty_gl_del(mty_get_uint32(ids + x * 4)));
|
|
|
+ },
|
|
|
+ glEnableVertexAttribArray: function (index) {
|
|
|
+ MTY.gl.enableVertexAttribArray(index);
|
|
|
+ },
|
|
|
+ glBufferData: function (target, size, data, usage) {
|
|
|
+ MTY.gl.bufferData(target, new Uint8Array(MTY_MEMORY.buffer, data, size), usage);
|
|
|
+ },
|
|
|
+ glDeleteShader: function (shader) {
|
|
|
+ MTY.gl.deleteShader(mty_gl_del(shader));
|
|
|
+ },
|
|
|
+ glGenBuffers: function (n, ids) {
|
|
|
+ for (let x = 0; x < n; x++)
|
|
|
+ mty_set_uint32(ids + x * 4, mty_gl_new(MTY.gl.createBuffer()));
|
|
|
+ },
|
|
|
+ glCompileShader: function (shader) {
|
|
|
+ MTY.gl.compileShader(mty_gl_obj(shader));
|
|
|
+ },
|
|
|
+ glLinkProgram: function (program) {
|
|
|
+ MTY.gl.linkProgram(mty_gl_obj(program));
|
|
|
+ },
|
|
|
+ glGetUniformLocation: function (program, name) {
|
|
|
+ return mty_gl_new(MTY.gl.getUniformLocation(mty_gl_obj(program), mty_str_to_js(name)));
|
|
|
+ },
|
|
|
+ glCreateShader: function (type) {
|
|
|
+ return mty_gl_new(MTY.gl.createShader(type));
|
|
|
+ },
|
|
|
+ glAttachShader: function (program, shader) {
|
|
|
+ MTY.gl.attachShader(mty_gl_obj(program), mty_gl_obj(shader));
|
|
|
+ },
|
|
|
+ glUseProgram: function (program) {
|
|
|
+ MTY.gl.useProgram(program ? mty_gl_obj(program) : null);
|
|
|
+ },
|
|
|
+ glGetShaderiv: function (shader, pname, params) {
|
|
|
+ if (pname == 0x8B81) {
|
|
|
+ let ok = MTY.gl.getShaderParameter(mty_gl_obj(shader), MTY.gl.COMPILE_STATUS);
|
|
|
+ mty_set_uint32(params, ok);
|
|
|
+
|
|
|
+ if (!ok)
|
|
|
+ console.warn(MTY.gl.getShaderInfoLog(mty_gl_obj(shader)));
|
|
|
+
|
|
|
+ } else {
|
|
|
+ mty_set_uint32(params, 0);
|
|
|
+ }
|
|
|
+ },
|
|
|
+ glDetachShader: function (program, shader) {
|
|
|
+ MTY.gl.detachShader(mty_gl_obj(program), mty_gl_obj(shader));
|
|
|
+ },
|
|
|
+ glDeleteProgram: function (program) {
|
|
|
+ MTY.gl.deleteProgram(mty_gl_del(program));
|
|
|
+ },
|
|
|
+ glClear: function (mask) {
|
|
|
+ MTY.gl.clear(mask);
|
|
|
+ },
|
|
|
+ glClearColor: function (red, green, blue, alpha) {
|
|
|
+ MTY.gl.clearColor(red, green, blue, alpha);
|
|
|
+ },
|
|
|
+ glGetError: function () {
|
|
|
+ return MTY.gl.getError();
|
|
|
+ },
|
|
|
+ glGetShaderInfoLog: function (shader, maxLength, length, infoLog) {
|
|
|
+ const log = gl.getShaderInfoLog(mty_gl_obj(shader));
|
|
|
+ const buf = mty_encode(log);
|
|
|
+
|
|
|
+ if (buf.length < maxLength) {
|
|
|
+ mty_set_uint32(length);
|
|
|
+ mty_strcpy(infoLog, buf);
|
|
|
+ }
|
|
|
+ },
|
|
|
+ glFinish: function () {
|
|
|
+ MTY.gl.finish();
|
|
|
+ },
|
|
|
+ glScissor: function (x, y, width, height) {
|
|
|
+ MTY.gl.scissor(x, y, width, height);
|
|
|
+ },
|
|
|
+ glBlendFunc: function (sfactor, dfactor) {
|
|
|
+ MTY.gl.blendFunc(sfactor, dfactor);
|
|
|
+ },
|
|
|
+ glBlendEquation: function (mode) {
|
|
|
+ MTY.gl.blendEquation(mode);
|
|
|
+ },
|
|
|
+ glUniformMatrix4fv: function (loc, count, transpose, value) {
|
|
|
+ MTY.gl.uniformMatrix4fv(mty_gl_obj(loc), transpose,
|
|
|
+ new Float32Array(MTY_MEMORY.buffer, value, 4 * 4 * count));
|
|
|
+ },
|
|
|
+ glGetProgramiv: function (program, pname, params) {
|
|
|
+ mty_set_uint32(params, MTY.gl.getProgramParameter(mty_gl_obj(program), pname));
|
|
|
+ },
|
|
|
+ glPixelStorei: function (pname, param) {
|
|
|
+ MTY.gl.pixelStorei(pname, param);
|
|
|
+ },
|
|
|
+ web_gl_flush: function () {
|
|
|
+ MTY.gl.flush();
|
|
|
+ },
|
|
|
+};
|
|
|
+
|
|
|
+
|
|
|
+// Audio
|
|
|
+
|
|
|
+function mty_mutex_lock_nb(mutex, index)
|
|
|
+{
|
|
|
+ return Atomics.compareExchange(mutex, index, 0, 1) == 0;
|
|
|
+}
|
|
|
+
|
|
|
+function mty_mutex_lock(mutex, index) {
|
|
|
+ while (true) {
|
|
|
+ if (mty_mutex_lock_nb(mutex, index))
|
|
|
+ return;
|
|
|
+
|
|
|
+ Atomics.wait(mutex, index, 1);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+function mty_mutex_unlock(mutex, index, notify) {
|
|
|
+ Atomics.compareExchange(mutex, index, 1, 0);
|
|
|
+
|
|
|
+ if (notify)
|
|
|
+ Atomics.notify(mutex, index, 1);
|
|
|
+}
|
|
|
+
|
|
|
+const MTY_AUDIO_API = {
|
|
|
+ MTY_AudioCreate: function (sampleRate, minBuffer, maxBuffer, channels, deviceID, fallback) {
|
|
|
+ MTY.audio = {
|
|
|
+ sampleRate,
|
|
|
+ minBuffer,
|
|
|
+ maxBuffer,
|
|
|
+ channels,
|
|
|
+ };
|
|
|
+
|
|
|
+ return 0xCDD;
|
|
|
+ },
|
|
|
+ MTY_AudioDestroy: function (audio) {
|
|
|
+ if (!audio || !mty_get_uint32(audio))
|
|
|
+ return;
|
|
|
+
|
|
|
+ postMessage({type: 'audio-destroy'});
|
|
|
+ mty_set_uint32(audio, 0);
|
|
|
+ },
|
|
|
+ MTY_AudioQueue: function (ctx, frames, count) {
|
|
|
+ const buf = new Int16Array(MTY_MEMORY.buffer, frames, count * MTY.audio.channels);
|
|
|
+
|
|
|
+ mty_mutex_lock(MTY.audioObjs.control, 0);
|
|
|
+
|
|
|
+ if (buf.length <= MTY.audioObjs.buf.length - MTY.audioObjs.control[1]) {
|
|
|
+ MTY.audioObjs.buf.set(buf, MTY.audioObjs.control[1]);
|
|
|
+ MTY.audioObjs.control[1] += buf.length;
|
|
|
+ }
|
|
|
+
|
|
|
+ mty_mutex_unlock(MTY.audioObjs.control, 0, false);
|
|
|
+
|
|
|
+ postMessage({type: 'audio-queue', ...MTY.audio});
|
|
|
+ },
|
|
|
+ MTY_AudioReset: function (ctx) {
|
|
|
+ mty_mutex_lock(MTY.audioObjs.control, 0);
|
|
|
+
|
|
|
+ MTY.audioObjs.control[1] = 0;
|
|
|
+
|
|
|
+ mty_mutex_unlock(MTY.audioObjs.control, 0, false);
|
|
|
+ },
|
|
|
+ MTY_AudioGetQueued: function (ctx) {
|
|
|
+ return Atomics.load(MTY.audioObjs.control, 2);
|
|
|
+ },
|
|
|
+};
|
|
|
+
|
|
|
+
|
|
|
+// Audio Worklet
|
|
|
+
|
|
|
+if (typeof AudioWorkletGlobalScope != 'undefined') {
|
|
|
+
|
|
|
+function mty_int16_to_float(i) {
|
|
|
+ return i < 0 ? i / 32768 : i / 32767;
|
|
|
+}
|
|
|
+
|
|
|
+class MTY_Audio extends AudioWorkletProcessor {
|
|
|
+ constructor(options) {
|
|
|
+ super();
|
|
|
+
|
|
|
+ const frames_per_ms = Math.round(sampleRate / 1000.0);
|
|
|
+
|
|
|
+ this.minBuffer = Math.round(options.processorOptions.minBuffer * frames_per_ms);
|
|
|
+ this.maxBuffer = Math.round(options.processorOptions.maxBuffer * frames_per_ms);
|
|
|
+ this.channels = options.outputChannelCount[0];
|
|
|
+ this.playing = false;
|
|
|
+
|
|
|
+ this.ibuf = new Int16Array(new ArrayBuffer(1024 * 1024));
|
|
|
+ this.ibufLen = 0;
|
|
|
+
|
|
|
+ this.port.onmessage = (evt) => {
|
|
|
+ this.sbuf = evt.data.buf;
|
|
|
+ this.control = evt.data.control;
|
|
|
+ };
|
|
|
+ }
|
|
|
+
|
|
|
+ process(inputs, outputs, parameters) {
|
|
|
+ // Copy from staging buffer to internal buffer
|
|
|
+ if (mty_mutex_lock_nb(this.control, 0)) {
|
|
|
+ if (this.control[1] <= this.ibuf.length - this.ibufLen) {
|
|
|
+ this.ibuf.set(new Int16Array(this.sbuf.buffer, 0, this.control[1]), this.ibufLen);
|
|
|
+ this.ibufLen += this.control[1];
|
|
|
+ this.control[1] = 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ mty_mutex_unlock(this.control, 0, true);
|
|
|
+ }
|
|
|
+
|
|
|
+ let queued = this.ibufLen / this.channels;
|
|
|
+
|
|
|
+ // Queued audio has reached the min buffer, begin playing
|
|
|
+ if (!this.playing && queued >= this.minBuffer)
|
|
|
+ this.playing = true;
|
|
|
+
|
|
|
+ // No audio left, pause and let buffer refill
|
|
|
+ if (this.playing && this.ibufLen == 0)
|
|
|
+ this.playing = false;
|
|
|
+
|
|
|
+ // Fill output with buffered audio
|
|
|
+ if (this.playing) {
|
|
|
+ const l = outputs[0][0];
|
|
|
+ const r = outputs[0][1];
|
|
|
+
|
|
|
+ let x = 0;
|
|
|
+ for (let o = 0; x < this.ibufLen && o < l.length && o < r.length; x += this.channels, o++) {
|
|
|
+ l[o] = mty_int16_to_float(this.ibuf[x]);
|
|
|
+ r[o] = mty_int16_to_float(this.ibuf[x + 1]);
|
|
|
+ }
|
|
|
+
|
|
|
+ // Essentially a 'memmove' to bring remaining audio to front of the buffer
|
|
|
+ this.ibufLen -= x;
|
|
|
+ this.ibuf.set(new Int16Array(this.ibuf.buffer, x * 2, this.ibufLen));
|
|
|
+ }
|
|
|
+
|
|
|
+ queued = this.ibufLen / this.channels;
|
|
|
+
|
|
|
+ // If buffer has exceeded the max, reset
|
|
|
+ if (this.playing && queued > this.maxBuffer) {
|
|
|
+ this.playing = false;
|
|
|
+ this.ibufLen = 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ // Store queued frames
|
|
|
+ Atomics.store(this.control, 2, queued);
|
|
|
+
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+registerProcessor('MTY_Audio', MTY_Audio);
|
|
|
+
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+// Net
|
|
|
+
|
|
|
+function mty_net_headers(cheaders) {
|
|
|
+ const headers_str = mty_str_to_js(cheaders);
|
|
|
+
|
|
|
+ const headers = {};
|
|
|
+ const headers_nl = headers_str.split('\n');
|
|
|
+ for (let x = 0; x < headers_nl.length; x++) {
|
|
|
+ const pair = headers_nl[x];
|
|
|
+ const pair_split = pair.split(':');
|
|
|
+
|
|
|
+ if (pair_split[0] && pair_split[1])
|
|
|
+ headers[pair_split[0]] = pair_split[1];
|
|
|
+ }
|
|
|
+
|
|
|
+ return headers;
|
|
|
+}
|
|
|
+
|
|
|
+const MTY_NET_API = {
|
|
|
+ MTY_HttpRequest: function (curl, cmethod, cheaders, cbody, bodySize, proxy, timeout,
|
|
|
+ response, responseSize, cstatus)
|
|
|
+ {
|
|
|
+ // FIXME timeout is currently ignored
|
|
|
+ // FIXME proxy is currently ignored
|
|
|
+
|
|
|
+ const body = cbody ? mty_dup(cbody, bodySize) : null;
|
|
|
+
|
|
|
+ postMessage({
|
|
|
+ type: 'http',
|
|
|
+ url: mty_str_to_js(curl),
|
|
|
+ method: mty_str_to_js(cmethod),
|
|
|
+ headers: mty_net_headers(cheaders),
|
|
|
+ body: body,
|
|
|
+ sync: MTY.sync,
|
|
|
+ sab: MTY.sab,
|
|
|
+ }, body ? [body.buffer] : []);
|
|
|
+
|
|
|
+ mty_wait(MTY.sync);
|
|
|
+
|
|
|
+ const error = MTY.sab[0];
|
|
|
+ if (error)
|
|
|
+ return false;
|
|
|
+
|
|
|
+ const size = MTY.sab[1];
|
|
|
+ mty_set_uint32(responseSize, size);
|
|
|
+
|
|
|
+ const status = MTY.sab[2];
|
|
|
+ mty_set_uint16(cstatus, status);
|
|
|
+
|
|
|
+ if (size > 0) {
|
|
|
+ const buf = mty_alloc(size + 1);
|
|
|
+ mty_set_uint32(response, buf);
|
|
|
+
|
|
|
+ postMessage({
|
|
|
+ type: 'async-copy',
|
|
|
+ sync: MTY.sync,
|
|
|
+ sab8: new Uint8Array(MTY_MEMORY.buffer, buf, size + 1),
|
|
|
+ });
|
|
|
+
|
|
|
+ mty_wait(MTY.sync);
|
|
|
+ }
|
|
|
+
|
|
|
+ return true;
|
|
|
+ },
|
|
|
+ MTY_WebSocketConnect: function (curl, cheaders, proxy, timeout, upgrade_status_out) {
|
|
|
+ // FIXME headers are currently ignored
|
|
|
+ // FIXME proxy is currently ignored
|
|
|
+ // FIXME timeout is currently ignored
|
|
|
+ // FIXME upgrade_status_out currently unsupported
|
|
|
+
|
|
|
+ postMessage({
|
|
|
+ type: 'ws-connect',
|
|
|
+ url: mty_str_to_js(curl),
|
|
|
+ sync: MTY.sync,
|
|
|
+ sab: MTY.sab,
|
|
|
+ });
|
|
|
+
|
|
|
+ mty_wait(MTY.sync);
|
|
|
+
|
|
|
+ return MTY.sab[0];
|
|
|
+ },
|
|
|
+ MTY_WebSocketDestroy: function (ctx_out) {
|
|
|
+ if (!ctx_out)
|
|
|
+ return;
|
|
|
+
|
|
|
+ postMessage({
|
|
|
+ type: 'ws-close',
|
|
|
+ ctx: mty_get_uint32(ctx_out),
|
|
|
+ });
|
|
|
+ },
|
|
|
+ MTY_WebSocketRead: function (ctx, timeout, msg_out, size) {
|
|
|
+ postMessage({
|
|
|
+ type: 'ws-read',
|
|
|
+ ctx: ctx,
|
|
|
+ timeout: timeout,
|
|
|
+ sab: MTY.sab,
|
|
|
+ sync: MTY.sync,
|
|
|
+ });
|
|
|
+
|
|
|
+ mty_wait(MTY.sync);
|
|
|
+
|
|
|
+ if (MTY.sab[0] == 0) { // MTY_ASYNC_OK
|
|
|
+ const rsize = MTY.sab[1];
|
|
|
+
|
|
|
+ if (rsize < size) {
|
|
|
+ const buf = mty_alloc(rsize);
|
|
|
+ const sab8 = new Uint8Array(MTY_MEMORY.buffer, buf, rsize);
|
|
|
+
|
|
|
+ postMessage({
|
|
|
+ type: 'async-copy',
|
|
|
+ sync: MTY.sync,
|
|
|
+ sab8: sab8,
|
|
|
+ });
|
|
|
+
|
|
|
+ mty_wait(MTY.sync);
|
|
|
+
|
|
|
+ mty_strcpy(msg_out, sab8);
|
|
|
+ mty_free(buf);
|
|
|
+
|
|
|
+ } else {
|
|
|
+ MTY.sab[0] = 3 // MTY_ASYNC_ERROR
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return MTY.sab[0]; // MTY_Async
|
|
|
+ },
|
|
|
+ MTY_WebSocketWrite: function (ctx, msg_c) {
|
|
|
+ postMessage({
|
|
|
+ type: 'ws-write',
|
|
|
+ ctx: ctx,
|
|
|
+ text: mty_str_to_js(msg_c),
|
|
|
+ });
|
|
|
+
|
|
|
+ return true;
|
|
|
+ },
|
|
|
+ MTY_WebSocketGetCloseCode: function (ctx) {
|
|
|
+ postMessage({
|
|
|
+ type: 'ws-code',
|
|
|
+ ctx: ctx,
|
|
|
+ sab: MTY.sab,
|
|
|
+ sync: MTY.sync,
|
|
|
+ });
|
|
|
+
|
|
|
+ mty_wait(MTY.sync);
|
|
|
+
|
|
|
+ return MTY.sab[0];
|
|
|
+ },
|
|
|
+};
|
|
|
+
|
|
|
+
|
|
|
+// Image
|
|
|
+
|
|
|
+const MTY_IMAGE_API = {
|
|
|
+ MTY_DecompressImage: function (input, size, cwidth, cheight) {
|
|
|
+ const jinput = mty_dup(input, size);
|
|
|
+
|
|
|
+ postMessage({
|
|
|
+ type: 'decode-image',
|
|
|
+ input: jinput.buffer,
|
|
|
+ sync: MTY.sync,
|
|
|
+ sab: MTY.sab,
|
|
|
+ }, [jinput.buffer]);
|
|
|
+
|
|
|
+ mty_wait(MTY.sync);
|
|
|
+
|
|
|
+ const width = MTY.sab[0];
|
|
|
+ mty_set_uint32(cwidth, width);
|
|
|
+
|
|
|
+ const height = MTY.sab[1];
|
|
|
+ mty_set_uint32(cheight, height);
|
|
|
+
|
|
|
+ const buf_size = width * height * 4;
|
|
|
+ const buf = mty_alloc(buf_size);
|
|
|
+
|
|
|
+ postMessage({
|
|
|
+ type: 'async-copy',
|
|
|
+ sync: MTY.sync,
|
|
|
+ sab8: new Uint8Array(MTY_MEMORY.buffer, buf, buf_size),
|
|
|
+ });
|
|
|
+
|
|
|
+ mty_wait(MTY.sync);
|
|
|
+
|
|
|
+ return buf;
|
|
|
+ },
|
|
|
+ MTY_CompressImage: function (method, input, width, height, outputSize) {
|
|
|
+ },
|
|
|
+ MTY_GetProgramIcon: function (path, width, height) {
|
|
|
+ },
|
|
|
+};
|
|
|
+
|
|
|
+
|
|
|
+// Crypto
|
|
|
+
|
|
|
+const MTY_CRYPTO_API = {
|
|
|
+ MTY_CryptoHash: function (algo, input, inputSize, key, keySize, output, outputSize) {
|
|
|
+ },
|
|
|
+ MTY_GetRandomBytes: function (buf, size) {
|
|
|
+ mty_memcpy(buf, crypto.getRandomValues(new Uint8Array(size)));
|
|
|
+ },
|
|
|
+ MTY_BytesToBase64: function (bytes, size, base64, base64Size) {
|
|
|
+ const jbytes = new Uint8Array(MTY_MEMORY.buffer, bytes, size);
|
|
|
+
|
|
|
+ try {
|
|
|
+ mty_str_to_c(mty_buf_to_b64(jbytes), base64, base64Size);
|
|
|
+
|
|
|
+ } catch (e) {
|
|
|
+ console.error("'base64Size' is too small");
|
|
|
+ }
|
|
|
+ },
|
|
|
+};
|
|
|
+
|
|
|
+
|
|
|
+// System
|
|
|
+
|
|
|
+const MTY_SYSTEM_API = {
|
|
|
+ MTY_HandleProtocol: function (uri, token) {
|
|
|
+ postMessage({type: 'uri', uri});
|
|
|
+ },
|
|
|
+};
|
|
|
+
|
|
|
+
|
|
|
+// Web API (mostly used in app.c)
|
|
|
+
|
|
|
+function mty_update_window(app, info) {
|
|
|
+ MTY.exports.mty_window_update_position(app, info.posX, info.posY);
|
|
|
+ MTY.exports.mty_window_update_screen(app, info.screenWidth, info.screenHeight);
|
|
|
+ MTY.exports.mty_window_update_size(app, info.canvasWidth, info.canvasHeight);
|
|
|
+ MTY.exports.mty_window_update_focus(app, info.hasFocus);
|
|
|
+ MTY.exports.mty_window_update_fullscreen(app, info.fullscreen);
|
|
|
+ MTY.exports.mty_window_update_visibility(app, info.visible);
|
|
|
+ MTY.exports.mty_window_update_pixel_ratio(app, info.devicePixelRatio);
|
|
|
+ MTY.exports.mty_window_update_relative_mouse(app, info.relative);
|
|
|
+}
|
|
|
+
|
|
|
+const MTY_WEB_API = {
|
|
|
+ web_alert: function (title, msg) {
|
|
|
+ postMessage({type: 'alert', title, msg});
|
|
|
+ },
|
|
|
+ web_set_fullscreen: function (fullscreen) {
|
|
|
+ postMessage({type: 'fullscreen', fullscreen});
|
|
|
+ },
|
|
|
+ web_wake_lock: function (enable) {
|
|
|
+ postMessage({type: 'wake-lock', enable});
|
|
|
+ },
|
|
|
+ web_rumble_gamepad: function (id, low, high) {
|
|
|
+ postMessage({type: 'rumble', id, low, high});
|
|
|
+ },
|
|
|
+ web_show_cursor: function (show) {
|
|
|
+ postMessage({type: 'show-cursor', show});
|
|
|
+ },
|
|
|
+ web_get_clipboard: function () {
|
|
|
+ postMessage({type: 'get-clip', sync: MTY.sync, sab: MTY.sab});
|
|
|
+ mty_wait(MTY.sync);
|
|
|
+
|
|
|
+ const size = MTY.sab[0];
|
|
|
+ const buf = mty_alloc(size + 1);
|
|
|
+
|
|
|
+ if (size > 0) {
|
|
|
+ postMessage({
|
|
|
+ type: 'async-copy',
|
|
|
+ sync: MTY.sync,
|
|
|
+ sab8: new Uint8Array(MTY_MEMORY.buffer, buf, size + 1),
|
|
|
+ });
|
|
|
+
|
|
|
+ mty_wait(MTY.sync);
|
|
|
+ }
|
|
|
+
|
|
|
+ return buf;
|
|
|
+ },
|
|
|
+ web_set_clipboard: function (text) {
|
|
|
+ postMessage({type: 'set-clip', text});
|
|
|
+ },
|
|
|
+ web_set_pointer_lock: function (enable) {
|
|
|
+ postMessage({type: 'pointer-lock', enable});
|
|
|
+ },
|
|
|
+ web_use_default_cursor: function (use_default) {
|
|
|
+ postMessage({type: 'cursor-default', use_default});
|
|
|
+ },
|
|
|
+ web_set_rgba_cursor: function (buffer, width, height, hot_x, hot_y) {
|
|
|
+ const buf = buffer ? mty_dup(buffer, width * height * 4) : null
|
|
|
+ postMessage({type: 'cursor-rgba', buf, width, height, hot_x, hot_y}, buf ? [buf.buffer] : []);
|
|
|
+ },
|
|
|
+ web_set_png_cursor: function (buffer, size, hot_x, hot_y) {
|
|
|
+ const buf = buffer ? mty_dup(buffer, size) : null
|
|
|
+ postMessage({type: 'cursor-png', buf, hot_x, hot_y}, buf ? [buf.buffer] : []);
|
|
|
+ },
|
|
|
+ web_set_kb_grab: function (grab) {
|
|
|
+ postMessage({type: 'kb-grab', grab});
|
|
|
+ },
|
|
|
+ web_get_hostname: function () {
|
|
|
+ return mty_dup_c(mty_encode(MTY.hostname));
|
|
|
+ },
|
|
|
+ web_platform: function (platform, size) {
|
|
|
+ mty_str_to_c(navigator.platform, platform, size);
|
|
|
+ },
|
|
|
+ web_set_key: function (reverse, code, key) {
|
|
|
+ const str = mty_str_to_js(code);
|
|
|
+ MTY.keys[str] = key;
|
|
|
+
|
|
|
+ if (reverse)
|
|
|
+ MTY.keysRev[key] = str;
|
|
|
+ },
|
|
|
+ web_get_key: function (key, buf, len) {
|
|
|
+ const code = MTY.keysRev[key];
|
|
|
+
|
|
|
+ if (code != undefined) {
|
|
|
+ const text = MTY.kbMap[code];
|
|
|
+ if (text) {
|
|
|
+ mty_str_to_c(text.toUpperCase(), buf, len);
|
|
|
+
|
|
|
+ } else {
|
|
|
+ mty_str_to_c(code, buf, len);
|
|
|
+ }
|
|
|
+
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ return false;
|
|
|
+ },
|
|
|
+ web_set_title: function (title) {
|
|
|
+ postMessage({
|
|
|
+ type: 'title',
|
|
|
+ title: mty_str_to_js(title),
|
|
|
+ });
|
|
|
+ },
|
|
|
+ web_set_gfx: function () {
|
|
|
+ const info = MTY.initWindowInfo;
|
|
|
+ const canvas = new OffscreenCanvas(info.canvasWidth, info.canvasHeight);
|
|
|
+
|
|
|
+ MTY.gl = canvas.getContext('webgl2', {
|
|
|
+ depth: false,
|
|
|
+ antialias: false,
|
|
|
+ powerPreference: 'high-performance',
|
|
|
+ });
|
|
|
+ },
|
|
|
+ web_set_canvas_size: function (width, height) {
|
|
|
+ MTY.gl.canvas.width = width;
|
|
|
+ MTY.gl.canvas.height = height;
|
|
|
+ },
|
|
|
+ web_present: function (wait) {
|
|
|
+ const image = MTY.gl.canvas.transferToImageBitmap();
|
|
|
+
|
|
|
+ postMessage({
|
|
|
+ type: 'present',
|
|
|
+ image: image,
|
|
|
+ }, [image]);
|
|
|
+
|
|
|
+ if (wait)
|
|
|
+ mty_wait(MTY.psync);
|
|
|
+ },
|
|
|
+
|
|
|
+ // Synchronization from C
|
|
|
+ MTY_WaitPtr: function (csync) {
|
|
|
+ mty_wait(new Int32Array(MTY_MEMORY.buffer, csync, 1));
|
|
|
+ },
|
|
|
+
|
|
|
+ // Should be called on main thread only
|
|
|
+ web_set_app: function (app) {
|
|
|
+ MTY.app = app;
|
|
|
+ mty_update_window(app, MTY.initWindowInfo);
|
|
|
+ },
|
|
|
+ web_run_and_yield: function (iter, opaque) {
|
|
|
+ MTY.exports.mty_app_set_keys();
|
|
|
+
|
|
|
+ const step = () => {
|
|
|
+ if (mty_cfunc(iter)(opaque))
|
|
|
+ setTimeout(step, 0);
|
|
|
+ };
|
|
|
+
|
|
|
+ setTimeout(step, 0);
|
|
|
+ throw 'MTY_RunAndYield halted execution';
|
|
|
+ },
|
|
|
+};
|
|
|
+
|
|
|
+
|
|
|
+// WASI API
|
|
|
+
|
|
|
+// github.com/WebAssembly/wasi-libc/blob/main/libc-bottom-half/headers/public/wasi/api.h
|
|
|
+
|
|
|
+const __WASI_ERRNO_SUCCESS = 0;
|
|
|
+const __WASI_ERRNO_BADF = 8;
|
|
|
+const __WASI_ERRNO_INVAL = 28;
|
|
|
+
|
|
|
+function mty_append_buf(cur_buf, buf) {
|
|
|
+ // FIXME This is a crude way to handle appending to an open file,
|
|
|
+ // complex seek operations will break this
|
|
|
+
|
|
|
+ const new_buf = new Uint8Array(cur_buf.length + buf.length);
|
|
|
+
|
|
|
+ new_buf.set(cur_buf);
|
|
|
+ new_buf.set(buf, cur_buf.length);
|
|
|
+
|
|
|
+ return new_buf;
|
|
|
+}
|
|
|
+
|
|
|
+function mty_arg_list(bin, args) {
|
|
|
+ let plist = [mty_encode(bin)];
|
|
|
+
|
|
|
+ const params = new URLSearchParams(args);
|
|
|
+ const qs = params.toString();
|
|
|
+
|
|
|
+ if (qs)
|
|
|
+ plist.push(mty_encode(qs));
|
|
|
+
|
|
|
+ return plist;
|
|
|
+}
|
|
|
+
|
|
|
+const MTY_WASI_SNAPSHOT_PREVIEW1_API = {
|
|
|
+ // Command line arguments
|
|
|
+ args_get: function (argv, argv_buf) {
|
|
|
+ const args = mty_arg_list(MTY.bin, MTY.queryString);
|
|
|
+
|
|
|
+ for (let x = 0; x < args.length; x++) {
|
|
|
+ mty_strcpy(argv_buf, args[x]);
|
|
|
+ mty_set_uint32(argv + x * 4, argv_buf);
|
|
|
+ argv_buf += args[x].length + 1;
|
|
|
+ }
|
|
|
+
|
|
|
+ return __WASI_ERRNO_SUCCESS;
|
|
|
+ },
|
|
|
+ args_sizes_get: function (retptr0, retptr1) {
|
|
|
+ const args = mty_arg_list(MTY.bin, MTY.queryString);
|
|
|
+
|
|
|
+ let len = 0;
|
|
|
+ for (let x = 0; x < args.length; x++)
|
|
|
+ len += args[x].length + 1;
|
|
|
+
|
|
|
+ mty_set_uint32(retptr0, args.length);
|
|
|
+ mty_set_uint32(retptr1, len);
|
|
|
+
|
|
|
+ return __WASI_ERRNO_SUCCESS;
|
|
|
+ },
|
|
|
+
|
|
|
+ // WASI preopened directory (/)
|
|
|
+ fd_prestat_get: function (fd, retptr0) {
|
|
|
+ if (MTY.preopen == undefined) {
|
|
|
+ mty_set_int8(retptr0, 0);
|
|
|
+ mty_set_uint64(retptr0 + 4, 1);
|
|
|
+ MTY.preopen = fd;
|
|
|
+
|
|
|
+ return __WASI_ERRNO_SUCCESS;
|
|
|
+ }
|
|
|
+
|
|
|
+ return __WASI_ERRNO_BADF;
|
|
|
+ },
|
|
|
+ fd_prestat_dir_name: function (fd, path, path_len) {
|
|
|
+ if (MTY.preopen == fd) {
|
|
|
+ mty_strcpy(path, mty_encode('/'));
|
|
|
+ return __WASI_ERRNO_SUCCESS;
|
|
|
+ }
|
|
|
+
|
|
|
+ return __WASI_ERRNO_INVAL;
|
|
|
+ },
|
|
|
+
|
|
|
+ // Paths
|
|
|
+ path_filestat_get: function (fd, flags, path, path_size, retptr0) {
|
|
|
+ const jpath = mty_str_to_js(path);
|
|
|
+ const buf = mty_get_ls(jpath);
|
|
|
+
|
|
|
+ // We only need to return the size
|
|
|
+ if (buf)
|
|
|
+ mty_set_uint64(retptr0 + 32, buf.byteLength);
|
|
|
+
|
|
|
+ return __WASI_ERRNO_SUCCESS;
|
|
|
+ },
|
|
|
+ path_open: function (fd, dirflags, path, path_size, oflags, fs_rights_base,
|
|
|
+ fs_rights_inheriting, fdflags, retptr0)
|
|
|
+ {
|
|
|
+ const new_fd = MTY.fdIndex++;
|
|
|
+ mty_set_uint32(retptr0, new_fd);
|
|
|
+
|
|
|
+ MTY.fds[new_fd] = {
|
|
|
+ path: mty_str_to_js(path),
|
|
|
+ append: fdflags == 1,
|
|
|
+ offset: 0,
|
|
|
+ };
|
|
|
+
|
|
|
+ return __WASI_ERRNO_SUCCESS;
|
|
|
+ },
|
|
|
+ path_create_directory: function (fd, path) {
|
|
|
+ return __WASI_ERRNO_SUCCESS;
|
|
|
+ },
|
|
|
+ path_remove_directory: function (fd, path) {
|
|
|
+ return __WASI_ERRNO_SUCCESS;
|
|
|
+ },
|
|
|
+ path_unlink_file: function (fd, path) {
|
|
|
+ return __WASI_ERRNO_SUCCESS;
|
|
|
+ },
|
|
|
+ path_readlink: function (fd, path, buf, buf_len, retptr0) {
|
|
|
+ },
|
|
|
+ path_rename: function (fd, old_path, new_fd, new_path) {
|
|
|
+ return __WASI_ERRNO_SUCCESS;
|
|
|
+ },
|
|
|
+
|
|
|
+ // File descriptors
|
|
|
+ fd_close: function (fd) {
|
|
|
+ delete MTY.fds[fd];
|
|
|
+ },
|
|
|
+ fd_fdstat_get: function (fd, retptr0) {
|
|
|
+ return __WASI_ERRNO_SUCCESS;
|
|
|
+ },
|
|
|
+ fd_fdstat_set_flags: function (fd, flags) {
|
|
|
+ },
|
|
|
+ fd_readdir: function (fd, buf, buf_len, cookie, retptr0) {
|
|
|
+ return __WASI_ERRNO_BADF;
|
|
|
+ },
|
|
|
+ fd_seek: function (fd, offset, whence, retptr0) {
|
|
|
+ return __WASI_ERRNO_SUCCESS;
|
|
|
+ },
|
|
|
+ fd_read: function (fd, iovs, iovs_len, retptr0) {
|
|
|
+ const finfo = MTY.fds[fd];
|
|
|
+ const file_buf = mty_get_ls(finfo.path);
|
|
|
+
|
|
|
+ if (finfo && file_buf) {
|
|
|
+ let offset = 0;
|
|
|
+
|
|
|
+ for (let x = 0; x < iovs_len; x++) {
|
|
|
+ let ptr = iovs + x * 8;
|
|
|
+ let buf = mty_get_uint32(ptr);
|
|
|
+ let buf_len = mty_get_uint32(ptr + 4);
|
|
|
+ let len = buf_len < file_buf.length - offset ? buf_len : file_buf.length - offset;
|
|
|
+
|
|
|
+ mty_memcpy(buf, new Uint8Array(file_buf.buffer, offset, len));
|
|
|
+
|
|
|
+ offset += len;
|
|
|
+ }
|
|
|
+
|
|
|
+ mty_set_uint32(retptr0, offset);
|
|
|
+ }
|
|
|
+
|
|
|
+ return __WASI_ERRNO_SUCCESS;
|
|
|
+ },
|
|
|
+ fd_write: function (fd, iovs, iovs_len, retptr0) {
|
|
|
+ // Calculate full write size
|
|
|
+ let len = 0;
|
|
|
+ for (let x = 0; x < iovs_len; x++)
|
|
|
+ len += mty_get_uint32(iovs + x * 8 + 4);
|
|
|
+
|
|
|
+ mty_set_uint32(retptr0, len);
|
|
|
+
|
|
|
+ // Create a contiguous buffer
|
|
|
+ let offset = 0;
|
|
|
+ let file_buf = new Uint8Array(len);
|
|
|
+ for (let x = 0; x < iovs_len; x++) {
|
|
|
+ let ptr = iovs + x * 8;
|
|
|
+ let buf = mty_get_uint32(ptr);
|
|
|
+ let buf_len = mty_get_uint32(ptr + 4);
|
|
|
+
|
|
|
+ file_buf.set(new Uint8Array(MTY_MEMORY.buffer, buf, buf_len), offset);
|
|
|
+ offset += buf_len;
|
|
|
+ }
|
|
|
+
|
|
|
+ // stdout
|
|
|
+ if (fd == 1) {
|
|
|
+ const str = mty_decode(file_buf);
|
|
|
+ if (str != '\n')
|
|
|
+ console.log(str);
|
|
|
+
|
|
|
+ // stderr
|
|
|
+ } else if (fd == 2) {
|
|
|
+ const str = mty_decode(file_buf)
|
|
|
+ if (str != '\n')
|
|
|
+ console.error(str);
|
|
|
+
|
|
|
+ // Filesystem
|
|
|
+ } else if (MTY.fds[fd]) {
|
|
|
+ const finfo = MTY.fds[fd];
|
|
|
+ const cur_buf = mty_get_ls(finfo.path);
|
|
|
+
|
|
|
+ if (cur_buf && finfo.append) {
|
|
|
+ mty_set_ls(finfo.path, mty_append_buf(cur_buf, file_buf));
|
|
|
+
|
|
|
+ } else {
|
|
|
+ mty_set_ls(finfo.path, file_buf);
|
|
|
+ }
|
|
|
+
|
|
|
+ finfo.offet += len;
|
|
|
+ }
|
|
|
+
|
|
|
+ return __WASI_ERRNO_SUCCESS;
|
|
|
+ },
|
|
|
+
|
|
|
+ // Misc
|
|
|
+ clock_time_get: function (id, precision, retptr0) {
|
|
|
+ mty_set_uint64(retptr0, Math.round(performance.now() * 1000.0 * 1000.0));
|
|
|
+ return __WASI_ERRNO_SUCCESS;
|
|
|
+ },
|
|
|
+ poll_oneoff: function (_in, out, nsubscriptions, retptr0) {
|
|
|
+ // __WASI_EVENTTYPE_CLOCK
|
|
|
+ if (mty_get_uint8(_in + 8) == 0)
|
|
|
+ Atomics.wait(MTY.sleeper, 0, 0, Number(mty_get_uint64(_in + 24)) / 1000000);
|
|
|
+
|
|
|
+ mty_set_uint32(out + 8, 0);
|
|
|
+ return __WASI_ERRNO_SUCCESS;
|
|
|
+ },
|
|
|
+ proc_exit: function (rval) {
|
|
|
+ },
|
|
|
+ environ_get: function (environ, environ_buf) {
|
|
|
+ },
|
|
|
+ environ_sizes_get: function (retptr0, retptr1) {
|
|
|
+ },
|
|
|
+ sched_yield: function () {
|
|
|
+ },
|
|
|
+};
|
|
|
+
|
|
|
+const MTY_WASI_API = {
|
|
|
+ 'thread-spawn': function (start_arg) {
|
|
|
+ postMessage({
|
|
|
+ type: 'thread',
|
|
|
+ startArg: start_arg,
|
|
|
+ sab: MTY.sab,
|
|
|
+ sync: MTY.sync,
|
|
|
+ });
|
|
|
+
|
|
|
+ mty_wait(MTY.sync);
|
|
|
+
|
|
|
+ return MTY.sab[0];
|
|
|
+ },
|
|
|
+};
|
|
|
+
|
|
|
+
|
|
|
+// Entry
|
|
|
+
|
|
|
+if (typeof WorkerGlobalScope != 'undefined') {
|
|
|
+
|
|
|
+async function mty_instantiate_wasm(wasmBuf, userEnv) {
|
|
|
+ // Imports
|
|
|
+ const imports = {
|
|
|
+ env: {
|
|
|
+ memory: MTY_MEMORY,
|
|
|
+ ...MTY_UNISTD_API,
|
|
|
+ ...MTY_GL_API,
|
|
|
+ ...MTY_AUDIO_API,
|
|
|
+ ...MTY_NET_API,
|
|
|
+ ...MTY_IMAGE_API,
|
|
|
+ ...MTY_CRYPTO_API,
|
|
|
+ ...MTY_SYSTEM_API,
|
|
|
+ ...MTY_WEB_API,
|
|
|
+ },
|
|
|
+ wasi_snapshot_preview1: {
|
|
|
+ ...MTY_WASI_SNAPSHOT_PREVIEW1_API,
|
|
|
+ },
|
|
|
+ wasi: {
|
|
|
+ ...MTY_WASI_API,
|
|
|
+ },
|
|
|
+ }
|
|
|
+
|
|
|
+ // Add userEnv to imports, run on the main thread
|
|
|
+ for (let x = 0; x < userEnv.length; x++) {
|
|
|
+ const key = userEnv[x];
|
|
|
+
|
|
|
+ imports.env[key] = function () {
|
|
|
+ const args = [];
|
|
|
+ for (let y = 0; y < arguments.length; y++)
|
|
|
+ args.push(arguments[y]);
|
|
|
+
|
|
|
+ postMessage({
|
|
|
+ type: 'user-env',
|
|
|
+ name: key,
|
|
|
+ args: args,
|
|
|
+ sab: MTY.sab,
|
|
|
+ sync: MTY.sync,
|
|
|
+ });
|
|
|
+
|
|
|
+ mty_wait(MTY.sync);
|
|
|
+
|
|
|
+ return MTY.sab[0];
|
|
|
+ };
|
|
|
+ }
|
|
|
+
|
|
|
+ return await WebAssembly.instantiate(wasmBuf, imports);
|
|
|
+}
|
|
|
+
|
|
|
+onmessage = async (ev) => {
|
|
|
+ const msg = ev.data;
|
|
|
+
|
|
|
+ switch (msg.type) {
|
|
|
+ case 'init':
|
|
|
+ importScripts(msg.file);
|
|
|
+
|
|
|
+ MTY_MEMORY = msg.memory;
|
|
|
+
|
|
|
+ MTY.queryString = msg.args;
|
|
|
+ MTY.hostname = msg.hostname;
|
|
|
+ MTY.bin = msg.bin;
|
|
|
+ MTY.fdIndex = 64;
|
|
|
+ MTY.kbMap = msg.kbMap;
|
|
|
+ MTY.psync = msg.psync;
|
|
|
+ MTY.audioObjs = msg.audioObjs;
|
|
|
+ MTY.initWindowInfo = msg.windowInfo;
|
|
|
+ MTY.sync = new Int32Array(new SharedArrayBuffer(4));
|
|
|
+ MTY.sleeper = new Int32Array(new SharedArrayBuffer(4));
|
|
|
+ MTY.module = await mty_instantiate_wasm(msg.wasmBuf, msg.userEnv);
|
|
|
+ MTY.exports = MTY.module.instance.exports;
|
|
|
+ MTY.sab = new Uint32Array(new SharedArrayBuffer(128));
|
|
|
+
|
|
|
+ // WASI will buffer stdout and stderr by default, disable it
|
|
|
+ MTY.exports.mty_system_disable_buffering();
|
|
|
+
|
|
|
+ try {
|
|
|
+ // Additional thread
|
|
|
+ if (msg.startArg) {
|
|
|
+ MTY.exports.wasi_thread_start(msg.threadId, msg.startArg);
|
|
|
+
|
|
|
+ // Main thread
|
|
|
+ } else {
|
|
|
+ MTY.exports._start();
|
|
|
+ }
|
|
|
+
|
|
|
+ close();
|
|
|
+
|
|
|
+ } catch (e) {
|
|
|
+ if (e.toString().search('MTY_RunAndYield') == -1)
|
|
|
+ console.error(e);
|
|
|
+ }
|
|
|
+ break;
|
|
|
+
|
|
|
+ // "Main" thread only
|
|
|
+ case 'window-update':
|
|
|
+ if (MTY.app)
|
|
|
+ mty_update_window(MTY.app, msg.windowInfo);
|
|
|
+ break;
|
|
|
+ case 'keyboard': {
|
|
|
+ if (!MTY.app)
|
|
|
+ return;
|
|
|
+
|
|
|
+ const key = MTY.keys[msg.code];
|
|
|
+
|
|
|
+ if (key != undefined) {
|
|
|
+ let packed = 0;
|
|
|
+
|
|
|
+ if (msg.key.length == 1) {
|
|
|
+ const buf = mty_encode(msg.key);
|
|
|
+
|
|
|
+ for (let x = 0; x < buf.length; x++)
|
|
|
+ packed |= buf[x] << x * 8;
|
|
|
+ }
|
|
|
+
|
|
|
+ MTY.exports.mty_window_keyboard(MTY.app, msg.pressed, key, packed, msg.mods);
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ case 'motion':
|
|
|
+ if (MTY.app)
|
|
|
+ MTY.exports.mty_window_motion(MTY.app, msg.relative, msg.x, msg.y);
|
|
|
+ break;
|
|
|
+ case 'button':
|
|
|
+ if (MTY.app)
|
|
|
+ MTY.exports.mty_window_button(MTY.app, msg.pressed, msg.button, msg.x, msg.y);
|
|
|
+ break;
|
|
|
+ case 'scroll':
|
|
|
+ if (MTY.app)
|
|
|
+ MTY.exports.mty_window_scroll(MTY.app, msg.x, msg.y);
|
|
|
+ break;
|
|
|
+ case 'move':
|
|
|
+ if (MTY.app)
|
|
|
+ MTY.exports.mty_window_move(MTY.app);
|
|
|
+ break;
|
|
|
+ case 'size':
|
|
|
+ if (MTY.app) {
|
|
|
+ MTY.exports.mty_window_update_size(MTY.app, msg.width, msg.height);
|
|
|
+ MTY.exports.mty_window_size(MTY.app);
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ case 'focus':
|
|
|
+ if (MTY.app) {
|
|
|
+ MTY.exports.mty_window_update_focus(MTY.app, msg.focus);
|
|
|
+ MTY.exports.mty_window_focus(MTY.app, msg.focus);
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ case 'controller':
|
|
|
+ if (MTY.app)
|
|
|
+ MTY.exports.mty_window_controller(MTY.app, msg.id, msg.state, msg.buttons, msg.lx,
|
|
|
+ msg.ly, msg.rx, msg.ry, msg.lt, msg.rt);
|
|
|
+ break;
|
|
|
+ case 'controller-disconnect':
|
|
|
+ if (MTY.app)
|
|
|
+ MTY.exports.mty_window_controller(MTY.app, msg.id, msg.state, 0, 0, 0, 0, 0, 0, 0);
|
|
|
+ break;
|
|
|
+ case 'drop': {
|
|
|
+ if (!MTY.app)
|
|
|
+ return;
|
|
|
+
|
|
|
+ const cmem = mty_dup_c(new Uint8Array(msg.data));
|
|
|
+ const cname = mty_dup_c(mty_encode(msg.name));
|
|
|
+
|
|
|
+ MTY.exports.mty_window_drop(MTY.app, cname, cmem, buf.length);
|
|
|
+
|
|
|
+ mty_free(cname);
|
|
|
+ mty_free(cmem);
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+};
|
|
|
+
|
|
|
+}
|