| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081 |
- // 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.
- // Global State
- let MTY_MEMORY;
- let MTY_CURRENT_SCRIPT;
- // Worker
- if (typeof importScripts == 'function') {
- MTY_CURRENT_SCRIPT = location;
- // Main thread
- } else {
- MTY_CURRENT_SCRIPT = new URL(document.currentScript.src);
- window.MTY = {
- wsIndex: 1,
- wsObj: {},
- cursorId: 0,
- threadId: 1,
- cursorCache: {},
- cursorClass: '',
- defaultCursor: false,
- synthesizeEsc: true,
- relative: false,
- gps: [false, false, false, false],
- };
- }
- // Memory
- function mty_encode(str) {
- return new TextEncoder().encode(str);
- }
- function mty_decode(buf) {
- return new TextDecoder().decode(buf);
- }
- function mty_strlen(buf) {
- let len = 0;
- for (; buf[len] != 0; len++);
- return len;
- }
- function mty_memcpy(ptr, buf) {
- new Uint8Array(MTY_MEMORY.buffer, ptr, buf.byteLength).set(buf);
- }
- function mty_strcpy(ptr, buf) {
- mty_memcpy(ptr, buf);
- mty_set_int8(ptr + buf.byteLength, 0);
- }
- function mty_dup(ptr, size) {
- return new Uint8Array(MTY_MEMORY.buffer, ptr).slice(0, size);
- }
- function mty_str_to_js(ptr) {
- const buf = new Uint8Array(MTY_MEMORY.buffer, ptr);
- return mty_decode(buf.slice(0, mty_strlen(buf)));
- }
- function mty_str_to_c(str, ptr, size) {
- const buf = mty_encode(str);
- if (buf.byteLength >= size)
- throw 'mty_str_to_c overflow'
- mty_strcpy(ptr, buf);
- }
- function mty_get_uint8(ptr) {
- return new DataView(MTY_MEMORY.buffer).getUint8(ptr);
- }
- function mty_set_int8(ptr, value) {
- new DataView(MTY_MEMORY.buffer).setInt8(ptr, value);
- }
- function mty_set_uint16(ptr, value) {
- new DataView(MTY_MEMORY.buffer).setUint16(ptr, value, true);
- }
- function mty_get_uint32(ptr) {
- return new DataView(MTY_MEMORY.buffer).getUint32(ptr, true);
- }
- function mty_set_uint32(ptr, value) {
- new DataView(MTY_MEMORY.buffer).setUint32(ptr, value, true);
- }
- function mty_get_uint64(ptr, value) {
- return new DataView(MTY_MEMORY.buffer).getBigUint64(ptr, true);
- }
- function mty_set_uint64(ptr, value) {
- new DataView(MTY_MEMORY.buffer).setBigUint64(ptr, BigInt(value), true);
- }
- function mty_set_float(ptr, value) {
- new DataView(MTY_MEMORY.buffer).setFloat32(ptr, value, true);
- }
- // Base64
- function mty_buf_to_b64(buf) {
- let bstr = '';
- for (let x = 0; x < buf.byteLength; x++)
- bstr += String.fromCharCode(buf[x]);
- return btoa(bstr);
- }
- function mty_b64_to_buf(b64) {
- const bstr = atob(b64);
- const buf = new Uint8Array(bstr.length);
- for (let x = 0; x < bstr.length; x++)
- buf[x] = bstr.charCodeAt(x);
- return buf;
- }
- // Synchronization
- function mty_wait(sync) {
- if (Atomics.compareExchange(sync, 0, 0, 1) == 0)
- Atomics.wait(sync, 0, 1);
- Atomics.store(sync, 0, 0);
- }
- function mty_signal(sync, allow_miss = false) {
- if (Atomics.compareExchange(sync, 0, 0, 1) != 0)
- while (Atomics.notify(sync, 0, 1) == 0 && !allow_miss);
- }
- function MTY_SignalPtr(csync) {
- mty_signal(new Int32Array(MTY_MEMORY.buffer, csync, 1));
- }
- // Input
- function mty_scaled(num) {
- return Math.round(num * window.devicePixelRatio);
- }
- function mty_correct_relative() {
- if (!document.pointerLockElement && MTY.relative)
- MTY.canvas.requestPointerLock();
- }
- function mty_get_mods(ev) {
- let mods = 0;
- if (ev.shiftKey) mods |= 0x01;
- if (ev.ctrlKey) mods |= 0x02;
- if (ev.altKey) mods |= 0x04;
- if (ev.metaKey) mods |= 0x08;
- if (ev.getModifierState("CapsLock")) mods |= 0x10;
- if (ev.getModifierState("NumLock") ) mods |= 0x20;
- return mods;
- }
- function mty_set_pointer_lock(enable) {
- if (enable && !document.pointerLockElement) {
- MTY.canvas.requestPointerLock();
- } else if (!enable && document.pointerLockElement) {
- MTY.synthesizeEsc = false;
- document.exitPointerLock();
- }
- MTY.relative = enable;
- }
- function mty_allow_default(ev) {
- // The "allowed" browser hotkey list. Copy/Paste, Refresh, fullscreen, developer console, and tab switching
- return ((ev.ctrlKey || ev.metaKey) && ev.code == 'KeyV') ||
- ((ev.ctrlKey || ev.metaKey) && ev.code == 'KeyC') ||
- ((ev.ctrlKey || ev.shiftKey) && ev.code == 'KeyI') ||
- (ev.ctrlKey && ev.code == 'KeyR') ||
- (ev.ctrlKey && ev.code == 'F5') ||
- (ev.ctrlKey && ev.code == 'Digit1') ||
- (ev.ctrlKey && ev.code == 'Digit2') ||
- (ev.ctrlKey && ev.code == 'Digit3') ||
- (ev.ctrlKey && ev.code == 'Digit4') ||
- (ev.ctrlKey && ev.code == 'Digit5') ||
- (ev.ctrlKey && ev.code == 'Digit6') ||
- (ev.ctrlKey && ev.code == 'Digit7') ||
- (ev.ctrlKey && ev.code == 'Digit8') ||
- (ev.ctrlKey && ev.code == 'Digit9') ||
- (ev.code == 'F5') ||
- (ev.code == 'F11') ||
- (ev.code == 'F12');
- }
- function mty_add_input_events(thread) {
- MTY.canvas.addEventListener('mousemove', (ev) => {
- let x = mty_scaled(ev.clientX);
- let y = mty_scaled(ev.clientY);
- if (MTY.relative) {
- x = ev.movementX;
- y = ev.movementY;
- }
- thread.postMessage({
- type: 'motion',
- relative: MTY.relative,
- x: x,
- y: y,
- });
- });
- document.addEventListener('pointerlockchange', (ev) => {
- // Left relative via the ESC key, which swallows a natural ESC keypress
- if (!document.pointerLockElement && MTY.synthesizeEsc) {
- const msg = {
- type: 'keyboard',
- pressed: true,
- code: 'Escape',
- key: 'Escape',
- mods: 0,
- };
- thread.postMessage(msg);
- msg.pressed = false;
- thread.postMessage(msg);
- }
- MTY.synthesizeEsc = true;
- });
- window.addEventListener('click', (ev) => {
- // Popup blockers can interfere with window.open if not called from within the 'click' listener
- mty_run_action();
- ev.preventDefault();
- });
- window.addEventListener('mousedown', (ev) => {
- mty_correct_relative();
- ev.preventDefault();
- thread.postMessage({
- type: 'button',
- pressed: true,
- button: ev.button,
- x: mty_scaled(ev.clientX),
- y: mty_scaled(ev.clientY),
- });
- });
- window.addEventListener('mouseup', (ev) => {
- ev.preventDefault();
- thread.postMessage({
- type: 'button',
- pressed: false,
- button: ev.button,
- x: mty_scaled(ev.clientX),
- y: mty_scaled(ev.clientY),
- });
- });
- MTY.canvas.addEventListener('contextmenu', (ev) => {
- ev.preventDefault();
- });
- MTY.canvas.addEventListener('dragover', (ev) => {
- ev.preventDefault();
- });
- MTY.canvas.addEventListener('wheel', (ev) => {
- let x = ev.deltaX > 0 ? 120 : ev.deltaX < 0 ? -120 : 0;
- let y = ev.deltaY > 0 ? 120 : ev.deltaY < 0 ? -120 : 0;
- thread.postMessage({
- type: 'scroll',
- x: x,
- y: y,
- });
- }, {passive: true});
- window.addEventListener('keydown', (ev) => {
- mty_correct_relative();
- thread.postMessage({
- type: 'keyboard',
- pressed: true,
- code: ev.code,
- key: ev.key,
- mods: mty_get_mods(ev),
- });
- if (MTY.kb_grab || !mty_allow_default(ev))
- ev.preventDefault();
- });
- window.addEventListener('keyup', (ev) => {
- thread.postMessage({
- type: 'keyboard',
- pressed: false,
- code: ev.code,
- key: '',
- mods: mty_get_mods(ev),
- });
- if (MTY.kb_grab || !mty_allow_default(ev))
- ev.preventDefault();
- });
- window.addEventListener('blur', (ev) => {
- thread.postMessage({
- type: 'focus',
- focus: false,
- });
- });
- window.addEventListener('focus', (ev) => {
- thread.postMessage({
- type: 'focus',
- focus: true,
- });
- });
- window.addEventListener('resize', (ev) => {
- const rect = mty_update_canvas(MTY.canvas);
- thread.postMessage({
- type: 'size',
- width: mty_scaled(rect.width),
- height: mty_scaled(rect.height),
- });
- });
- MTY.canvas.addEventListener('drop', (ev) => {
- ev.preventDefault();
- if (!ev.dataTransfer.items)
- return;
- for (let x = 0; x < ev.dataTransfer.items.length; x++) {
- if (ev.dataTransfer.items[x].kind == 'file') {
- let file = ev.dataTransfer.items[x].getAsFile();
- const reader = new FileReader();
- reader.addEventListener('loadend', (fev) => {
- if (reader.readyState == 2) {
- thread.postMessage({
- type: 'drop',
- name: file.name,
- data: reader.result,
- }, [reader.result]);
- }
- });
- reader.readAsArrayBuffer(file);
- break;
- }
- }
- });
- }
- // Dialog
- function mty_alert(title, msg) {
- window.alert(mty_str_to_js(title) + '\n\n' + mty_str_to_js(msg));
- }
- // URI opener
- function mty_run_action() {
- setTimeout(() => {
- if (MTY.action) {
- MTY.action();
- delete MTY.action;
- }
- }, 100);
- }
- function mty_set_action(action) {
- MTY.action = action;
- // In case click handler doesn't happen
- mty_run_action();
- }
- // Window
- function mty_is_visible() {
- if (document.hidden != undefined) {
- return !document.hidden;
- } else if (document.webkitHidden != undefined) {
- return !document.webkitHidden;
- }
- return true;
- }
- function mty_window_info() {
- const rect = MTY.canvas.getBoundingClientRect();
- return {
- posX: window.screenX,
- posY: window.screenY,
- relative: MTY.relative,
- devicePixelRatio: window.devicePixelRatio,
- hasFocus: document.hasFocus(),
- screenWidth: screen.width,
- screenHeight: screen.height,
- fullscreen: document.fullscreenElement != null,
- visible: mty_is_visible(),
- canvasWidth: mty_scaled(rect.width),
- canvasHeight: mty_scaled(rect.height),
- };
- }
- function mty_update_canvas(canvas) {
- const rect = canvas.getBoundingClientRect();
- canvas.width = rect.width;
- canvas.height = rect.height;
- return rect;
- }
- function mty_set_fullscreen(fullscreen) {
- if (fullscreen && !document.fullscreenElement) {
- if (navigator.keyboard)
- navigator.keyboard.lock(["Escape"]);
- document.documentElement.requestFullscreen();
- } else if (!fullscreen && document.fullscreenElement) {
- document.exitFullscreen();
- if (navigator.keyboard)
- navigator.keyboard.unlock();
- }
- }
- async function mty_wake_lock(enable) {
- try {
- if (enable && !MTY.wakeLock) {
- MTY.wakeLock = await navigator.wakeLock.request('screen');
- } else if (!enable && MTY.wakeLock) {
- MTY.wakeLock.release();
- delete MTY.wakeLock;
- }
- } catch (e) {
- delete MTY.wakeLock;
- }
- }
- // Cursor
- function mty_show_cursor(show) {
- MTY.canvas.style.cursor = show ? '': 'none';
- }
- function mty_use_default_cursor(use_default) {
- if (MTY.cursorClass.length > 0) {
- if (use_default) {
- MTY.canvas.classList.remove(MTY.cursorClass);
- } else {
- MTY.canvas.classList.add(MTY.cursorClass);
- }
- }
- MTY.defaultCursor = use_default;
- }
- function mty_set_cursor(url, hot_x, hot_y) {
- if (url) {
- if (!MTY.cursorCache[url]) {
- MTY.cursorCache[url] = `cursor-x-${MTY.cursorId}`;
- const style = document.createElement('style');
- style.type = 'text/css';
- style.innerHTML = `.cursor-x-${MTY.cursorId++} ` +
- `{cursor: url(${url}) ${hot_x} ${hot_y}, auto;}`;
- document.querySelector('head').appendChild(style);
- }
- if (MTY.cursorClass.length > 0)
- MTY.canvas.classList.remove(MTY.cursorClass);
- MTY.cursorClass = MTY.cursorCache[url];
- if (!MTY.defaultCursor)
- MTY.canvas.classList.add(MTY.cursorClass);
- } else {
- if (!MTY.defaultCursor && MTY.cursorClass.length > 0)
- MTY.canvas.classList.remove(MTY.cursorClass);
- MTY.cursorClass = '';
- }
- }
- function mty_set_png_cursor(buf, hot_x, hot_y) {
- const url = buf ? 'data:image/png;base64,' + mty_buf_to_b64(buf) : null;
- mty_set_cursor(url, hot_x, hot_y);
- }
- function mty_set_rgba_cursor(buf, width, height, hot_x, hot_y) {
- let url = null;
- if (buf) {
- if (!MTY.ccanvas) {
- MTY.ccanvas = document.createElement('canvas');
- MTY.cctx = MTY.ccanvas.getContext('2d');
- }
- MTY.ccanvas.width = width;
- MTY.ccanvas.height = height;
- const image = MTY.cctx.getImageData(0, 0, width, height);
- image.data.set(buf);
- MTY.cctx.putImageData(image, 0, 0);
- url = MTY.ccanvas.toDataURL();
- }
- mty_set_cursor(url, hot_x, hot_y);
- }
- // Gamepads
- function mty_rumble_gamepad(id, low, high) {
- const gps = navigator.getGamepads();
- const gp = gps[id];
- if (gp && gp.vibrationActuator)
- gp.vibrationActuator.playEffect('dual-rumble', {
- startDelay: 0,
- duration: 2000,
- weakMagnitude: low,
- strongMagnitude: high,
- });
- }
- function mty_poll_gamepads() {
- const gps = navigator.getGamepads();
- for (let x = 0; x < 4; x++) {
- const gp = gps[x];
- if (gp) {
- let state = 0;
- // Connected
- if (!MTY.gps[x]) {
- MTY.gps[x] = true;
- state = 1;
- }
- let lx = 0;
- let ly = 0;
- let rx = 0;
- let ry = 0;
- let lt = 0;
- let rt = 0;
- let buttons = 0;
- if (gp.buttons) {
- if (gp.buttons[6]) lt = gp.buttons[6].value;
- if (gp.buttons[7]) rt = gp.buttons[7].value;
- for (let i = 0; i < gp.buttons.length && i < 32; i++)
- if (gp.buttons[i].pressed)
- buttons |= 1 << i;
- }
- if (gp.axes) {
- if (gp.axes[0]) lx = gp.axes[0];
- if (gp.axes[1]) ly = gp.axes[1];
- if (gp.axes[2]) rx = gp.axes[2];
- if (gp.axes[3]) ry = gp.axes[3];
- }
- thread.postMessage({
- type: 'controller',
- id: x,
- state: state,
- buttons: buttons,
- lx: lx,
- ly: ly,
- rx: rx,
- ry: ry,
- lt: lt,
- rt: rt,
- });
- // Disconnected
- } else if (MTY.gps[x]) {
- thread.postMessage({
- type: 'controller-disconnect',
- id: x,
- state: 2,
- });
- MTY.gps[x] = false;
- }
- }
- }
- // Audio
- async function mty_audio_queue(ctx, sampleRate, minBuffer, maxBuffer, channels) {
- // Initialize on first queue otherwise the browser may complain about user interaction
- if (!MTY.audioCtx) {
- MTY.audioCtx = new AudioContext({sampleRate: sampleRate});
- const baseFile = MTY_CURRENT_SCRIPT.pathname;
- await MTY.audioCtx.audioWorklet.addModule(baseFile.replace('.js', '-worker.js'));
- const node = new AudioWorkletNode(MTY.audioCtx, 'MTY_Audio', {
- outputChannelCount: [channels],
- processorOptions: {
- minBuffer,
- maxBuffer,
- },
- });
- node.connect(MTY.audioCtx.destination);
- node.port.postMessage(MTY.audioObjs);
- }
- }
- // Image
- async function mty_decode_image(input) {
- const img = new Image();
- img.src = URL.createObjectURL(new Blob([input]));
- await img.decode();
- const width = img.naturalWidth;
- const height = img.naturalHeight;
- const canvas = new OffscreenCanvas(width, height);
- const ctx = canvas.getContext('2d');
- ctx.drawImage(img, 0, 0, width, height);
- return ctx.getImageData(0, 0, width, height);
- }
- // Net
- function mty_ws_new(obj) {
- MTY.wsObj[MTY.wsIndex] = obj;
- return MTY.wsIndex++;
- }
- function mty_ws_del(index) {
- let obj = MTY.wsObj[index];
- delete MTY.wsObj[index];
- return obj;
- }
- function mty_ws_obj(index) {
- return MTY.wsObj[index];
- }
- async function mty_http_request(url, method, headers, body, buf) {
- let error = false
- let size = 0;
- let status = 0;
- let data = null;
- try {
- const response = await fetch(url, {
- method: method,
- headers: headers,
- body: body,
- });
- const res_ab = await response.arrayBuffer();
- data = new Uint8Array(res_ab);
- status = response.status;
- size = data.byteLength;
- } catch (err) {
- console.error(err);
- error = true;
- }
- return {
- data,
- error,
- size,
- status,
- };
- }
- async function mty_ws_connect(url) {
- return new Promise((resolve, reject) => {
- const ws = new WebSocket(url);
- const sab = new SharedArrayBuffer(4);
- ws.sync = new Int32Array(sab, 0, 1);
- ws.closeCode = 0;
- ws.msgs = [];
- ws.onclose = (ev) => {
- ws.closeCode = ev.code == 1005 ? 1000 : ev.code;
- resolve(null);
- };
- ws.onerror = (err) => {
- console.error(err);
- resolve(null);
- };
- ws.onopen = () => {
- resolve(ws);
- };
- ws.onmessage = (ev) => {
- ws.msgs.push(ev.data);
- Atomics.notify(ws.sync, 0, 1);
- };
- });
- }
- async function mty_ws_read(ws, timeout) {
- let msg = ws.msgs.shift()
- if (!msg) {
- const r0 = Atomics.waitAsync(ws.sync, 0, 0, timeout);
- const r1 = await r0.value;
- if (r1 != 'timed-out')
- msg = ws.msgs.shift()
- }
- return msg ? mty_encode(msg) : null;
- }
- // Entry
- function mty_supports_web_gl() {
- try {
- return document.createElement('canvas').getContext('webgl2');
- } catch (e) {}
- return false;
- }
- function mty_update_interval(thread) {
- // Poll gamepads
- if (document.hasFocus())
- mty_poll_gamepads();
- // Poll position changes
- if (MTY.posX != window.screenX || MTY.posY != window.screenY) {
- MTY.posX = window.screenX;
- MTY.posY = window.screenY;
- thread.postMessage({
- type: 'move',
- });
- }
- // send rect event
- thread.postMessage({
- type: 'window-update',
- windowInfo: mty_window_info(),
- });
- }
- function mty_thread_start(threadId, bin, wasmBuf, memory, startArg, userEnv, kbMap, psync, audioObjs, name) {
- const baseFile = MTY_CURRENT_SCRIPT.pathname;
- const worker = new Worker(baseFile.replace('.js', '-worker.js'), {name: name});
- worker.onmessage = mty_thread_message;
- worker.postMessage({
- type: 'init',
- file: baseFile,
- bin: bin,
- wasmBuf: wasmBuf,
- psync: psync,
- windowInfo: mty_window_info(),
- args: window.location.search,
- hostname: window.location.hostname,
- userEnv: userEnv ? Object.keys(userEnv) : [],
- kbMap: kbMap,
- startArg: startArg,
- threadId: threadId,
- memory: memory,
- audioObjs,
- });
- return worker;
- }
- async function MTY_Start(bin, container, userEnv) {
- if (!mty_supports_web_gl())
- return false;
- MTY.bin = bin;
- MTY.userEnv = userEnv;
- MTY.psync = new Int32Array(new SharedArrayBuffer(4));
- MTY.audioObjs = {
- buf: new Int16Array(new SharedArrayBuffer(1024 * 1024)),
- control: new Int32Array(new SharedArrayBuffer(32)),
- };
- // Drawing surface
- MTY.canvas = document.createElement('canvas');
- MTY.renderer = MTY.canvas.getContext('bitmaprenderer');
- MTY.canvas.style.width = '100%';
- MTY.canvas.style.height = '100%';
- container.appendChild(MTY.canvas);
- mty_update_canvas(MTY.canvas);
- // WASM binary
- const wasmRes = await fetch(bin);
- MTY.wasmBuf = await wasmRes.arrayBuffer();
- // Shared global memory
- MTY_MEMORY = new WebAssembly.Memory({
- initial: 512, // 32 MB
- maximum: 16384, // 1 GB
- shared: true,
- });
- // Load keyboard map
- MTY.kbMap = {};
- if (navigator.keyboard) {
- const layout = await navigator.keyboard.getLayoutMap();
- layout.forEach((currentValue, index) => {
- MTY.kbMap[index] = currentValue;
- });
- }
- // Main thread
- MTY.mainThread = mty_thread_start(MTY.threadId, bin, MTY.wasmBuf, MTY_MEMORY,
- 0, userEnv, MTY.kbMap, MTY.psync, MTY.audioObjs, 'main');
- // Init position, update loop
- MTY.posX = window.screenX;
- MTY.posY = window.screenY;
- setInterval(() => {
- mty_update_interval(MTY.mainThread);
- }, 10);
- // Vsync
- const vsync = () => {
- mty_signal(MTY.psync, true);
- requestAnimationFrame(vsync);
- };
- requestAnimationFrame(vsync);
- // Add input events
- mty_add_input_events(MTY.mainThread);
- return true;
- }
- async function mty_thread_message(ev) {
- const msg = ev.data;
- switch (msg.type) {
- case 'user-env':
- msg.sab[0] = MTY.userEnv[msg.name](...msg.args);
- mty_signal(msg.sync);
- break;
- case 'thread': {
- MTY.threadId++;
- const worker = mty_thread_start(MTY.threadId, MTY.bin, MTY.wasmBuf, MTY_MEMORY,
- msg.startArg, MTY.userEnv, MTY.kbMap, MTY.psync, MTY.audioObjs, 'thread-' + MTY.threadId);
- msg.sab[0] = MTY.threadId;
- mty_signal(msg.sync);
- break;
- }
- case 'present':
- MTY.renderer.transferFromImageBitmap(msg.image);
- break;
- case 'decode-image': {
- const image = await mty_decode_image(msg.input);
- this.tmp = image.data;
- msg.sab[0] = image.width;
- msg.sab[1] = image.height;
- mty_signal(msg.sync);
- break;
- }
- case 'kb-grab':
- MTY.kb_grab = msg.grab;
- break;
- case 'title':
- document.title = msg.title;
- break;
- case 'get-ls': {
- const val = window.localStorage[msg.key];
- if (val) {
- this.tmp = mty_b64_to_buf(val);
- msg.sab[0] = this.tmp.byteLength;
- } else {
- msg.sab[0] = 0;
- }
- mty_signal(msg.sync);
- break;
- }
- case 'set-ls':
- window.localStorage[msg.key] = mty_buf_to_b64(msg.val);
- mty_signal(msg.sync);
- break;
- case 'alert':
- mty_alert(msg.title, msg.msg);
- break;
- case 'fullscreen':
- mty_set_fullscreen(msg.fullscreen);
- break;
- case 'wake-lock':
- mty_wake_lock(msg.enable);
- break;
- case 'rumble':
- mty_rumble_gamepad(msg.id, msg.low, msg.high);
- break;
- case 'show-cursor':
- mty_show_cursor(msg.show);
- break;
- case 'get-clip':
- // FIXME Unsupported on Firefox
- if (navigator.clipboard.readText) {
- const text = await navigator.clipboard.readText();
- this.tmp = mty_encode(text);
- msg.sab[0] = this.tmp.byteLength;
- } else {
- msg.sab[0] = 0;
- }
- mty_signal(msg.sync);
- break;
- case 'set-clip':
- navigator.clipboard.writeText(mty_str_to_js(msg.text));
- break;
- case 'pointer-lock':
- mty_set_pointer_lock(msg.enable);
- break;
- case 'cursor-default':
- mty_use_default_cursor(msg.use_default);
- break;
- case 'cursor-rgba':
- mty_set_rgba_cursor(msg.buf, msg.width, msg.height, msg.hot_x, msg.hot_y);
- break;
- case 'cursor-png':
- mty_set_png_cursor(msg.buf, msg.hot_x, msg.hot_y);
- break;
- case 'uri':
- mty_set_action(() => {
- window.open(mty_str_to_js(msg.uri), '_blank');
- });
- break;
- case 'http': {
- const res = await mty_http_request(msg.url, msg.method, msg.headers, msg.body);
- this.tmp = res.data;
- msg.sab[0] = res.error ? 1 : 0;
- msg.sab[1] = res.size;
- msg.sab[2] = res.status;
- mty_signal(msg.sync);
- break;
- }
- case 'ws-connect': {
- const ws = await mty_ws_connect(msg.url);
- msg.sab[0] = ws ? mty_ws_new(ws) : 0;
- mty_signal(msg.sync);
- break;
- }
- case 'ws-read': {
- msg.sab[0] = 3; // MTY_ASYNC_ERROR
- const ws = mty_ws_obj(msg.ctx);
- if (ws) {
- if (ws.closeCode != 0) {
- msg.sab[0] = 1; // MTY_ASYNC_DONE
- } else {
- const buf = await mty_ws_read(ws, msg.timeout);
- if (buf) {
- this.tmp = buf;
- msg.sab[0] = 0; // MTY_ASYNC_OK
- msg.sab[1] = buf.length;
- } else {
- msg.sab[0] = 2; // MTY_ASYNC_CONTINUE
- }
- }
- }
- mty_signal(msg.sync);
- break;
- }
- case 'ws-write': {
- const ws = mty_ws_obj(msg.ctx);
- if (ws)
- ws.send(msg.text)
- break;
- }
- case 'ws-close': {
- const ws = mty_ws_obj(msg.ctx);
- if (ws) {
- ws.close();
- mty_ws_del(msg.ctx);
- }
- break;
- }
- case 'ws-code': {
- msg.sab[0] = 0;
- const ws = mty_ws_obj(msg.ctx);
- if (ws)
- msg.sab[0] = ws.closeCode;
- mty_signal(msg.sync);
- break;
- }
- case 'audio-queue':
- mty_audio_queue(MTY.audio, msg.sampleRate, msg.minBuffer,
- msg.maxBuffer, msg.channels);
- break;
- case 'audio-destroy':
- if (MTY.audioCtx)
- MTY.audioCtx.close();
- delete MTY.audioCtx;
- break;
- case 'async-copy':
- msg.sab8.set(this.tmp);
- delete this.tmp;
- mty_signal(msg.sync);
- break;
- }
- }
|